commit 3f5dafcdd1d64300a017db114b8086ecd46ae2ac Author: Vomitblood Date: Sun Jan 19 15:01:49 2025 +0800 initial commit diff --git a/Cartomancer-v.4.10-fix-fix/LICENSE b/Cartomancer-v.4.10-fix-fix/LICENSE new file mode 100644 index 0000000..e72bfdd --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/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 +. \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/README.md b/Cartomancer-v.4.10-fix-fix/README.md new file mode 100644 index 0000000..d1c5fe5 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/README.md @@ -0,0 +1,46 @@ +## Requirements +- [Lovely](https://github.com/ethangreen-dev/lovely-injector) - a Balatro injector. + +## Installation +1. Install [Lovely](https://github.com/ethangreen-dev/lovely-injector?tab=readme-ov-file#manual-installation). +2. Download the [latest release](https://github.com/stupxd/Cartomancer/releases/) of this mod. +3. Unzip the folder, and move it into the `%appdata%/Balatro/Mods` folder. +4. Restart the game to load the mod. + +## Features +1. Limit amount of cards visible in your deck pile, to make it appear smaller. Default limit is 100 cards, which can be modified in mod config menu. + +![cards-pile-difference](git-assets/deck-pile.jpg) + + +2. Improved deck view + +- Stack identical playing cards, which looks much cleaner and improves performance. + +![stackable-cards-difference](git-assets/stackable-cards.jpg) + +- Optionally, stack cards regardless of modifier, if your deck has tons of unique cards. +- Hide drawn cards from deck view + +4. Custom scoring flames intensity and SFX volume. + +5. Hide non-essential (edition) shaders. + +6. Improved jokers management + +- Option to hide all jokers (improves performance at 100+ jokers). + +- Zoom into the jokers area for easier jokers management and navigation. + +![zoom-jokers-difference](git-assets/zoom-jokers.jpg) + +Settings for this mod can be found under `Mods` tab, if you use Steamodded 1.0.0 - find `Cartomancer`, and open `Config` tab. + +If you play vanilla, go to `Settings` and open ![cartomancer](assets/1x/modicon.png) tab. + +## Credits + +[Jen Walter](https://github.com/jenwalter666/) for the code for UI box on stacked cards. + +[Mysthaps](https://github.com/Mysthaps/) for most of the initial mod config code. + diff --git a/Cartomancer-v.4.10-fix-fix/assets/1x/modicon.png b/Cartomancer-v.4.10-fix-fix/assets/1x/modicon.png new file mode 100644 index 0000000..0d7f610 Binary files /dev/null and b/Cartomancer-v.4.10-fix-fix/assets/1x/modicon.png differ diff --git a/Cartomancer-v.4.10-fix-fix/assets/1x/settings.png b/Cartomancer-v.4.10-fix-fix/assets/1x/settings.png new file mode 100644 index 0000000..e81fdd7 Binary files /dev/null and b/Cartomancer-v.4.10-fix-fix/assets/1x/settings.png differ diff --git a/Cartomancer-v.4.10-fix-fix/assets/2x/modicon.png b/Cartomancer-v.4.10-fix-fix/assets/2x/modicon.png new file mode 100644 index 0000000..7d42c97 Binary files /dev/null and b/Cartomancer-v.4.10-fix-fix/assets/2x/modicon.png differ diff --git a/Cartomancer-v.4.10-fix-fix/assets/2x/settings.png b/Cartomancer-v.4.10-fix-fix/assets/2x/settings.png new file mode 100644 index 0000000..bd610ca Binary files /dev/null and b/Cartomancer-v.4.10-fix-fix/assets/2x/settings.png differ diff --git a/Cartomancer-v.4.10-fix-fix/cartomancer.lua b/Cartomancer-v.4.10-fix-fix/cartomancer.lua new file mode 100644 index 0000000..a982536 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/cartomancer.lua @@ -0,0 +1,75 @@ +require 'cartomancer.init' + +Cartomancer.path = assert( + Cartomancer.find_self('cartomancer.lua'), + "Failed to find mod folder. Make sure that `Cartomancer` folder has `cartomancer.lua` file!" +) + +Cartomancer.load_mod_file('internal/config.lua', 'internal.config') +Cartomancer.load_mod_file('internal/atlas.lua', 'internal.atlas') +Cartomancer.load_mod_file('internal/ui.lua', 'internal.ui') +Cartomancer.load_mod_file('internal/keybinds.lua', 'internal.keybinds') + +Cartomancer.load_mod_file('core/view-deck.lua', 'core.view-deck') +Cartomancer.load_mod_file('core/flames.lua', 'core.flames') +Cartomancer.load_mod_file('core/optimizations.lua', 'core.optimizations') +Cartomancer.load_mod_file('core/jokers.lua', 'core.jokers') +Cartomancer.load_mod_file('core/hand.lua', 'core.hand') + +Cartomancer.load_config() + +Cartomancer.INTERNAL_jokers_menu = false + +-- TODO dedicated keybinds file? keybinds need to load after config +Cartomancer.register_keybind { + name = 'hide_joker', + func = function (controller) + Cartomancer.hide_hovered_joker(controller) + end +} + +Cartomancer.register_keybind { + name = 'toggle_tags', + func = function (controller) + Cartomancer.SETTINGS.hide_tags = not Cartomancer.SETTINGS.hide_tags + Cartomancer.update_tags_visibility() + end +} + +Cartomancer.register_keybind { + name = 'toggle_consumables', + func = function (controller) + Cartomancer.SETTINGS.hide_consumables = not Cartomancer.SETTINGS.hide_consumables + end +} + +Cartomancer.register_keybind { + name = 'toggle_deck', + func = function (controller) + Cartomancer.SETTINGS.hide_deck = not Cartomancer.SETTINGS.hide_deck + end +} + +Cartomancer.register_keybind { + name = 'toggle_jokers', + func = function (controller) + if not (G and G.jokers) then + return + end + G.jokers.cart_hide_all = not G.jokers.cart_hide_all + + if G.jokers.cart_hide_all then + Cartomancer.hide_all_jokers() + else + Cartomancer.show_all_jokers() + end + Cartomancer.align_G_jokers() + end +} + +Cartomancer.register_keybind { + name = 'toggle_jokers_buttons', + func = function (controller) + Cartomancer.SETTINGS.jokers_controls_buttons = not Cartomancer.SETTINGS.jokers_controls_buttons + end +} diff --git a/Cartomancer-v.4.10-fix-fix/config.lua b/Cartomancer-v.4.10-fix-fix/config.lua new file mode 100644 index 0000000..09171ef --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/config.lua @@ -0,0 +1,72 @@ + +return { + compact_deck_enabled = true, + compact_deck_visible_cards = 100, + + deck_view_stack_enabled = true, + deck_view_stack_modifiers = false, + deck_view_stack_chips = true, + -- [top center bottom] + deck_view_stack_pos_vertical = 't', + -- [left middle right] + deck_view_stack_pos_horizontal = 'm', + -- Hex color code for x (before amount) + deck_view_stack_x_color = 'ed7575', + -- Opacity in % + deck_view_stack_background_opacity = '60', + + deck_view_hide_drawn_cards = false, + -- todo: maybe custom shader for drawn cards to adjust opacity + + improved_hand_sorting = false, + draw_non_essential_shaders = true, + hide_tags = false, + hide_consumables = false, + hide_deck = false, + hide_jokers = false, + + flames_intensity_min = 0.5, + flames_intensity_max = 10, + flames_relative_intensity = false, + flames_slower_speed = false, + flames_volume = 100, + + jokers_controls_buttons = true, + jokers_controls_show_after = 13, + + keybinds = { + hide_joker = { + lalt = true, + h = true, + }, + toggle_tags = {['[none]'] = true}, + toggle_consumables = {['[none]'] = true}, + toggle_deck = {['[none]'] = true}, + toggle_jokers = {['[none]'] = true}, + toggle_jokers_buttons = {['[none]'] = true}, + }, +} + +--[[ +--------- WIP / concepting + + -- Stack cards in hand? + hand_stack_enabled = true, + hand_stack_after_total = 100, + + -- stacked jokers? + hide_jokers = { + enabled = true, + rarities = { + common = true, + uncommon = true, + rare = false, + }, + all = true, + editions = { + negative = true, + }, + -- When total reaches this number, matching jokers will be hidden TODO : make sure to show jokers once the number's back down :) + hide_after_total = 100, + }, +]] diff --git a/Cartomancer-v.4.10-fix-fix/core/flames.lua b/Cartomancer-v.4.10-fix-fix/core/flames.lua new file mode 100644 index 0000000..773203e --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/core/flames.lua @@ -0,0 +1,40 @@ + + +function Cartomancer.get_flames_intensity() + local value + if Cartomancer.SETTINGS.flames_relative_intensity then + -- Scale intensity relative to the required score + value = math.max(0., math.log(G.ARGS.score_intensity.earned_score/G.ARGS.score_intensity.required_score + 5, 5)) + else + value = math.max(0., math.log(G.ARGS.score_intensity.earned_score, 5) - 2) + end + + if Cartomancer.SETTINGS.flames_intensity_max >= Cartomancer._INTERNAL_max_flames_intensity then + return value + end + + return math.max( + math.min(value, Cartomancer.SETTINGS.flames_intensity_max), + Cartomancer.SETTINGS.flames_intensity_min + ) +end + +function Cartomancer.handle_flames_volume(value) + return Cartomancer.SETTINGS.flames_volume/100. * value +end + +local function intensity_for_big_scores(real_intensity) + local power = 0.55 + + real_intensity = math.max(0, real_intensity) + + return math.max(0, math.min(6, real_intensity) + math.max(1, math.log(real_intensity)) ^ power) - 1. +end + +function Cartomancer.handle_flames_timer(timer, intensity) + if not Cartomancer.SETTINGS.flames_slower_speed then + return timer + G.real_dt*(1 + intensity*0.2) + end + + return timer + G.real_dt*(1 + intensity_for_big_scores(intensity)*0.7) +end diff --git a/Cartomancer-v.4.10-fix-fix/core/hand.lua b/Cartomancer-v.4.10-fix-fix/core/hand.lua new file mode 100644 index 0000000..eb80468 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/core/hand.lua @@ -0,0 +1,70 @@ +-- Hand sorting + +G.FUNCS.cartomancer_sort_hand_off = function(e) + if G.hand.cart_sorting == false then + G.hand.cart_old_sorting = G.hand.config.sort + G.hand:sort('off') + else + G.hand.config.sort = G.hand.cart_old_sorting + G.hand:sort() + end +end + +local function is_desc(method) + return string.find(method, "desc") +end + +local function is_suit(method) + return string.find(method, "suit") +end + +local function is_sorted() + -- TODO : check if hand is already sorted with current method + return true +end + +local g_func_sort_hand_suit = G.FUNCS.sort_hand_suit +G.FUNCS.sort_hand_suit = function(e) + G.hand.cart_sorting = true + if not Cartomancer.SETTINGS.improved_hand_sorting then + return g_func_sort_hand_suit(e) + end + + local current = G.hand.config.sort + local new = 'suit desc' + + -- If already sorted by suit, toggle ascending/descending order + if is_suit(current) and is_sorted() then + if is_desc(current) then + new = 'suit asc' + else + new = 'suit desc' + end + end + + G.hand:sort(new) + play_sound('paper1') +end + +local g_func_sort_hand_value = G.FUNCS.sort_hand_value +G.FUNCS.sort_hand_value = function(e) + G.hand.cart_sorting = true + if not Cartomancer.SETTINGS.improved_hand_sorting then + return g_func_sort_hand_value(e) + end + + local current = G.hand.config.sort + local new = 'desc' + + -- If already sorted by value, toggle ascending/descending order + if not is_suit(current) and is_sorted() then + if is_desc(current) then + new = 'asc' + else + new = 'desc' + end + end + + G.hand:sort(new) + play_sound('paper1') +end diff --git a/Cartomancer-v.4.10-fix-fix/core/jokers.lua b/Cartomancer-v.4.10-fix-fix/core/jokers.lua new file mode 100644 index 0000000..4935c5e --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/core/jokers.lua @@ -0,0 +1,253 @@ +-- EXPAND JOKERS CARDAREA + + +function Cartomancer.align_G_jokers() + if not G or not G.jokers then + return + end + -- Refresh controls + if G.jokers.children.cartomancer_controls then + G.jokers.children.cartomancer_controls:remove() + G.jokers.children.cartomancer_controls = nil + end + G.jokers:align_cards() + G.jokers:hard_set_cards() +end + +local old_slider_value = 0 +local slide_speedup = nil + +function Cartomancer.expand_G_jokers() + G.jokers.cart_zoom_slider = G.jokers.cart_zoom_slider or 0 + + local self_T_w = math.max(4.9*G.CARD_W, 0.6*#G.jokers.cards * G.CARD_W) + local self_T_x = G.jokers.T.x - (self_T_w- 4.9*G.CARD_W) * G.jokers.cart_zoom_slider / 100 + + local self = G.jokers + + for k, card in ipairs(self.cards) do + if card.states.drag.is then + local sign = nil + if card.T.x < -1 then + sign = -1 + elseif card.T.x > G.TILE_W then + sign = 1 + end + + if sign then + slide_speedup = (slide_speedup or 1) + 0.01 + local shift = math.min( + self_T_w / 3, + 4 * (slide_speedup ^ 1.7) + ) + G.jokers.cart_zoom_slider = math.max(0, math.min(100, G.jokers.cart_zoom_slider + sign * shift / self_T_w)) + + local slider = G.jokers.children.cartomancer_controls:get_UIE_by_ID('joker_slider') + -- Only relevant part, copied from G.FUNCS.slider + local rt = slider.config.ref_table + slider.T.w = (G.jokers.cart_zoom_slider - rt.min)/(rt.max - rt.min)*rt.w + slider.config.w = slider.T.w + else + slide_speedup = nil + end + else + card.T.r = 0.1*(-#self.cards/2 - 0.5 + k)/(#self.cards)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+card.T.x) + local max_cards = math.max(#self.cards, self.config.temp_limit) + card.T.x = self_T_x + (self_T_w-self.card_w)*((k-1)/math.max(max_cards-1, 1) - 0.5*(#self.cards-max_cards)/math.max(max_cards-1, 1)) + 0.5*(self.card_w - card.T.w) + if #self.cards > 2 or (#self.cards > 1 and self == G.consumeables) or (#self.cards > 1 and self.config.spread) then + card.T.x = self_T_x + (self_T_w-self.card_w)*((k-1)/(#self.cards-1)) + 0.5*(self.card_w - card.T.w) + elseif #self.cards > 1 and self ~= G.consumeables then + card.T.x = self_T_x + (self_T_w-self.card_w)*((k - 0.5)/(#self.cards)) + 0.5*(self.card_w - card.T.w) + else + card.T.x = self_T_x + self_T_w/2 - self.card_w/2 + 0.5*(self.card_w - card.T.w) + end + local highlight_height = G.HIGHLIGHT_H/2 + if not card.highlighted then highlight_height = 0 end + card.T.y = self.T.y + self.T.h/2 - card.T.h/2 - highlight_height+ (G.SETTINGS.reduced_motion and 0 or 1)*0.03*math.sin(0.666*G.TIMERS.REAL+card.T.x) + card.T.x = card.T.x + card.shadow_parrallax.x/30 + end + end + if not (old_slider_value == G.jokers.cart_zoom_slider) then + old_slider_value = G.jokers.cart_zoom_slider + return true + end +end + + + + +--*-------------------------- +--*------HIDE JOKERS +--*-------------------------- +-- TODO : popup below joker to hide it + +local JOKER_RARITY = { + 'common', + 'uncommon', + 'rare', + 'legendary', +} + +function Cartomancer.add_visibility_controls() + if not G.jokers then + return + end + + + if not (Cartomancer.SETTINGS.jokers_controls_buttons and #G.jokers.cards >= Cartomancer.SETTINGS.jokers_controls_show_after) then + G.jokers.cart_jokers_expanded = false + if G.jokers.children.cartomancer_controls then + Cartomancer.align_G_jokers() + end + return + end + + if not G.jokers.children.cartomancer_controls then + local settings = Sprite(0,0,0.425,0.425,G.ASSET_ATLAS["cart_settings"], {x=0, y=0}) + settings.states.drag.can = false + + local joker_slider = nil + if G.jokers.cart_jokers_expanded then + joker_slider = create_slider({id = 'joker_slider', w = 6, h = 0.4, + ref_table = G.jokers, ref_value = 'cart_zoom_slider', min = 0, max = 100, + decimal_places = 1, + hide_val = true, + colour = G.C.CHIPS, + }) + joker_slider.config.padding = 0 + end + + G.jokers.children.cartomancer_controls = UIBox { + definition = { + n = G.UIT.ROOT, + config = { align = 'cm', padding = 0.07, colour = G.C.CLEAR, }, + nodes = { + {n=G.UIT.R, config={align = 'tm', padding = 0.07, no_fill = true}, nodes={ + + G.jokers.cart_hide_all and + {n=G.UIT.C, config={align = "cm"}, nodes={ + UIBox_button({id = 'show_all_jokers', button = 'cartomancer_show_all_jokers', label = {localize('carto_jokers_show')}, + minh = 0.45, minw = 1, col = false, scale = 0.3, + colour = G.C.CHIPS, --func = function ()return Cartomancer.SETTINGS.jokers_visibility_controls end + }) + }} + or + {n=G.UIT.C, config={align = "cm", }, nodes={ + UIBox_button({id = 'hide_all_jokers', button = 'cartomancer_hide_all_jokers', label ={localize('carto_jokers_hide')}, + minh = 0.45, minw = 1, col = false, scale = 0.3,-- func = function ()return Cartomancer.SETTINGS.jokers_visibility_controls end + }) + }}, + + {n=G.UIT.C, config={align = "cm"}, nodes={ + UIBox_button({id = 'zoom_jokers', button = 'cartomancer_zoom_jokers', label = {localize('carto_jokers_zoom')}, + minh = 0.45, minw = 1, col = false, scale = 0.3, + }) + }}, + joker_slider, + + Cartomancer.INTERNAL_jokers_menu and {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.01, r = 0.1, hover = true, colour = G.C.BLUE, button = 'cartomancer_joker_visibility_settings', shadow = true}, nodes={ + {n=G.UIT.O, config={object = settings}}, + }}, + }} or nil, + }} + } + }, + config = { + align = 't', + bond = 'Strong', + parent = G.jokers + }, + } + end + + -- This makes sure UIBox is drawn every frame + G.jokers.children.cartomancer_controls:draw() +end + +G.FUNCS.cartomancer_hide_all_jokers = function(e) + Cartomancer.hide_all_jokers() + G.jokers.cart_hide_all = true + Cartomancer.align_G_jokers() +end + +G.FUNCS.cartomancer_show_all_jokers = function(e) + Cartomancer.show_all_jokers() + G.jokers.cart_hide_all = false + Cartomancer.align_G_jokers() +end + +G.FUNCS.cartomancer_zoom_jokers = function(e) + G.jokers.cart_jokers_expanded = not G.jokers.cart_jokers_expanded + Cartomancer.align_G_jokers() +end + + +G.FUNCS.cartomancer_joker_visibility_settings = function(e) + + G.CARTO_JOKER_VISIBILITY = UIBox{ + definition = Cartomancer.jokers_visibility_standalone_menu(), + config = {align="cm", offset = {x=0,y=10},major = G.ROOM_ATTACH, bond = 'Weak', instance_type = "POPUP"} + } + G.CARTO_JOKER_VISIBILITY.alignment.offset.y = 0 + G.ROOM.jiggle = G.ROOM.jiggle + 1 + G.CARTO_JOKER_VISIBILITY:align_to_major() + -- TODO : REMOVE WHEN APPLY/CANCEL IS PRESSED +end + +local function hide_card(card) + card.states.visible = false +end + +function Cartomancer.handle_joker_added(card) + + + if G.jokers.cart_hide_all then + hide_card(card) + end +end + +function Cartomancer.hide_hovered_joker(controller) + if not G.jokers then + return + end + + local selected = controller.focused.target or controller.hovering.target + + if not selected or not selected:is(Card) then + return + end + + if selected.area ~= G.jokers then + return + end + + hide_card(selected) +end + +function Cartomancer.hide_all_jokers() + if not G.jokers then + print("no jokers") + return + end + + local total_jokers = #G.jokers.cards + + for i = 1, total_jokers do + hide_card(G.jokers.cards[i]) + end +end + +function Cartomancer.show_all_jokers() + if not G.jokers then + Cartomancer.log("no jokers") + return + end + + local total_jokers = #G.jokers.cards + + for i = 1, total_jokers do + G.jokers.cards[i].states.visible = true + end + +end \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/core/optimizations.lua b/Cartomancer-v.4.10-fix-fix/core/optimizations.lua new file mode 100644 index 0000000..29768c2 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/core/optimizations.lua @@ -0,0 +1,54 @@ + +-- ============================ +-- Hide non-essential shaders +-- ============================ +local essential_shaders = { + background = true, + CRT = true, + flame = true, + flash = true, + dissolve = true, + vortex = true, + voucher = true, + booster = true, + hologram = true, + debuff = true, + played = true, + skew = true, + splash = true, +} + +local sprite_draw_shader = Sprite.draw_shader +function Sprite:draw_shader(_shader, _shadow_height, _send, _no_tilt, other_obj, ms, mr, mx, my, custom_shader, tilt_shadow) + if not Cartomancer.SETTINGS.draw_non_essential_shaders and _shader == 'negative' then + _shader = 'dissolve' + _send = nil + end + + if Cartomancer.SETTINGS.draw_non_essential_shaders or essential_shaders[_shader] then + return sprite_draw_shader(self, _shader, _shadow_height, _send, _no_tilt, other_obj, ms, mr, mx, my, custom_shader, tilt_shadow) + end +end + +-- ============================ +-- Hide card areas +-- ============================ +local cardarea_draw = CardArea.draw +function CardArea:draw() + if Cartomancer.SETTINGS.hide_consumables and self == G.consumeables then + return + elseif Cartomancer.SETTINGS.hide_deck and self == G.deck then + return + end + + return cardarea_draw(self) +end + +-- ============================ +-- Hide tags +-- ============================ +function Cartomancer.update_tags_visibility() + for _, tag in pairs(G.GAME.tags) do + tag.HUD_tag.states.visible = not Cartomancer.SETTINGS.hide_tags + end +end diff --git a/Cartomancer-v.4.10-fix-fix/core/view-deck-steamodded.lua b/Cartomancer-v.4.10-fix-fix/core/view-deck-steamodded.lua new file mode 100644 index 0000000..1357ec4 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/core/view-deck-steamodded.lua @@ -0,0 +1,143 @@ + +local Cartomancer_replacements = { + { + find = [[ + for k, v in ipairs%(G%.playing_cards%) do + if v%.base%.suit then table%.insert%(SUITS%[v%.base%.suit%], v%) end]], + -- Steamodded<0917b + find_alt = [[ + for k, v in ipairs%(G%.playing_cards%) do + table%.insert%(SUITS%[v%.base%.suit%], v%)]], + place = [[ +local SUITS_SORTED = Cartomancer.tablecopy(SUITS) +for k, v in ipairs(G.playing_cards) do + if v.base.suit then + local greyed + if unplayed_only and not ((v.area and v.area == G.deck) or v.ability.wheel_flipped) then + greyed = true + end + local card_string = v:cart_to_string() + if greyed then + card_string = card_string .. "Greyed" -- for some reason format doesn't work and final string is `sGreyed` + end + if greyed and Cartomancer.SETTINGS.deck_view_hide_drawn_cards then + -- Ignore this card. + elseif not Cartomancer.SETTINGS.deck_view_stack_enabled then + -- Don't stack cards + local _scale = 0.7 + local copy = copy_card(v, nil, _scale) + + copy.greyed = greyed + copy.stacked_quantity = 1 + table.insert(SUITS_SORTED[v.base.suit], copy) + + elseif not SUITS[v.base.suit][card_string] then + -- Initiate stack + table.insert(SUITS_SORTED[v.base.suit], card_string) + + local _scale = 0.7 + local copy = copy_card(v, nil, _scale) + + copy.greyed = greyed + copy.stacked_quantity = 1 + + SUITS[v.base.suit][card_string] = copy + else + -- Stack cards + local stacked_card = SUITS[v.base.suit][card_string] + stacked_card.stacked_quantity = stacked_card.stacked_quantity + 1 + end + end]] + }, + + { + find = "card_limit = #SUITS%[suit_map%[j%]%],", + place = "card_limit = #SUITS_SORTED[suit_map[j]]," + }, + + { + find = [[ +for i = 1%, %#SUITS%[suit_map%[j%]%] do + if SUITS%[suit_map%[j%]%]%[i%] then + local greyed%, _scale = nil%, 0%.7 + if unplayed_only and not %(%(SUITS%[suit_map%[j%]%]%[i%]%.area and SUITS%[suit_map%[j%]%]%[i%]%.area == G%.deck%) or SUITS%[suit_map%[j%]%]%[i%]%.ability%.wheel_flipped%) then + greyed = true + end + local copy = copy_card%(SUITS%[suit_map%[j%]%]%[i%]%, nil%, _scale%) + copy%.greyed = greyed + copy%.T%.x = view_deck%.T%.x %+ view_deck%.T%.w %/ 2 + copy%.T%.y = view_deck%.T%.y + + copy:hard_set_T%(%) + view_deck:emplace%(copy%) + end + end]], + place = [[ +for i = 1%, %#SUITS_SORTED%[suit_map%[j%]%] do + local card + if not Cartomancer.SETTINGS.deck_view_stack_enabled then + card = SUITS_SORTED%[suit_map%[j%]%]%[i%] + else + local card_string = SUITS_SORTED%[suit_map%[j%]%]%[i%] + card = SUITS%[suit_map%[j%]%]%[card_string%] + end + + card%.T%.x = view_deck%.T%.x %+ view_deck%.T%.w%/2 + card%.T%.y = view_deck%.T%.y + card:create_quantity_display%(%) + + card:hard_set_T%(%) + view_deck:emplace%(card%) +end]] + }, + + { + find = ' modded and {n = G.UIT.R, config = {align = "cm"}, nodes = {', + place = [=[ + not unplayed_only and Cartomancer.add_unique_count() or nil, + modded and {n = G.UIT.R, config = {align = "cm"}, nodes = {]=] + }, + +} + + +-- Mom, can we have lovely patches for overrides.lua? +-- No, we have lovely patches at home + +-- Lovely patches at home: + +local Cartomancer_nfs_read +local Cartomancer_nfs_read_override = function (containerOrName, nameOrSize, sizeOrNil) + local data, size = Cartomancer_nfs_read(containerOrName, nameOrSize, sizeOrNil) + + if type(containerOrName) ~= "string" then + return data, size + end + local overrides = '/overrides.lua' + if containerOrName:sub(-#overrides) ~= overrides then + return data, size + end + + local replaced = 0 + local total_replaced = 0 + for _, v in ipairs(Cartomancer_replacements) do + data, replaced = string.gsub(data, v.find, v.place) + + if replaced == 0 and v.find_alt then + data, replaced = string.gsub(data, v.find_alt, v.place) + end + + if replaced == 0 then + print("Failed to replace " .. v.find .. " for overrides.lua") + else + total_replaced = total_replaced + 1 + end + end + + print("Totally applied " .. total_replaced .. " replacements to overrides.lua") + + -- We no longer need this override + NFS.read = Cartomancer_nfs_read + + return data, size +end diff --git a/Cartomancer-v.4.10-fix-fix/core/view-deck.lua b/Cartomancer-v.4.10-fix-fix/core/view-deck.lua new file mode 100644 index 0000000..e32945f --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/core/view-deck.lua @@ -0,0 +1,147 @@ + +function Card:cart_to_string(args) + local args = args or {} + + local suit = self.base and ( + -- if has base, check for stone / no_suit + -- only use NoSuit for unique_count, deck view displays every stone card in respective suit area + (self.ability.effect == 'Stone Card' or self.config.center.no_suit) and args.unique_count and 'NoSuit' or self.base.suit + -- otherwise empty string + ) or '' + + local rank = self.base and ( + -- if has base, check for stone / no_rank + (self.ability.effect == 'Stone Card' or self.config.center.no_rank) and 'NoRank' or self.base.value + -- otherwise empty string + ) or '' + + if not Cartomancer.SETTINGS.deck_view_stack_chips then + rank = rank .. tostring(self:get_chip_bonus()) + end + + if not args.unique_count and Cartomancer.SETTINGS.deck_view_stack_modifiers then + return string.format( + "%s%s", + suit, + rank, + self.greyed and 'Greyed' or '' + ) + end + + return string.format( + "%s%s%s%s%s%s%s%s%s%s", + suit, + rank, + self.ability and self.ability.name or '', + self.edition and (self.edition.type or next(self.edition)) or '', + self.seal or '', + -- TODO : steamodded stickers compatibility + self.eternal and 'Eternal' or '', + self.perishable and 'Perishable' or '', + self.rental and 'Rental' or '', + self.debuff and 'Debuff' or '', + + self.greyed and 'Greyed' or '' + ) +end + +-- Util +function Cartomancer.tablecopy(t) + local t2 = {} + for k,v in pairs(t) do + t2[k] = v + end + return t2 +end + +function Cartomancer.table_size(t) + local size = 0 + for _, _ in pairs(t) do + size = size + 1 + end + + return size +end + +function Cartomancer.add_unique_count() + local unique_count = 0 + + local all_keys = {} + + for i = 1, #G.playing_cards do + local key = G.playing_cards[i]:cart_to_string{ unique_count=true } + if not all_keys[key] then + all_keys[key] = true + unique_count = unique_count + 1 + end + end + + -- for _, cards in pairs(SUITS_SORTED) do + -- unique_count = unique_count + #cards + -- end + + return {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = localize('carto_deck_view_unique_cards')..' '..tostring(unique_count), colour = G.C.WHITE, scale = 0.3}}, + }} +end + +-- Handle amount display + +----- Copied from incantation +G.FUNCS.disable_quantity_display = function(e) + local preview_card = e.config.ref_table + e.states.visible = preview_card.stacked_quantity > 1 +end + + +function Card:create_quantity_display() + if not Cartomancer.SETTINGS.deck_view_stack_enabled then + return + end + + local X_COLOR = HEX(Cartomancer.SETTINGS.deck_view_stack_x_color) + + if not self.children.stack_display and self.stacked_quantity > 1 then + self.children.stack_display = UIBox { + definition = { + n = G.UIT.ROOT, + config = { + minh = 0.5, + maxh = 1.2, + minw = 0.43, + maxw = 2, + r = 0.001, + padding = 0.1, + align = 'cm', + colour = adjust_alpha(darken(G.C.BLACK, 0.2), Cartomancer.SETTINGS.deck_view_stack_background_opacity / 100), + shadow = false, + func = 'disable_quantity_display', + ref_table = self + }, + nodes = { + { + n = G.UIT.T, -- node type + config = { text = 'x', scale = 0.35, colour = X_COLOR } + , padding = -1 + }, + { + n = G.UIT.T, -- node type + config = { + ref_table = self, ref_value = 'stacked_quantity', + scale = 0.35, colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + config = { + align = (Cartomancer.SETTINGS.deck_view_stack_pos_vertical:sub(1, 1)) .. (Cartomancer.SETTINGS.deck_view_stack_pos_horizontal:sub(1, 1)), + bond = 'Strong', + parent = self + }, + states = { + collide = { can = false }, + drag = { can = true } + } + } + end +end diff --git a/Cartomancer-v.4.10-fix-fix/internal/atlas.lua b/Cartomancer-v.4.10-fix-fix/internal/atlas.lua new file mode 100644 index 0000000..756673f --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/internal/atlas.lua @@ -0,0 +1,25 @@ +local function asset_path(filename) + return Cartomancer.path.."/assets/"..G.SETTINGS.GRAPHICS.texture_scaling.."x/"..filename +end + +local assets = { + {name = 'cart_modicon', path = asset_path('modicon.png'), px = 32, py = 32}, + {name = 'cart_settings', path = asset_path('settings.png'), px = 80, py = 80}, +} + +local game_set_render_settings = Game.set_render_settings + +function Game:set_render_settings() + game_set_render_settings(self) + + for i=1, #assets do + G.ASSET_ATLAS[assets[i].name] = {} + G.ASSET_ATLAS[assets[i].name].name = assets[i].name + -- File load method using steamodded's code + local file_data = assert(Cartomancer.nfs.newFileData(assets[i].path), 'Failed to collect file data for '..assets[i].name) + local image_data = assert(love.image.newImageData(file_data), 'Failed to initialize image data for '..assets[i].name) + G.ASSET_ATLAS[assets[i].name].image = love.graphics.newImage(image_data, {mipmaps = true, dpiscale = G.SETTINGS.GRAPHICS.texture_scaling}) + G.ASSET_ATLAS[assets[i].name].px = assets[i].px + G.ASSET_ATLAS[assets[i].name].py = assets[i].py + end +end diff --git a/Cartomancer-v.4.10-fix-fix/internal/config.lua b/Cartomancer-v.4.10-fix-fix/internal/config.lua new file mode 100644 index 0000000..91dacb7 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/internal/config.lua @@ -0,0 +1,78 @@ + +Cartomancer.save_config = function () + Cartomancer.log "Saving cartomancer config..." + love.filesystem.write('config/cartomancer.jkr', "return " .. Cartomancer.dump(Cartomancer.SETTINGS)) +end + +-- Logic for keeping config tables up to date +local function remove_unused_keys(from_table, template) + for k, v in pairs(from_table) do + if template[k] == nil then + Cartomancer.log("Removing setting `"..k.. "` because it is not in default config") + from_table[k] = nil + end + end +end + +local function add_missing_keys(to_table, template) + for k, v in pairs(template) do + if to_table[k] == nil then + Cartomancer.log("Adding setting `"..k.. "` because it is missing in latest config") + to_table[k] = v + end + end +end + +Cartomancer.load_config = function () + Cartomancer.log "Starting to load config" + if not love.filesystem.getInfo('config') then + Cartomancer.log("Creating config folder...") + love.filesystem.createDirectory('config') + end + + -- Steamodded config file location + local config_file = love.filesystem.read('config/cartomancer.jkr') + local latest_default_config = Cartomancer.load_mod_file('config.lua', 'default-config') + + if config_file then + Cartomancer.log "Reading config file: " + Cartomancer.log(config_file) + Cartomancer.SETTINGS = STR_UNPACK(config_file) -- Use STR_UNPACK to avoid code injectons via config files + else + Cartomancer.log "Creating default settings" + Cartomancer.SETTINGS = latest_default_config + Cartomancer.save_config() + end + + remove_unused_keys(Cartomancer.SETTINGS, latest_default_config) + add_missing_keys(Cartomancer.SETTINGS, latest_default_config) + + remove_unused_keys(Cartomancer.SETTINGS.keybinds, latest_default_config.keybinds) + add_missing_keys(Cartomancer.SETTINGS.keybinds, latest_default_config.keybinds) + + Cartomancer.log "Successfully loaded config: " + Cartomancer.log(Cartomancer.SETTINGS) +end + +local cart_options_ref = G.FUNCS.options +G.FUNCS.options = function(e) + if Cartomancer.INTERNAL_in_config then + Cartomancer.INTERNAL_in_config = false + if Cartomancer._recording_keybind then + Cartomancer.log "Quit config, stopping to record keybind" + Cartomancer._recording_keybind = nil + end + Cartomancer.save_config() + end + return cart_options_ref(e) +end + +local settings_tab_ref = G.UIDEF.settings_tab +function G.UIDEF.settings_tab(tab) + if Cartomancer._recording_keybind then + Cartomancer.log "Changed settings tab, stopping to record keybind" + Cartomancer._recording_keybind = nil + end + -- Maybe I should port cartomancer settings to use this function as well? but it would still not work with other mods that add custom tabs :sob: + return settings_tab_ref(tab) +end \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/internal/init.lua b/Cartomancer-v.4.10-fix-fix/internal/init.lua new file mode 100644 index 0000000..c4d75a2 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/internal/init.lua @@ -0,0 +1,99 @@ +Cartomancer = {} + +Cartomancer.SETTINGS = {} + +Cartomancer.nfs = require "cartomancer.nfs" +local lovely = require "lovely" + +Cartomancer.INTERNAL_debugging = not not love.filesystem.getInfo('cartomancer_debugging') + +Cartomancer.use_smods = function () + return SMODS and not (MODDED_VERSION == "0.9.8-STEAMODDED") +end + + +Cartomancer.find_self = function (target_filename) + local mods_path = lovely.mod_dir + + local mod_folders = Cartomancer.nfs.getDirectoryItems(mods_path) + for _, folder in pairs(mod_folders) do + local path = string.format('%s/%s', mods_path, folder) + local files = Cartomancer.nfs.getDirectoryItems(path) + + for _, filename in pairs(files) do + if filename == target_filename then + return path + end + end + end +end + +Cartomancer.load_mod_file = function (path, name, as_txt) + name = name or path + + local file, err = Cartomancer.nfs.read(Cartomancer.path..'/'..path) + + assert(file, string.format([=[[Cartomancer] Failed to load mod file %s (%s).: +%s + +Get latest release here: https://github.com/stupxd/Cartomancer/releases ]=], path, name, tostring(err))) + + return as_txt and file or load(file, string.format(" Cartomancer - %s ", name))() +end + +Cartomancer.log = function (msg) + if Cartomancer.INTERNAL_debugging then + local msg = type(msg) == "string" and msg or Cartomancer.dump(msg) + + print("[Cartomancer] "..msg) + end +end + +Cartomancer.dump = function (o, level, prefix) + level = level or 1 + prefix = prefix or ' ' + if type(o) == 'table' and level <= 5 then + local s = '{ \n' + for k, v in pairs(o) do + local format + if type(k) == 'number' then + format = '%s[%d] = %s,\n' + else + format = '%s["%s"] = %s,\n' + end + s = s .. string.format( + format, + prefix, + k, + -- Compact parent & draw_major to avoid recursion and huge dumps. + (k == 'parent' or k == 'draw_major') and string.format("'%s'", tostring(v)) or Cartomancer.dump(v, level + 1, prefix..' ') + ) + end + return s..prefix:sub(3)..'}' + else + if type(o) == "string" then + return string.format('"%s"', o) + end + + if type(o) == "function" or type(o) == "table" then + return string.format("'%s'", tostring(o)) + end + + return tostring(o) + end +end + +Cartomancer.table_join_keys = function (tab_, separator) + local separator = separator or "" + local inline + for k, _ in pairs(tab_) do + inline = (inline and inline..separator or "") .. k + end + + return inline or "[empty]" +end + +Cartomancer.do_nothing = function (...) end + + +return Cartomancer \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/internal/keybinds.lua b/Cartomancer-v.4.10-fix-fix/internal/keybinds.lua new file mode 100644 index 0000000..a725dfd --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/internal/keybinds.lua @@ -0,0 +1,128 @@ +Cartomancer.INTERNAL_keybinds = {} + +-- Lock for activated keybinds to not trigger multiple times +local activated = {} + +-- Check if a specific keybind can +local function is_keybind_pressed(controller, name) + local required_keys = Cartomancer.SETTINGS.keybinds[name] + + for key, _ in pairs(required_keys) do + if not controller.held_keys[key] then + return false + end + end + return true +end + +-- On key press, check keybinds that can activate +local function check_keybinds_activation(controller) + for name, func in pairs(Cartomancer.INTERNAL_keybinds) do + if not activated[name] and is_keybind_pressed(controller, name) then + func(controller) + activated[name] = true + end + end +end + +-- On key unpress, check active keybinds and remove the ones that should deactivate +local function check_keybinds_deactivation(controller) + local to_remove = {} + for name, _ in pairs(activated) do + if not is_keybind_pressed(controller, name) then + table.insert(to_remove, name) + end + end + + for _, name in pairs(to_remove) do + activated[name] = nil + end +end + +-- +-- Public functions to handle keybinds +-- + +function Cartomancer.register_keybind(args) + assert(type(args.name) == "string", 'keybind args `name` is missing or not a string') + assert(type(args.func) == "function", 'keybind args `func` is missing or not a function') + + assert(Cartomancer.SETTINGS.keybinds[args.name], 'invalid keybind name: '..args.name) + + Cartomancer.INTERNAL_keybinds[args.name] = args.func +end + +function Cartomancer.record_keybind(args) + Cartomancer.log "Starting to record keybind" + if Cartomancer._recording_keybind then + Cartomancer.log "Already recording keybind, resetting that one!" + local existing_keybind = Cartomancer.SETTINGS.keybinds[Cartomancer._recording_keybind.name] + Cartomancer._recording_keybind.callback(existing_keybind) + Cartomancer._recording_keybind = nil + --return + end + assert(type(args.name) == "string", "missing keybind name") + + if not args.callback then + args.callback = function (new_keys) + Cartomancer.SETTINGS.keybinds[args.name] = new_keys + end + end + assert(type(args.callback) == "function", 'arg `callback` must be a function') + -- optional arg display pressed keys live + args.press_callback = args.press_callback or Cartomancer.do_nothing + assert(type(args.press_callback) == "function", 'arg `press_callback` must be a function') + Cartomancer._recording_keybind = { + name = args.name, + pressed = {}, + callback = args.callback, + press_callback = args.press_callback + } +end + +-- +-- Handle key press / release +-- + +local on_press = Controller.key_press +function Controller:key_press(key) + if key == 'escape' and Cartomancer._recording_keybind then + -- Reset keybind completely + Cartomancer.log "Resetting keybind" + local empty_keybind = {['[none]'] = true} + Cartomancer._recording_keybind.callback(empty_keybind) + Cartomancer._recording_keybind = nil + return + end + + local ret = on_press(self, key) + + if Cartomancer._recording_keybind then + Cartomancer.log("Adding key "..key) + Cartomancer._recording_keybind.pressed[key] = true + Cartomancer._recording_keybind.press_callback(Cartomancer._recording_keybind.pressed) + else + -- Only check activation if not recording + check_keybinds_activation(self) + end + + return ret +end + +local on_release = Controller.key_release +function Controller:key_release(key) + local ret = on_release(self, key) + + -- Only callback if key was pressed during keybind recording + if Cartomancer._recording_keybind and Cartomancer._recording_keybind.pressed[key] then + Cartomancer.log "Saving keybind" + Cartomancer._recording_keybind.callback(Cartomancer._recording_keybind.pressed) + Cartomancer._recording_keybind = nil + end + -- Keybinds should still deactivate even during recording + check_keybinds_deactivation(self) + + return ret +end + + diff --git a/Cartomancer-v.4.10-fix-fix/internal/localization.lua b/Cartomancer-v.4.10-fix-fix/internal/localization.lua new file mode 100644 index 0000000..c97851d --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/internal/localization.lua @@ -0,0 +1,18 @@ +if Cartomancer.use_smods() then return end + +-- Vanilla will only support default loc cuz yes. +local loc_table = Cartomancer.load_mod_file('localization/en-us.lua', 'localization') + +-- Credits: Steamodded +-- I was lazy and it's not like I'm going to code anything different from this anyways~ +local function recurse(target, ref_table) + if type(target) ~= 'table' then return end --this shouldn't happen unless there's a bad return value + for k, v in pairs(target) do + if not ref_table[k] or (type(v) ~= 'table') then + ref_table[k] = v + else + recurse(v, ref_table[k]) + end + end +end +recurse(loc_table, G.localization) diff --git a/Cartomancer-v.4.10-fix-fix/internal/ui.lua b/Cartomancer-v.4.10-fix-fix/internal/ui.lua new file mode 100644 index 0000000..7b37017 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/internal/ui.lua @@ -0,0 +1,507 @@ + +-- Setting max intensity to this value disables limit. +Cartomancer._INTERNAL_max_flames_intensity = 40 + +local create_column_tabs, + create_inline_slider, + create_toggle_option, + create_keybind, + create_text_line, + create_input_option, + create_inline_options, + create_option_cycle_custom + +local create_UIBox_generic_options_custom = function (args) + args = args or {} + + return {n=G.UIT.ROOT, config = {align = "cl", minw = G.ROOM.T.w*0.6, padding = 0.0, r = 0.1, + colour = args.bg_colour or {G.C.GREY[1], G.C.GREY[2], G.C.GREY[3],0.7}}, + nodes = { + {n=G.UIT.C, config={align = "cl", padding = 0, minw = args.minw or 5, minh = args.minh or 3}, + nodes = args.contents + }, + } + } +end + +local function is_chosen(tab) + return Cartomancer.LAST_OPEN_TAB == tab +end + +local function choose_tab(tab) + Cartomancer.LAST_OPEN_TAB = tab + if Cartomancer._recording_keybind and not (tab == "keybinds") then + Cartomancer.log "Switched settings tab, stopping recording keybind" + Cartomancer._recording_keybind = nil + end +end + +local tab_config = {r = 0.1, align = "t", padding = 0.0, colour = G.C.CLEAR, minw = 8.5, minh = 6} + +Cartomancer.config_tab = function() + Cartomancer.INTERNAL_in_config = true + Cartomancer.log "Opened cartomancer config" + local vertical_tabs = {} + + choose_tab "compact_deck" + + + table.insert(vertical_tabs, { + label = localize('carto_settings_compact_deck'), + chosen = is_chosen("compact_deck"), + tab_definition_function = function (...) + choose_tab "compact_deck" + -- Yellow node. Align changes the position of modes inside + return {n = G.UIT.ROOT, config = tab_config, nodes = { + create_toggle_option { + ref_value = 'compact_deck_enabled', + localization = 'carto_compact_deck_enabled', + }, + create_inline_slider({ref_value = 'compact_deck_visible_cards', localization = 'carto_compact_deck_visible_cards', max_value = 300}), + }} + end + }) + + table.insert(vertical_tabs, { + label = localize('carto_settings_deck_view'), + chosen = is_chosen("deck_view"), + tab_definition_function = function (...) + choose_tab "deck_view" + return {n = G.UIT.ROOT, config = tab_config, nodes = { + create_toggle_option { + ref_value = 'deck_view_hide_drawn_cards', + localization = 'carto_deck_view_hide_drawn_cards', + }, + create_toggle_option { + ref_value = 'deck_view_stack_enabled', + localization = 'carto_deck_view_stack_enabled', + }, + create_toggle_option { + ref_value = 'deck_view_stack_modifiers', + localization = 'carto_deck_view_stack_modifiers', + }, + create_toggle_option { + ref_value = 'deck_view_stack_chips', + localization = 'carto_deck_view_stack_chips', + }, + --create_toggle_option('deck_view_stack_suits', 'carto_deck_view_stack_suits'), + create_inline_slider({ref_value = 'deck_view_stack_background_opacity', localization = 'carto_deck_view_stack_background_opacity',}), + create_input_option('deck_view_stack_x_color', 'carto_deck_view_stack_x_color', 6), + + -- inline this + {n = G.UIT.R, config = {align = "cl", padding = 0.05}, nodes = { + {n = G.UIT.C, config = {align = "l", padding = 0}, nodes = { + create_option_cycle_custom('deck_view_stack_pos_vertical', 'carto_deck_view_stack_pos_vertical', + 'cartomancer_deck_view_pos_vertical', 'carto_deck_view_stack_pos_vertical_options'), + }}, + {n = G.UIT.C, config = {align = "r", padding = 0}, nodes = { + create_option_cycle_custom('deck_view_stack_pos_horizontal', 'carto_deck_view_stack_pos_horizontal', + 'cartomancer_deck_view_pos_horizontal', 'carto_deck_view_stack_pos_horizontal_options'), + }}, + }} + }} + end + }) + + table.insert(vertical_tabs, { + label = localize('carto_settings_jokers'), + chosen = is_chosen("jokers"), + tab_definition_function = Cartomancer.jokers_visibility_menu + }) + + table.insert(vertical_tabs, { + label = localize('carto_settings_flames'), + chosen = is_chosen("flames"), + tab_definition_function = function (...) + choose_tab "flames" + return {n = G.UIT.ROOT, config = tab_config, nodes = { + create_inline_slider({ref_value = 'flames_intensity_min', localization = 'carto_flames_intensity_min', max_value = Cartomancer._INTERNAL_max_flames_intensity, decimal_places = 1}), + create_inline_slider({ref_value = 'flames_intensity_max', localization = 'carto_flames_intensity_max', max_value = Cartomancer._INTERNAL_max_flames_intensity, decimal_places = 1}), + create_toggle_option { + ref_value = 'flames_relative_intensity', + localization = 'carto_flames_relative_intensity', + }, + create_toggle_option { + ref_value = 'flames_slower_speed', + localization = 'carto_flames_slower_speed', + }, + create_inline_slider({ref_value = 'flames_volume', localization = 'carto_flames_volume',}), + -- + }} + end + }) + + + table.insert(vertical_tabs, { + label = localize('carto_settings_other'), + chosen = is_chosen("other"), + tab_definition_function = function (...) + choose_tab "other" + return {n = G.UIT.ROOT, config = tab_config, nodes = { + create_toggle_option { + ref_value = 'improved_hand_sorting', + localization = 'carto_improved_hand_sorting', + callback = function () G.FUNCS.change_play_discard_position {to_key = G.SETTINGS.play_button_pos} end + }, + create_toggle_option { + ref_value = 'draw_non_essential_shaders', + localization = 'carto_draw_non_essential_shaders', + }, + create_toggle_option { + ref_value = 'hide_tags', + localization = 'carto_hide_tags', + callback = function () Cartomancer.update_tags_visibility() end + }, + create_toggle_option { + ref_value = 'hide_consumables', + localization = 'carto_hide_consumables', + }, + create_toggle_option { + ref_value = 'hide_deck', + localization = 'carto_hide_deck', + }, + create_toggle_option { + ref_value = 'hide_jokers', + localization = 'carto_hide_jokers', + }, + }} + end + }) + + table.insert(vertical_tabs, { + label = localize('carto_settings_keybinds'), + chosen = is_chosen("keybinds"), + tab_definition_function = function (...) + choose_tab "keybinds" + return {n = G.UIT.ROOT, config = tab_config, nodes = { + create_keybind { + name = 'hide_joker', + localization = 'carto_kb_hide_joker', + }, + create_keybind { + name = 'toggle_tags', + localization = 'carto_kb_toggle_tags', + }, + create_keybind { + name = 'toggle_consumables', + localization = 'carto_kb_toggle_consumables', + }, + create_keybind { + name = 'toggle_jokers', + localization = 'carto_kb_toggle_jokers', + }, + create_keybind { + name = 'toggle_jokers_buttons', + localization = 'carto_kb_toggle_jokers_buttons', + }, + }} + end + }) + + return create_UIBox_generic_options_custom({ + bg_colour = G.C.CLEAR,-- G.C.BLUE, + contents = { + { + n = G.UIT.R, + config = { padding = 0, align = "tl", minw = 9, colour = G.C.CLEAR }, + nodes = { + create_column_tabs({ + tab_alignment = 'tl', + tab_w = 8, + tab_h = 4.3,-- this seems to not do shit? + text_scale = 0.4, + snap_to_nav = true, + colour = G.C.CLEAR,-- G.C.RED, + tabs = vertical_tabs + }) + } + }, + } + }) +end + +Cartomancer.jokers_visibility_standalone_menu = function () + return {n = G.UIT.C, config = {r = 0.1, align = "cm", padding = 0.0, colour = G.C.BLUE, minw = 8.5, minh = 6}, nodes = { + Cartomancer.jokers_visibility_menu(), + + }} +end + +Cartomancer.jokers_visibility_menu = function () + + choose_tab "jokers" + + return {n = G.UIT.ROOT, config = tab_config, nodes = { + create_toggle_option { + ref_value = 'jokers_controls_buttons', + localization = 'carto_jokers_controls_buttons', + }, + create_inline_slider({ref_value = 'jokers_controls_show_after', localization = 'carto_jokers_controls_show_after',}), + --create_text_line{ loc = 'carto_jokers_hide_keybind' }, + }} +end + + +create_inline_slider = function (args) + local args = args or {} + + local slider = create_slider({label = localize(args.localization), label_scale = 0.36, w = 3, h = 0.3, padding = -0.05, + ref_table = Cartomancer.SETTINGS, ref_value = args.ref_value, min = args.min_value or 0, max = args.max_value or 100, + decimal_places = args.decimal_places}) + + slider.nodes[1].config.align = "cl" + + for _, node in pairs(slider.nodes) do + node.n = G.UIT.C + end + -- slider.nodes[2].nodes[1].n = G.UIT.R + return slider +end + +local function starts_with(str, start) + return str:sub(1, #start) == start + end + +local function find_option(options, value) + for i, str in pairs(options) do + if starts_with(str, value) then + return i + end + end +end + +create_option_cycle_custom = function (ref_value, localization, change_function, options) + local options_loc = localize(options) + + local cycle = create_option_cycle({w = 3, label = localize(localization),scale = 0.7, options = options_loc, + opt_callback = change_function, current_option = find_option(options_loc, Cartomancer.SETTINGS[ref_value])}) + + cycle.config.padding = 0 + + return + {n = G.UIT.R, config = {align = "cl", padding = 0.05}, w = 0.4, colour = G.C.CHIPS, nodes = { + cycle + }} +end + +create_toggle_option = function (args) + return {n = G.UIT.R, config = {align = "cl", padding = 0.05}, nodes = { + {n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = { + { n = G.UIT.T, config = { text = localize(args.localization), scale = 0.35, colour = G.C.UI.TEXT_LIGHT }}, + }}, + {n = G.UIT.C, config = { align = "cr", padding = 0.05 }, nodes = { + create_toggle{ col = true, label = "", scale = 0.70, w = 0, shadow = true, ref_table = Cartomancer.SETTINGS, ref_value = args.ref_value, callback = args.callback }, + }}, + }} +end + +create_keybind = function (args) + assert(args.name, "Missing `name` in create_keybind " .. Cartomancer.dump(args)) + + local ref_table = { + name = args.name, + label = { + text = Cartomancer.table_join_keys(Cartomancer.SETTINGS.keybinds[args.name], "+") + }, + } + + local id = 'kb_'..args.name + + return + {n = G.UIT.R, config = {align = "cr", padding = 0.05}, nodes = { + {n = G.UIT.C, config = { align = "cl", padding = 0 }, nodes = { + { n = G.UIT.T, config = { text = localize(args.localization), scale = 0.35, colour = G.C.UI.TEXT_LIGHT }}, + }}, + {n = G.UIT.C, config = { align = "cr", padding = 0.05 }, nodes = { + UIBox_button({id = id, ref_table = ref_table, colour = G.C.GREY, button = 'cartomancer_settings_change_keybind', label = {}, dynamic_label = ref_table.label, + minh = 0.32, minw = 3, col = true, scale = 0.3, + }) + }}, + }} +end + +create_input_option = function (ref_value, localization, max_length) + return { n = G.UIT.R, config = {align = "cl", minw = 4, minh = 0.5, colour = G.C.CLEAR, padding = 0.05}, nodes = { + { n = G.UIT.T, config = {text = localize(localization), scale = .36, minw = 4, minh = 0.5, colour = G.C.WHITE} }, + create_text_input({ id = 'Input:'..ref_value, w = 2, max_length = max_length or 3, prompt_text = tostring(Cartomancer.SETTINGS[ref_value]), ref_table = Cartomancer.SETTINGS, ref_value = ref_value}) + }} +end + +create_text_line = function(args) + return { n = G.UIT.R, config = {align = "cl", minw = 4, minh = 0.5, padding = 0.05, colour = G.C.CLEAR}, nodes = { + { n = G.UIT.T, config = {text = localize(args.loc), scale = .36, minw = 4, minh = 0.5, colour = G.C.WHITE} }, + }} +end + +create_column_tabs = function (args) + args = args or {} + args.colour = args.colour or G.C.CLEAR + args.tab_alignment = args.tab_alignment or 'cl' + args.opt_callback = args.opt_callback or nil + args.scale = args.scale or 1 + args.tab_w = args.tab_w or 0 + args.tab_h = args.tab_h or 0 + args.text_scale = (args.text_scale or 0.5) + + local tab_buttons = {} + + for k, v in ipairs(args.tabs) do + if v.chosen then args.current = {k = k, v = v} end + local id = 'tab_but_'..(v.label or '') + tab_buttons[#tab_buttons+1] = {n=G.UIT.R, config={align = "cm"}, nodes={ + UIBox_button({id = id, ref_table = v, button = 'cartomancer_settings_change_tab', label = {v.label}, + minh = 0.8*args.scale, minw = 2.5*args.scale, col = true, choice = true, scale = args.text_scale, + chosen = v.chosen and 'vert', func = v.func, focus_args = {type = 'none'}}) + }} + end + + -- Tabs + Contents + return {n=G.UIT.R, config={padding = 0.2, align = "cl", colour = args.colour,}, + + nodes={ + -- Tabs + {n=G.UIT.C, config={align = "cl", padding = 0.2, colour = G.C.CLEAR}, nodes=tab_buttons}, + + -- Tab contents + {n=G.UIT.C, config={align = args.tab_alignment, padding = args.padding or 0.1, no_fill = true, minh = args.tab_h, minw = args.tab_w}, nodes={ + {n=G.UIT.O, config={id = 'cartomancer_settings_tab_contents', + old_chosen = tab_buttons[1].nodes[1].nodes[1], + object = UIBox{definition = args.current.v.tab_definition_function(args.current.v.tab_definition_function_args), + config = {offset = {x=0,y=0}}}} + } + }}, + } + } +end + + +G.FUNCS.cartomancer_deck_view_pos_vertical = function(args) + Cartomancer.SETTINGS.deck_view_stack_pos_vertical = args.to_val +end + +G.FUNCS.cartomancer_deck_view_pos_horizontal = function(args) + Cartomancer.SETTINGS.deck_view_stack_pos_horizontal = args.to_val +end + +G.FUNCS.cartomancer_settings_change_keybind = function(e) + local name = e.config.ref_table.name + local dynamic_label = e.config.ref_table.label + dynamic_label.text = localize "carto_waiting_keybind" + + Cartomancer.record_keybind { + name = name, + callback = function (keys) + if not keys then + Cartomancer.log("No keys pressed! No keybind recorded") + dynamic_label.text = 'error :c' + return + end + dynamic_label.text = Cartomancer.table_join_keys(keys, "+") + + Cartomancer.SETTINGS.keybinds[name] = keys + Cartomancer.log("Saved keybind: " ..Cartomancer.table_join_keys(keys, "+")) + end, + press_callback = function (keys) + dynamic_label.text = Cartomancer.table_join_keys(keys, "+") + end, + } +end + +G.FUNCS.cartomancer_settings_change_tab = function(e) + if not e then return end + + local tab_contents = e.UIBox:get_UIE_by_ID('cartomancer_settings_tab_contents') + if not tab_contents then return end + -- Same tab, don't rebuild it. + if tab_contents.config.oid == e.config.id then return end + + if tab_contents.config.old_chosen then tab_contents.config.old_chosen.config.chosen = nil end + + tab_contents.config.old_chosen = e + e.config.chosen = 'vert' + + tab_contents.config.oid = e.config.id + tab_contents.config.object:remove() + tab_contents.config.object = UIBox{ + definition = e.config.ref_table.tab_definition_function(e.config.ref_table.tab_definition_function_args), + config = {offset = {x=0,y=0}, parent = tab_contents, type = 'cm'} + } + tab_contents.UIBox:recalculate() + +end + +Cartomancer.add_settings_icon = function () + if Cartomancer.use_smods() then return end + local icon = Sprite(0,0,0.75,0.75,G.ASSET_ATLAS["cart_modicon"], {x=0, y=0}) + icon.states.drag.can = false + return {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, button = 'change_tab'}, nodes={ + {n=G.UIT.O, config={object = icon}}, + }} +end + + +--[=[ +{n=G.UIT.R, config={align = "cm", padding = 0.05, id = args.id or nil}, nodes={ + args.label and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = args.label, scale = 0.5*args.scale, colour = G.C.UI.TEXT_LIGHT}} + }} or nil, + {n=G.UIT.R, config={align = "cm", colour = G.C.CLEAR, padding = 0.0}, nodes = { + {n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, colour = G.C.CLEAR, id = args.id and (not args.label and args.id or nil) or nil, focus_args = args.focus_args}, nodes={ + {n=G.UIT.C, config={align = "cm",r = 0.1, minw = 0.6*args.scale, hover = not disabled, colour = not disabled and args.colour or G.C.BLACK,shadow = not disabled, button = not disabled and 'option_cycle' or nil, ref_table = args, ref_value = 'l', focus_args = {type = 'none'}}, nodes={ + {n=G.UIT.T, config={ref_table = args, ref_value = 'l', scale = args.text_scale, colour = not disabled and G.C.UI.TEXT_LIGHT or G.C.UI.TEXT_INACTIVE}} + }}, + args.mid and + {n=G.UIT.C, config={id = 'cycle_main'}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + args.mid + }}, + not disabled and choice_pips or nil + }} + or {n=G.UIT.C, config={id = 'cycle_main', align = "cm", minw = args.w, minh = args.h, r = 0.1, padding = 0.05, colour = args.colour,emboss = 0.1, hover = true, can_collide = true, on_demand_tooltip = args.on_demand_tooltip}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = args, ref_value = "current_option_val"}}, colours = {G.C.UI.TEXT_LIGHT},pop_in = 0, pop_in_rate = 8, reset_pop_in = true,shadow = true, float = true, silent = true, bump = true, scale = args.text_scale, non_recalc = true})}}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + }}, + not disabled and choice_pips or nil + }} + }}, + {n=G.UIT.C, config={align = "cm",r = 0.1, minw = 0.6*args.scale, hover = not disabled, colour = not disabled and args.colour or G.C.BLACK,shadow = not disabled, button = not disabled and 'option_cycle' or nil, ref_table = args, ref_value = 'r', focus_args = {type = 'none'}}, nodes={ + {n=G.UIT.T, config={ref_table = args, ref_value = 'r', scale = args.text_scale, colour = not disabled and G.C.UI.TEXT_LIGHT or G.C.UI.TEXT_INACTIVE}} + }}, + }} + }}, + info, +}} +]=]-- + +create_inline_options = function (ref_value, localization, change_function, options) + local options_loc = localize(options) + + local cycle = create_option_cycle({w = 3, label = localize(localization),scale = 0.7, options = options_loc, + opt_callback = change_function, current_option = find_option(options_loc, Cartomancer.SETTINGS[ref_value])}) + cycle.n = G.UIT.R + cycle.config.align = "cl" + cycle.config.padding = 0 + cycle.config.colour = G.C.RED + cycle.config.minw = 6.5 + cycle.nodes[1].config.align = "cl" + cycle.nodes[2].config.align = "cr" + + for _, node in pairs(cycle.nodes) do + node.n = G.UIT.C + end + + for _, node in pairs(cycle.nodes[2].nodes) do + node.n = G.UIT.R + end + + return cycle +end + + + + + + diff --git a/Cartomancer-v.4.10-fix-fix/libs/nativefs.lua b/Cartomancer-v.4.10-fix-fix/libs/nativefs.lua new file mode 100644 index 0000000..7fde4fa --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/libs/nativefs.lua @@ -0,0 +1,493 @@ +--[[ +Copyright 2020 megagrump@pm.me + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]]-- + +local ffi, bit = require('ffi'), require('bit') +local C = ffi.C + +local fopen, getcwd, chdir, unlink, mkdir, rmdir +local BUFFERMODE, MODEMAP +local ByteArray = ffi.typeof('unsigned char[?]') +local function _ptr(p) return p ~= nil and p or nil end -- NULL pointer to nil + +local File = { + getBuffer = function(self) return self._bufferMode, self._bufferSize end, + getFilename = function(self) return self._name end, + getMode = function(self) return self._mode end, + isOpen = function(self) return self._mode ~= 'c' and self._handle ~= nil end, +} + +function File:open(mode) + if self._mode ~= 'c' then return false, "File " .. self._name .. " is already open" end + if not MODEMAP[mode] then return false, "Invalid open mode for " .. self._name .. ": " .. mode end + + local handle = _ptr(fopen(self._name, MODEMAP[mode])) + if not handle then return false, "Could not open " .. self._name .. " in mode " .. mode end + + self._handle, self._mode = ffi.gc(handle, C.fclose), mode + self:setBuffer(self._bufferMode, self._bufferSize) + + return true +end + +function File:close() + if self._mode == 'c' then return false, "File is not open" end + C.fclose(ffi.gc(self._handle, nil)) + self._handle, self._mode = nil, 'c' + return true +end + +function File:setBuffer(mode, size) + local bufferMode = BUFFERMODE[mode] + if not bufferMode then + return false, "Invalid buffer mode " .. mode .. " (expected 'none', 'full', or 'line')" + end + + if mode == 'none' then + size = math.max(0, size or 0) + else + size = math.max(2, size or 2) -- Windows requires buffer to be at least 2 bytes + end + + local success = self._mode == 'c' or C.setvbuf(self._handle, nil, bufferMode, size) == 0 + if not success then + self._bufferMode, self._bufferSize = 'none', 0 + return false, "Could not set buffer mode" + end + + self._bufferMode, self._bufferSize = mode, size + return true +end + +function File:getSize() + -- NOTE: The correct way to do this would be a stat() call, which requires a + -- lot more (system-specific) code. This is a shortcut that requires the file + -- to be readable. + local mustOpen = not self:isOpen() + if mustOpen and not self:open('r') then return 0 end + + local pos = mustOpen and 0 or self:tell() + C.fseek(self._handle, 0, 2) + local size = self:tell() + if mustOpen then + self:close() + else + self:seek(pos) + end + return size +end + +function File:read(containerOrBytes, bytes) + if self._mode ~= 'r' then return nil, 0 end + + local container = bytes ~= nil and containerOrBytes or 'string' + if container ~= 'string' and container ~= 'data' then + error("Invalid container type: " .. container) + end + + bytes = not bytes and containerOrBytes or 'all' + bytes = bytes == 'all' and self:getSize() - self:tell() or math.min(self:getSize() - self:tell(), bytes) + + if bytes <= 0 then + local data = container == 'string' and '' or love.data.newFileData('', self._name) + return data, 0 + end + + local data = love.data.newByteData(bytes) + local r = tonumber(C.fread(data:getFFIPointer(), 1, bytes, self._handle)) + + if container == 'data' then + -- FileData from ByteData requires LÖVE 11.4+ + local ok, fd = pcall(love.filesystem.newFileData, data, self._name) + if ok then return fd, r end + end + + local str = data:getString() + data:release() + data = container == 'data' and love.filesystem.newFileData(str, self._name) or str + return data, r +end + +local function lines(file, autoclose) + local BUFFERSIZE = 4096 + local buffer, bufferPos = ByteArray(BUFFERSIZE), 0 + local bytesRead = tonumber(C.fread(buffer, 1, BUFFERSIZE, file._handle)) + + local offset = file:tell() + return function() + file:seek(offset) + + local line = {} + while bytesRead > 0 do + for i = bufferPos, bytesRead - 1 do + if buffer[i] == 10 then -- end of line + bufferPos = i + 1 + return table.concat(line) + end + + if buffer[i] ~= 13 then -- ignore CR + table.insert(line, string.char(buffer[i])) + end + end + + bytesRead = tonumber(C.fread(buffer, 1, BUFFERSIZE, file._handle)) + offset, bufferPos = offset + bytesRead, 0 + end + + if not line[1] then + if autoclose then file:close() end + return nil + end + return table.concat(line) + end +end + +function File:lines() + if self._mode ~= 'r' then error("File is not opened for reading") end + return lines(self) +end + +function File:write(data, size) + if self._mode ~= 'w' and self._mode ~= 'a' then + return false, "File " .. self._name .. " not opened for writing" + end + + local toWrite, writeSize + if type(data) == 'string' then + writeSize = (size == nil or size == 'all') and #data or size + toWrite = data + else + writeSize = (size == nil or size == 'all') and data:getSize() or size + toWrite = data:getFFIPointer() + end + + if tonumber(C.fwrite(toWrite, 1, writeSize, self._handle)) ~= writeSize then + return false, "Could not write data" + end + return true +end + +function File:seek(pos) + return self._handle and C.fseek(self._handle, pos, 0) == 0 +end + +function File:tell() + if not self._handle then return nil, "Invalid position" end + return tonumber(C.ftell(self._handle)) +end + +function File:flush() + if self._mode ~= 'w' and self._mode ~= 'a' then + return nil, "File is not opened for writing" + end + return C.fflush(self._handle) == 0 +end + +function File:isEOF() + return not self:isOpen() or C.feof(self._handle) ~= 0 or self:tell() == self:getSize() +end + +function File:release() + if self._mode ~= 'c' then self:close() end + self._handle = nil +end + +function File:type() return 'File' end + +function File:typeOf(t) return t == 'File' end + +File.__index = File + +----------------------------------------------------------------------------- + +local nativefs = {} +local loveC = ffi.os == 'Windows' and ffi.load('love') or C + +function nativefs.newFile(name) + if type(name) ~= 'string' then + error("bad argument #1 to 'newFile' (string expected, got " .. type(name) .. ")") + end + return setmetatable({ + _name = name, + _mode = 'c', + _handle = nil, + _bufferSize = 0, + _bufferMode = 'none' + }, File) +end + +function nativefs.newFileData(filepath) + local f = nativefs.newFile(filepath) + local ok, err = f:open('r') + if not ok then return nil, err end + + local data, err = f:read('data', 'all') + f:close() + return data, err +end + +function nativefs.mount(archive, mountPoint, appendToPath) + return loveC.PHYSFS_mount(archive, mountPoint, appendToPath and 1 or 0) ~= 0 +end + +function nativefs.unmount(archive) + return loveC.PHYSFS_unmount(archive) ~= 0 +end + +function nativefs.read(containerOrName, nameOrSize, sizeOrNil) + local container, name, size + if sizeOrNil then + container, name, size = containerOrName, nameOrSize, sizeOrNil + elseif not nameOrSize then + container, name, size = 'string', containerOrName, 'all' + else + if type(nameOrSize) == 'number' or nameOrSize == 'all' then + container, name, size = 'string', containerOrName, nameOrSize + else + container, name, size = containerOrName, nameOrSize, 'all' + end + end + + local file = nativefs.newFile(name) + local ok, err = file:open('r') + if not ok then return nil, err end + + local data, size = file:read(container, size) + file:close() + return data, size +end + +local function writeFile(mode, name, data, size) + local file = nativefs.newFile(name) + local ok, err = file:open(mode) + if not ok then return nil, err end + + ok, err = file:write(data, size or 'all') + file:close() + return ok, err +end + +function nativefs.write(name, data, size) + return writeFile('w', name, data, size) +end + +function nativefs.append(name, data, size) + return writeFile('a', name, data, size) +end + +function nativefs.lines(name) + local f = nativefs.newFile(name) + local ok, err = f:open('r') + if not ok then return nil, err end + return lines(f, true) +end + +function nativefs.load(name) + local chunk, err = nativefs.read(name) + if not chunk then return nil, err end + return loadstring(chunk, name) +end + +function nativefs.getWorkingDirectory() + return getcwd() +end + +function nativefs.setWorkingDirectory(path) + if not chdir(path) then return false, "Could not set working directory" end + return true +end + +function nativefs.getDriveList() + if ffi.os ~= 'Windows' then return { '/' } end + local drives, bits = {}, C.GetLogicalDrives() + for i = 0, 25 do + if bit.band(bits, 2 ^ i) > 0 then + table.insert(drives, string.char(65 + i) .. ':/') + end + end + return drives +end + +function nativefs.createDirectory(path) + local current = path:sub(1, 1) == '/' and '/' or '' + for dir in path:gmatch('[^/\\]+') do + current = current .. dir .. '/' + local info = nativefs.getInfo(current, 'directory') + if not info and not mkdir(current) then return false, "Could not create directory " .. current end + end + return true +end + +function nativefs.remove(name) + local info = nativefs.getInfo(name) + if not info then return false, "Could not remove " .. name end + if info.type == 'directory' then + if not rmdir(name) then return false, "Could not remove directory " .. name end + return true + end + if not unlink(name) then return false, "Could not remove file " .. name end + return true +end + +local function withTempMount(dir, fn, ...) + local mountPoint = _ptr(loveC.PHYSFS_getMountPoint(dir)) + if mountPoint then return fn(ffi.string(mountPoint), ...) end + if not nativefs.mount(dir, '__nativefs__temp__') then return false, "Could not mount " .. dir end + local a, b = fn('__nativefs__temp__', ...) + nativefs.unmount(dir) + return a, b +end + +function nativefs.getDirectoryItems(dir) + local result, err = withTempMount(dir, love.filesystem.getDirectoryItems) + return result or {} +end + +local function getDirectoryItemsInfo(path, filtertype) + local items = {} + local files = love.filesystem.getDirectoryItems(path) + for i = 1, #files do + local filepath = string.format('%s/%s', path, files[i]) + local info = love.filesystem.getInfo(filepath, filtertype) + if info then + info.name = files[i] + table.insert(items, info) + end + end + return items +end + +function nativefs.getDirectoryItemsInfo(path, filtertype) + local result, err = withTempMount(path, getDirectoryItemsInfo, filtertype) + return result or {} +end + +local function getInfo(path, file, filtertype) + local filepath = string.format('%s/%s', path, file) + return love.filesystem.getInfo(filepath, filtertype) +end + +local function leaf(p) + p = p:gsub('\\', '/') + local last, a = p, 1 + while a do + a = p:find('/', a + 1) + if a then + last = p:sub(a + 1) + end + end + return last +end + +function nativefs.getInfo(path, filtertype) + local dir = path:match("(.*[\\/]).*$") or './' + local file = leaf(path) + local result, err = withTempMount(dir, getInfo, file, filtertype) + return result or nil +end + +----------------------------------------------------------------------------- + +MODEMAP = { r = 'rb', w = 'wb', a = 'ab' } +local MAX_PATH = 4096 + +ffi.cdef([[ + int PHYSFS_mount(const char* dir, const char* mountPoint, int appendToPath); + int PHYSFS_unmount(const char* dir); + const char* PHYSFS_getMountPoint(const char* dir); + + typedef struct FILE FILE; + + FILE* fopen(const char* path, const char* mode); + size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream); + size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream); + int fclose(FILE* stream); + int fflush(FILE* stream); + size_t fseek(FILE* stream, size_t offset, int whence); + size_t ftell(FILE* stream); + int setvbuf(FILE* stream, char* buffer, int mode, size_t size); + int feof(FILE* stream); +]]) + +if ffi.os == 'Windows' then + ffi.cdef([[ + int MultiByteToWideChar(unsigned int cp, uint32_t flags, const char* mb, int cmb, const wchar_t* wc, int cwc); + int WideCharToMultiByte(unsigned int cp, uint32_t flags, const wchar_t* wc, int cwc, const char* mb, + int cmb, const char* def, int* used); + int GetLogicalDrives(void); + int CreateDirectoryW(const wchar_t* path, void*); + int _wchdir(const wchar_t* path); + wchar_t* _wgetcwd(wchar_t* buffer, int maxlen); + FILE* _wfopen(const wchar_t* path, const wchar_t* mode); + int _wunlink(const wchar_t* path); + int _wrmdir(const wchar_t* path); + ]]) + + BUFFERMODE = { full = 0, line = 64, none = 4 } + + local function towidestring(str) + local size = C.MultiByteToWideChar(65001, 0, str, #str, nil, 0) + local buf = ffi.new('wchar_t[?]', size + 1) + C.MultiByteToWideChar(65001, 0, str, #str, buf, size) + return buf + end + + local function toutf8string(wstr) + local size = C.WideCharToMultiByte(65001, 0, wstr, -1, nil, 0, nil, nil) + local buf = ffi.new('char[?]', size + 1) + C.WideCharToMultiByte(65001, 0, wstr, -1, buf, size, nil, nil) + return ffi.string(buf) + end + + local nameBuffer = ffi.new('wchar_t[?]', MAX_PATH + 1) + + fopen = function(path, mode) return C._wfopen(towidestring(path), towidestring(mode)) end + getcwd = function() return toutf8string(C._wgetcwd(nameBuffer, MAX_PATH)) end + chdir = function(path) return C._wchdir(towidestring(path)) == 0 end + unlink = function(path) return C._wunlink(towidestring(path)) == 0 end + mkdir = function(path) return C.CreateDirectoryW(towidestring(path), nil) ~= 0 end + rmdir = function(path) return C._wrmdir(towidestring(path)) == 0 end +else + BUFFERMODE = { full = 0, line = 1, none = 2 } + + ffi.cdef([[ + char* getcwd(char *buffer, int maxlen); + int chdir(const char* path); + int unlink(const char* path); + int mkdir(const char* path, int mode); + int rmdir(const char* path); + ]]) + + local nameBuffer = ByteArray(MAX_PATH) + + fopen = C.fopen + unlink = function(path) return ffi.C.unlink(path) == 0 end + chdir = function(path) return ffi.C.chdir(path) == 0 end + mkdir = function(path) return ffi.C.mkdir(path, 0x1ed) == 0 end + rmdir = function(path) return ffi.C.rmdir(path) == 0 end + + getcwd = function() + local cwd = _ptr(C.getcwd(nameBuffer, MAX_PATH)) + return cwd and ffi.string(cwd) or nil + end +end + +return nativefs \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/localization/en-us.lua b/Cartomancer-v.4.10-fix-fix/localization/en-us.lua new file mode 100644 index 0000000..848128a --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/localization/en-us.lua @@ -0,0 +1,63 @@ +return { + misc = { + dictionary = { + carto_settings_compact_deck = "Compact deck", + carto_settings_deck_view = "Deck view", + carto_settings_jokers = "Jokers", + carto_settings_flames = "Flames", + carto_settings_other = "Other", + carto_settings_keybinds = "Keybinds", + + carto_compact_deck_enabled = "Limit cards in deck pile", + carto_compact_deck_visible_cards = "Cards limit ", + + carto_deck_view_stack_enabled = "Stack cards in deck view", + carto_deck_view_hide_drawn_cards = "Hide drawn cards", + carto_deck_view_stack_modifiers = "Stack all modifiers", + carto_deck_view_stack_chips = "Stack different chip values", + -- carto_deck_view_stack_suits = "Stack all suits", -- Do not think this is necessary if steamodded adds suit pages. + carto_deck_view_stack_x_color = "Stack display color (hex) ", + carto_deck_view_stack_background_opacity = "Stack display opacity ", + carto_deck_view_stack_pos_vertical = "Vertical stack alignment ", + carto_deck_view_stack_pos_vertical_options = { + "top", + "center", + "bottom" + }, + carto_deck_view_stack_pos_horizontal = "Horizontal stack alignment ", + carto_deck_view_stack_pos_horizontal_options = { + "left", + "middle", + "right" + }, + carto_deck_view_unique_cards = "Unique cards:", + + carto_draw_non_essential_shaders = "Draw non-essential shaders", + carto_improved_hand_sorting = "Improved hand sorting", + carto_hide_tags = "Hide tags", + carto_hide_consumables = "Hide consumables", + carto_hide_deck = "Hide deck", + carto_hide_jokers = "Hide jokers", + + carto_flames_intensity_min = "Min intensity ", + carto_flames_intensity_max = "Max intensity ", + carto_flames_relative_intensity = "Relative score intensity", + carto_flames_slower_speed = "Slower flames on big scores", + carto_flames_volume = "Flames volume ", + + carto_jokers_controls_buttons = "Show joker area buttons", + carto_jokers_controls_show_after = "after total # of jokers above ", + --carto_jokers_hide_keybind = "Hide hovered joker with Alt + 1", + carto_jokers_hide = "Hide", + carto_jokers_show = "Show", + carto_jokers_zoom = "Zoom", + + carto_waiting_keybind = "Waiting for input...", + carto_kb_hide_joker = "Hide joker", + carto_kb_toggle_tags = "Toggle tags visibility", + carto_kb_toggle_consumables = "Toggle consumables visibility", + carto_kb_toggle_jokers = "Toggle jokers visibility", + carto_kb_toggle_jokers_buttons = "Toggle jokers buttons", + } + } +} \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/lovely.toml b/Cartomancer-v.4.10-fix-fix/lovely.toml new file mode 100644 index 0000000..8900d49 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely.toml @@ -0,0 +1,25 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 11 + +[[patches]] +[patches.module] +source = "libs/nativefs.lua" +before = "main.lua" +name = "cartomancer.nfs" + +[[patches]] +[patches.module] +source = "internal/init.lua" +before = "main.lua" +name = "cartomancer.init" + +# Add core functionality +[[patches]] +[patches.copy] +target = "main.lua" +position = "append" +sources = [ + "cartomancer.lua", +] diff --git a/Cartomancer-v.4.10-fix-fix/lovely/apply-before-smods.toml b/Cartomancer-v.4.10-fix-fix/lovely/apply-before-smods.toml new file mode 100644 index 0000000..9763958 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/apply-before-smods.toml @@ -0,0 +1,14 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Patches applied before steamodded. + +[[patches]] +[patches.copy] +target = "main.lua" +position = "prepend" +sources = [ + "core/view-deck-steamodded.lua", +] diff --git a/Cartomancer-v.4.10-fix-fix/lovely/dynamic-ante-display.toml b/Cartomancer-v.4.10-fix-fix/lovely/dynamic-ante-display.toml new file mode 100644 index 0000000..b2ecac9 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/dynamic-ante-display.toml @@ -0,0 +1,49 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Patch dynamic ante display into +# function create_UIBox_your_collection_blinds(exit) +# this will only work for vanilla, as steamodded overrides this + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "local ante_amounts = {}" +position = "before" +payload = ''' +local min_ante = 1 +local max_ante = 16 +local spacing = 1 - 15*0.06 +if G.GAME and G.GAME.round_resets and G.GAME.round_resets.ante then + local current_ante = G.GAME.round_resets.ante + + if current_ante > 8 then + min_ante = current_ante - 8 + 1 + max_ante = current_ante + 8 + end +end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "for i = 1, math.min(16, math.max(16, G.PROFILES[G.SETTINGS.profile].high_scores.furthest_ante.amt)) do" +position = "at" +payload = ''' +for i = min_ante, max_ante do +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "local spacing = 1 - math.min(20, math.max(15, G.PROFILES[G.SETTINGS.profile].high_scores.furthest_ante.amt))*0.06" +position = "at" +payload = ''' +-- :3 +''' +match_indent = true + diff --git a/Cartomancer-v.4.10-fix-fix/lovely/fixed-flames.toml b/Cartomancer-v.4.10-fix-fix/lovely/fixed-flames.toml new file mode 100644 index 0000000..90904f1 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/fixed-flames.toml @@ -0,0 +1,30 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + + +[[patches]] +[patches.regex] +target = "functions/button_callbacks.lua" +position = "at" +pattern = ''' +(?math\.max\(0\., math\.log\(G\.ARGS\.score_intensity\.earned_score, 5\)-2\))''' +payload = "Cartomancer.get_flames_intensity()" + +[[patches]] +[patches.regex] +target = "functions/misc_functions.lua" +position = "at" +pattern = ''' +(?\(not G\.video_organ and G\.STATE == G\.STATES\.SPLASH\) and 0 or AC\[k\]\.vol and v\.volfunc\(AC\[k\]\.vol\) or 0)''' +payload = "Cartomancer.handle_flames_volume($value)" + +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = '''_F.timer = _F.timer + G.real_dt*(1 + _F.intensity*0.2)''' +position = 'at' +payload = ''' +_F.timer = Cartomancer.handle_flames_timer(_F.timer, _F.intensity)''' +match_indent = true diff --git a/Cartomancer-v.4.10-fix-fix/lovely/hand-sorting.toml b/Cartomancer-v.4.10-fix-fix/lovely/hand-sorting.toml new file mode 100644 index 0000000..7011b30 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/hand-sorting.toml @@ -0,0 +1,30 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Add no sort button to +# create_UIBox_buttons +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = ''' +{n=G.UIT.T, config={text = localize('b_sort_hand'), scale = text_scale*0.8, colour = G.C.UI.TEXT_LIGHT}}''' +position = "before" +payload = ''' +Cartomancer.SETTINGS.improved_hand_sorting and +create_toggle{ col = true, label = localize('b_sort_hand'), label_scale = text_scale*0.8, scale = 0.30, w = 0, shadow = true, ref_table = G.hand, ref_value = 'cart_sorting', callback = function () G.FUNCS.cartomancer_sort_hand_off() end } +or''' +match_indent = true + +# Set default value of G.hand.cart_sorting +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = ''' +function create_UIBox_buttons()''' +position = "after" +payload = ''' + if G.hand and G.hand.cart_sorting == nil then G.hand.cart_sorting = true end +''' +match_indent = true diff --git a/Cartomancer-v.4.10-fix-fix/lovely/hidden-jokers.toml b/Cartomancer-v.4.10-fix-fix/lovely/hidden-jokers.toml new file mode 100644 index 0000000..dfc920e --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/hidden-jokers.toml @@ -0,0 +1,28 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "self.children.area_uibox:draw()" +position = "after" +payload = ''' +if self == G.jokers then + Cartomancer.add_visibility_controls() +end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "function CardArea:emplace(card, location, stay_flipped)" +position = "after" +payload = ''' +if self == G.jokers then + Cartomancer.handle_joker_added(card) +end +''' +match_indent = true diff --git a/Cartomancer-v.4.10-fix-fix/lovely/keybinds.toml b/Cartomancer-v.4.10-fix-fix/lovely/keybinds.toml new file mode 100644 index 0000000..f82b968 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/keybinds.toml @@ -0,0 +1,20 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + + +[[patches]] +[patches.pattern] +target = 'engine/controller.lua' +pattern = "function Controller:key_press_update(key, dt)" +position = "after" +payload = ''' + if key == "escape" and Cartomancer.INTERNAL_in_config then + Cartomancer.INTERNAL_in_config = false + if not Cartomancer.use_smods() then + Cartomancer.save_config() + end + end +''' +match_indent = true diff --git a/Cartomancer-v.4.10-fix-fix/lovely/limit-deck-size.toml b/Cartomancer-v.4.10-fix-fix/lovely/limit-deck-size.toml new file mode 100644 index 0000000..f4ef9b2 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/limit-deck-size.toml @@ -0,0 +1,86 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + +# Make all drawn cards visible + +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "local stay_flipped = G.GAME and G.GAME.blind and G.GAME.blind:stay_flipped(self, card)" +position = "before" +payload = ''' +if self == G.hand and not card.states.visible then + card.states.visible = true +end''' +match_indent = true + +# Fix drawing specific card staying invisible +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "local stay_flipped = G.GAME and G.GAME.blind and G.GAME.blind:stay_flipped(to, card)" +position = "before" +payload = ''' +if card and to == G.hand and not card.states.visible then + card.states.visible = true +end''' +match_indent = true + +# Replace drawing deck pile + +[[patches]] +[patches.regex] +target = "cardarea.lua" +pattern = ''' +(?[\t ]*)local deck_height \= \(self\.config\.deck_height or 0\.15\)\/52 +[\t ]*for k, card in ipairs\(self\.cards\) do +[\t ]* if card\.facing \=\= 'front' then card\:flip\(\) end +[\t ]* +[\t ]* if not card\.states\.drag\.is then +[\t ]* card\.T\.x \= self\.T\.x \+ 0\.5\*\(self\.T\.w \- card\.T\.w\) \+ self\.shadow_parrallax\.x\*deck_height\*\(\#self\.cards\/\(self \=\= G\.deck and 1 or 2\) \- k\) \+ 0\.9\*self\.shuffle_amt\*\(1 \- k\*0\.01\)\*\(k%2 \=\= 1 and 1 or \-0\) +[\t ]* card\.T\.y \= self\.T\.y \+ 0\.5\*\(self\.T\.h \- card\.T\.h\) \+ self\.shadow_parrallax\.y\*deck_height\*\(\#self\.cards\/\(self \=\= G\.deck and 1 or 2\) \- k\) +[\t ]* card\.T\.r \= 0 \+ 0\.3\*self\.shuffle_amt\*\(1 \+ k\*0\.05\)\*\(k%2 \=\= 1 and 1 or \-0\) +[\t ]* card\.T\.x \= card\.T\.x \+ card\.shadow_parrallax\.x\/30 +[\t ]* end +[\t ]*end''' +position = "at" +payload = ''' +local display_limit +if not Cartomancer.SETTINGS.compact_deck_enabled then + display_limit = 999999 +else + display_limit = Cartomancer.SETTINGS.compact_deck_visible_cards +end + +local deck_height = (self.config.deck_height or 0.15)/52 +local total_cards = #self.cards <= display_limit and #self.cards or display_limit -- limit height +local fixedX, fixedY, fixedR = nil, nil, nil + +for k, card in ipairs(self.cards) do + if card.facing == 'front' then card:flip() end + + if not card.states.drag.is then + if fixedX then + card.T.x = fixedX + card.T.y = fixedY + card.T.r = fixedR -- rotation + card.states.visible = false + else + card.T.x = self.T.x + 0.5*(self.T.w - card.T.w) + self.shadow_parrallax.x*deck_height*(total_cards/(self == G.deck and 1 or 2) - k) + 0.9*self.shuffle_amt*(1 - k*0.01)*(k%2 == 1 and 1 or -0) + card.T.y = self.T.y + 0.5*(self.T.h - card.T.h) + self.shadow_parrallax.y*deck_height*(total_cards/(self == G.deck and 1 or 2) - k) + card.T.r = 0 + 0.3*self.shuffle_amt*(1 + k*0.05)*(k%2 == 1 and 1 or -0) + card.T.x = card.T.x + card.shadow_parrallax.x/30 + card.states.visible = true + + if k >= display_limit then + fixedX = card.T.x + fixedY = card.T.y + fixedR = card.T.r + end + end + end +end''' +line_prepend = '$indent' + diff --git a/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-steamodded-0.9.8.toml b/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-steamodded-0.9.8.toml new file mode 100644 index 0000000..bc0b3bf --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-steamodded-0.9.8.toml @@ -0,0 +1,15 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + +# Add unique count + +[[patches]] +[patches.pattern] +target = "main.lua" +match_indent = true +pattern = "modded and {" +position = "before" +payload = ''' +not unplayed_only and Cartomancer.add_unique_count() or nil, -- Cartomancer Steamodded 0.9.8 compatibility''' diff --git a/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-steamodded.toml b/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-steamodded.toml new file mode 100644 index 0000000..bd0eac2 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-steamodded.toml @@ -0,0 +1,31 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + +# Initialize upvalue from file above, and override NFS.read now. +[[patches]] +[patches.pattern] +target = "main.lua" +match_indent = true +pattern = "SMODS.path = find_self(SMODS.MODS_DIR, 'core.lua', '--- STEAMODDED CORE')" +position = "after" +payload = ''' + +Cartomancer_nfs_read = NFS.read +NFS.read = Cartomancer_nfs_read_override + +''' + +# todo : use lovely for view deck patches + +#[[patches]] +#[patches.pattern] +#target = "Steamodded - src/overrides.lua" +#match_indent = true +#pattern = "--- STEAMODDED CORE" +#position = "after" +#payload = ''' +#-- YO WHADDUP +#print"------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------" +#''' \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-vanilla.toml b/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-vanilla.toml new file mode 100644 index 0000000..855bb27 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/stackable-deck-vanilla.toml @@ -0,0 +1,118 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + +# +# Vanilla patches +# + +# Overwrite how suits are added + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "table.insert(SUITS[v.base.suit], v)" +position = "at" +payload = ''' +local greyed +if unplayed_only and not ((v.area and v.area == G.deck) or v.ability.wheel_flipped) then + greyed = true +end +local card_string = v:cart_to_string() +if greyed then + card_string = card_string .. "Greyed" +end +if greyed and Cartomancer.SETTINGS.deck_view_hide_drawn_cards then + -- Ignore this card. +elseif not SUITS[v.base.suit][card_string] then + table.insert(SUITS_SORTED[v.base.suit], card_string) + + local _scale = 0.7 + local copy = copy_card(v, nil, _scale) + + copy.greyed = greyed + copy.stacked_quantity = 1 + + SUITS[v.base.suit][card_string] = copy + else + local stacked_card = SUITS[v.base.suit][card_string] + stacked_card.stacked_quantity = stacked_card.stacked_quantity + 1 + end''' +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = ''' +if SUITS\[suit_map\[j\]\]\[1\] then +[\t ]*local view_deck = CardArea\(''' +position = "at" +payload = ''' +if SUITS_SORTED[suit_map[j]][1] then + local view_deck = CardArea(''' +line_prepend = '' + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "local suit_map = {'Spades', 'Hearts', 'Clubs', 'Diamonds'}" +position = "after" +payload = "local SUITS_SORTED = Cartomancer.tablecopy(SUITS)" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "{card_limit = #SUITS[suit_map[j]], type = 'title', view_deck = true, highlight_limit = 0, card_w = G.CARD_W*0.7, draw_layers = {'card'}})" +position = "at" +payload = "{card_limit = #SUITS_SORTED[suit_map[j]], type = 'title', view_deck = true, highlight_limit = 0, card_w = G.CARD_W*0.7, draw_layers = {'card'}})" +match_indent = true + +# Add unique count + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''modded and {n=G.UIT.R, config={align = "cm"}, nodes={''' +position = "before" +payload = "not unplayed_only and Cartomancer.add_unique_count() or nil," +match_indent = true + +# Overwrite cards copy and display code + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = ''' +(?[\t ]*)for i = 1\, \#SUITS\[suit_map\[j\]\] do +[\t ]* if SUITS\[suit_map\[j\]\]\[i\] then +[\t ]* local greyed\, _scale = nil\, 0\.7 +[\t ]* if unplayed_only and not \(\(SUITS\[suit_map\[j\]\]\[i\]\.area and SUITS\[suit_map\[j\]\]\[i\]\.area == G\.deck\) or SUITS\[suit_map\[j\]\]\[i\]\.ability\.wheel_flipped\) then +[\t ]* greyed = true +[\t ]* end +[\t ]* local copy = copy_card\(SUITS\[suit_map\[j\]\]\[i\]\,nil\, _scale\) +[\t ]* copy\.greyed = greyed +[\t ]* copy\.T\.x = view_deck\.T\.x \+ view_deck\.T\.w\/2 +[\t ]* copy\.T\.y = view_deck\.T\.y +[\t ]* +[\t ]* copy:hard_set_T\(\) +[\t ]* view_deck:emplace\(copy\) +[\t ]* end +[\t ]*end''' +position = "at" +payload = ''' +for i = 1, #SUITS_SORTED[suit_map[j]] do + local card_string = SUITS_SORTED[suit_map[j]][i] + local card = SUITS[suit_map[j]][card_string] + + card.T.x = view_deck.T.x + view_deck.T.w/2 + card.T.y = view_deck.T.y + card:create_quantity_display() + + card:hard_set_T() + view_deck:emplace(card) + +end +''' +line_prepend = '$indent' \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/lovely/tags.toml b/Cartomancer-v.4.10-fix-fix/lovely/tags.toml new file mode 100644 index 0000000..13a8f7e --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/tags.toml @@ -0,0 +1,14 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + + +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = '''G.FUNCS.blind_chip_UI_scale(G.hand_text_area.blind_chips)''' +position = 'before' +payload = ''' +Cartomancer.update_tags_visibility()''' +match_indent = true diff --git a/Cartomancer-v.4.10-fix-fix/lovely/vanilla-ui.toml b/Cartomancer-v.4.10-fix-fix/lovely/vanilla-ui.toml new file mode 100644 index 0000000..7aad247 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/vanilla-ui.toml @@ -0,0 +1,90 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + + +# Localization +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "boot_timer('prep stage', 'splash prep',1)" +position = "before" +payload = "Cartomancer.load_mod_file('internal/localization.lua', 'localization')" +match_indent = true + +# Add tab button to settings menu +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +match_indent = true +position = "before" +pattern = '''local t = create_UIBox_generic_options({back_func = 'options',contents = {create_tabs(''' +payload = ''' +local settings_icon = Cartomancer.add_settings_icon() +if settings_icon then + tabs[#tabs+1] = { + colour = G.C.MONEY, + custom_button = {settings_icon}, + tab_definition_function = Cartomancer.config_tab, + tab_definition_function_args = '' + } +end +''' + +# Use custom button +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +match_indent = true +position = "at" +pattern = '''but_UI_label''' +payload = ''' +args.ref_table and args.ref_table.custom_button or but_UI_label -- Cartomancer''' + +# Use custom color +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +match_indent = true +position = "at" +pattern = '''colour = args.colour,''' +payload = ''' +colour = args.ref_table and args.ref_table.colour or args.colour, -- Cartomancer''' + + +# From steamodded for vertical tabs support. +# Fix UIElement.config.chosen being overriden if choice=true is set +# UIElement:click() +[[patches]] +[patches.pattern] +target = "engine/ui.lua" +match_indent = true +position = "after" +pattern = "if self.config.choice then" +payload = " local chosen_temp = self.config.chosen" + +[[patches]] +[patches.pattern] +target = "engine/ui.lua" +match_indent = true +position = "at" +pattern = "self.config.chosen = true" +payload = "self.config.chosen = chosen_temp or true" + +# Add dynamic label support to UIBox_button +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +match_indent = true +position = "before" +pattern = "for k, v in ipairs(args.label) do" +payload = ''' +if args.dynamic_label then + but_UI_label = {} + + table.insert(but_UI_label, {n=G.UIT.R, config={align = "cm", padding = 0, minw = args.minw, maxw = args.maxw}, nodes={ + {n=G.UIT.T, config={ref_table = args.dynamic_label, ref_value = 'text', scale = args.scale, colour = args.text_colour, shadow = args.shadow, focus_args = button_pip and args.focus_args or nil, func = button_pip,}} + }}) +end +''' \ No newline at end of file diff --git a/Cartomancer-v.4.10-fix-fix/lovely/zoom-jokers.toml b/Cartomancer-v.4.10-fix-fix/lovely/zoom-jokers.toml new file mode 100644 index 0000000..dd92f4a --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/lovely/zoom-jokers.toml @@ -0,0 +1,58 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 69 + +# Replace joker sorting. Needs better mod compat +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "if self.config.type == 'joker' or self.config.type == 'title_2' then" +position = "at" +payload = ''' +if self == G.jokers and G.jokers.cart_jokers_expanded then + local align_cards = Cartomancer.expand_G_jokers() + + -- This should work fine without cryptid. But because cryptid's patch is priority=0, it has to be this way + if not G.GAME.modifiers.cry_conveyor then + table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end) + end + + if align_cards then + G.jokers:hard_set_cards() + end +elseif self.config.type == 'joker' or self.config.type == 'title_2' then''' +match_indent = true + +# Hide all cards that are off-screen +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if not self.states.visible then return end" +position = "after" +payload = ''' +if self.VT.x < -3 or self.VT.x > G.TILE_W + 2.5 then return end''' +match_indent = true + +# Add slider ID +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''{n=G.UIT.B, config={w=startval,h=args.h, r = 0.1, colour = args.colour, ref_table = args, refresh_movement = true}},''' +position = "at" +payload = ''' +{n=G.UIT.B, config={id = args.id, w=startval,h=args.h, r = 0.1, colour = args.colour, ref_table = args, refresh_movement = true}}, +''' +match_indent = true + +# Hide slider value +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = ''' +(?\{n=G\.UIT\.C\, config=\{align = "cm"\, minh = args\.h\,r = 0\.1\, minw = 0\.8\, colour = args\.colour\,shadow = true\}\, nodes=\{ +[\t ]*\{n=G\.UIT\.T\, config=\{ref_table = args\, ref_value = 'text'\, scale = args\.text_scale\, colour = G\.C\.UI\.TEXT_LIGHT\, decimal_places = args\.decimal_places\}\} +[\t ]*\}\})\,''' +position = "at" +payload = ''' +not args.hide_val and $content or nil''' diff --git a/Cartomancer-v.4.10-fix-fix/mod.lua b/Cartomancer-v.4.10-fix-fix/mod.lua new file mode 100644 index 0000000..57f2351 --- /dev/null +++ b/Cartomancer-v.4.10-fix-fix/mod.lua @@ -0,0 +1,22 @@ +--- STEAMODDED HEADER +--- MOD_NAME: Cartomancer +--- MOD_ID: cartomancer +--- MOD_AUTHOR: [stupxd aka stupid] +--- MOD_DESCRIPTION: Quality of life features and optimizations +--- PRIORITY: 69 +--- BADGE_COLOR: FFD700 +--- DISPLAY_NAME: Cartomancer +--- VERSION: 4.10 + +---------------------------------------------- +------------MOD CODE ------------------------- + +if not SMODS.current_mod then + return +end + +SMODS.current_mod.config_tab = Cartomancer.config_tab +SMODS.current_mod.save_mod_config = Cartomancer.save_config + +---------------------------------------------- +------------MOD CODE END---------------------- diff --git a/Cryptid/Cryptid.lua b/Cryptid/Cryptid.lua new file mode 100644 index 0000000..9f7f8ac --- /dev/null +++ b/Cryptid/Cryptid.lua @@ -0,0 +1,3360 @@ +--- STEAMODDED HEADER +--- MOD_NAME: Cryptid +--- MOD_ID: Cryptid +--- PREFIX: cry +--- MOD_AUTHOR: [MathIsFun_, Cryptid and Balatro Discords] +--- MOD_DESCRIPTION: Adds unbalanced ideas to Balatro. +--- BADGE_COLOUR: 708b91 +--- DEPENDENCIES: [Talisman>=2.0.0-beta8, Steamodded>=1.0.0~ALPHA-1216c] +--- VERSION: 0.5.3a +--- PRIORITY: 99999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 + +---------------------------------------------- +------------MOD CODE ------------------------- + +-- Currently there's no rhyme or reason to how the contents of this file are organized. It's kind of just an "anything goes" sort of file. +-- If you're learning about Cryptid's codebase, the files in the Items folder are generally much more organized. + +-- Enables debug features (I think this is currently useless.) +--Cryptid.debug = true + +-- Save the mod path permanently. +local mod_path = "" .. SMODS.current_mod.path +-- Load Options +Cryptid_config = SMODS.current_mod.config +-- This will save the current state even when settings are modified +Cryptid.enabled = copy_table(Cryptid_config) +--backwards compat moment +cry_enable_jokers = Cryptid.enabled["Misc. Jokers"] +cry_enable_epics = Cryptid.enabled["Epic Jokers"] +cry_enable_exotics = Cryptid.enabled["Exotic Jokers"] +cry_minvasion = Cryptid.enabled["M Jokers"] + +-- Gradient isn't included since other logic seems to also handle it +SMODS.Rarity{ + key = "exotic", + loc_txt = {}, + badge_colour = HEX('708b91'), +} + +SMODS.Rarity{ + key = "epic", + loc_txt = {}, + badge_colour = HEX('571d91'), + default_weight = 0.003, + pools = {["Joker"] = true}, + get_weight = function(self, weight, object_type) + -- The game shouldn't try generating Epic Jokers when they are disabled + if Cryptid_config["Epic Jokers"] then + return 0.003 + else + return 0 + end + end, +} + +SMODS.Rarity{ + key = "candy", + loc_txt = {}, + badge_colour = HEX("e91ff0"), +} + +SMODS.Rarity{ + key = "cursed", + loc_txt = {}, + badge_colour = HEX("474931"), +} + +--Add Event type - used for events in e.g. Chocolate Dice +SMODS.Events = {} +SMODS.Event = SMODS.GameObject:extend{ + obj_table = SMODS.Events, + obj_buffer = {}, + required_params = { + "key" + }, + inject = function() end, + set = "Event", + class_prefix = "ev", + -- This should be called to start an event. + start = function(self) + G.GAME.events[self.key] = true + end, + -- This should be called to finish an event. + finish = function(self) + G.GAME.events[self.key] = nil + end, + -- Runs once before and after jokers, as well as a few special cases + calculate = function(self, context) + end, + -- used for Chocolate Die tooltips, can maybe be repurposed later + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { set = "Other", key = self.key } + end, +} +--Calculate events on cash out +local gfco = G.FUNCS.cash_out +G.FUNCS.cash_out = function(e) + local ret = gfco(e) + for k, v in pairs(SMODS.Events) do + if G.GAME.events[k] then + v:calculate({cash_out = true}) + end + end + return ret +end +-- Calculate events on start of shop +local guis = G.UIDEF.shop +G.UIDEF.shop = function(e) + local ret = guis(e) + for k, v in pairs(SMODS.Events) do + if G.GAME.events[k] then + v:calculate({start_shop = true}) + end + end + return ret +end +-- Calculations for Please Take One. Incredibly scuffed and should get moved to Spooky file later +local gure = Game.update_round_eval +function Game:update_round_eval(dt) + if G.GAME.events.ev_cry_choco6 and not pack_opened and not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + for k, v in pairs(SMODS.Events) do + if G.GAME.events[k] then + v:calculate({pre_cash = true}) + end + end + return end + if G.GAME.events.ev_cry_choco6 and pack_opened and G.STATE_COMPLETE and not G.round_eval then G.STATE_COMPLETE = false; return end + gure(self, dt) +end +--Add Unique consumable set - used for unique consumables that aren't normally obtained (e.g. Potion) +SMODS.ConsumableType{ + key = "Unique", + primary_colour = G.C.MONEY, + secondary_colour = G.C.MONEY, + collection_rows = { 4, 4 }, + shop_rate = 0.0, + loc_txt = {}, + default = "c_cry_potion", + can_stack = false, + can_divide = false, +} +-- Create G.GAME.events when starting a run, so there's no errors +local gigo = Game.init_game_object +function Game:init_game_object() + local g = gigo(self) + g.events = {} + return g +end + +--Changes main menu colors and stuff +if Cryptid.enabled["Menu"] then + local oldfunc = Game.main_menu + Game.main_menu = function(change_context) + local ret = oldfunc(change_context) + -- adds a Cryptid spectral to the main menu + local newcard = create_card('Spectral',G.title_top, nil, nil, nil, nil, 'c_cryptid', 'elial1') + -- recenter the title + G.title_top.T.w = G.title_top.T.w*1.7675 + G.title_top.T.x = G.title_top.T.x - 0.8 + G.title_top:emplace(newcard) + -- make the card look the same way as the title screen Ace of Spades + newcard.T.w = newcard.T.w * 1.1*1.2 + newcard.T.h = newcard.T.h *1.1*1.2 + newcard.no_ui = true + + -- make the title screen use different background colors + G.SPLASH_BACK:define_draw_steps({{ + shader = 'splash', + send = { + {name = 'time', ref_table = G.TIMERS, ref_value = 'REAL_SHADER'}, + {name = 'vort_speed', val = 0.4}, + {name = 'colour_1', ref_table = G.C, ref_value = 'CRY_EXOTIC'}, + {name = 'colour_2', ref_table = G.C, ref_value = 'DARK_EDITION'}, + }}}) + return ret + end +end + +--Localization colors +local lc = loc_colour +function loc_colour(_c, _default) + if not G.ARGS.LOC_COLOURS then + lc() + end + G.ARGS.LOC_COLOURS.cry_code = G.C.SET.Code + G.ARGS.LOC_COLOURS.heart = G.C.SUITS.Hearts + G.ARGS.LOC_COLOURS.diamond = G.C.SUITS.Diamonds + G.ARGS.LOC_COLOURS.spade = G.C.SUITS.Spades + G.ARGS.LOC_COLOURS.club = G.C.SUITS.Clubs + for k, v in pairs(G.C) do + if string.len(k) > 4 and string.sub(k, 1, 4) == 'CRY_' then + G.ARGS.LOC_COLOURS[string.lower(k)] = v + end + end + return lc(_c, _default) +end + +-- Midground sprites - used for Exotic Jokers and Gateway +-- don't really feel like explaining this deeply, it's based on code for The Soul and Legendary Jokers +local set_spritesref = Card.set_sprites +function Card:set_sprites(_center, _front) + set_spritesref(self, _center, _front) + if _center and _center.name == "cry-Gateway" then + self.children.floating_sprite = Sprite( + self.T.x, + self.T.y, + self.T.w, + self.T.h, + G.ASSET_ATLAS[_center.atlas or _center.set], + { x = 2, y = 0 } + ) + self.children.floating_sprite.role.draw_major = self + self.children.floating_sprite.states.hover.can = false + self.children.floating_sprite.states.click.can = false + self.children.floating_sprite2 = Sprite( + self.T.x, + self.T.y, + self.T.w, + self.T.h, + G.ASSET_ATLAS[_center.atlas or _center.set], + { x = 1, y = 0 } + ) + self.children.floating_sprite2.role.draw_major = self + self.children.floating_sprite2.states.hover.can = false + self.children.floating_sprite2.states.click.can = false + end + if _center and _center.soul_pos and _center.soul_pos.extra then + self.children.floating_sprite2 = Sprite( + self.T.x, + self.T.y, + self.T.w, + self.T.h, + G.ASSET_ATLAS[_center.atlas or _center.set], + _center.soul_pos.extra + ) + self.children.floating_sprite2.role.draw_major = self + self.children.floating_sprite2.states.hover.can = false + self.children.floating_sprite2.states.click.can = false + end +end +--this is where the code starts to get really scuffed... I'd recommend closing your eyes +--anyway this function basically hardcodes unredeeming a voucher +function cry_debuff_voucher(center) -- sorry for all the mess here... + local new_center = G.GAME.cry_voucher_centers[center] + local center_table = { + name = new_center and new_center.name, + extra = new_center and new_center.config.extra, + } + if center_table.name == "Overstock" or center_table.name == "Overstock Plus" then + G.E_MANAGER:add_event(Event({ + func = function() + change_shop_size(-center_table.extra) + return true + end, + })) + end + if center_table.name == "Tarot Merchant" or center_table.name == "Tarot Tycoon" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.tarot_rate = G.GAME.tarot_rate / center_table.extra + return true + end, + })) + end + if center_table.name == "Planet Merchant" or center_table.name == "Planet Tycoon" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.planet_rate = G.GAME.planet_rate / center_table.extra + return true + end, + })) + end + if center_table.name == "Hone" or center_table.name == "Glow Up" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.edition_rate = G.GAME.edition_rate / center_table.extra + return true + end, + })) + end + if center_table.name == "Magic Trick" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.playing_card_rate = 0 + return true + end, + })) + end + if center_table.name == "Crystal Ball" then + G.E_MANAGER:add_event(Event({ + func = function() + G.consumeables.config.card_limit = G.consumeables.config.card_limit - center_table.extra + return true + end, + })) + end + if center_table.name == "Clearance Sale" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.discount_percent = 0 + for k, v in pairs(G.I.CARD) do + if v.set_cost then + v:set_cost() + end + end + return true + end, + })) + end + if center_table.name == "Liquidation" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.discount_percent = 25 + for k, v in pairs(G.I.CARD) do + if v.set_cost then + v:set_cost() + end + end + return true + end, + })) + end + if center_table.name == "Reroll Surplus" or center_table.name == "Reroll Glut" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost + center_table.extra + G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost + center_table.extra) + return true + end, + })) + end + if center_table.name == "Seed Money" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.interest_cap = 25 --note: does not account for potential deck effects + return true + end, + })) + end + if center_table.name == "Money Tree" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.interest_cap = G.P_CENTERS.v_seed_money.extra + return true + end, + })) + end + if center_table.name == "Grabber" or center_table.name == "Nacho Tong" then + G.GAME.round_resets.hands = G.GAME.round_resets.hands - center_table.extra + ease_hands_played(-center_table.extra) + end + if center_table.name == "Paint Brush" or center_table.name == "Palette" then + G.hand:change_size(-center_table.extra) + end + if center_table.name == "Wasteful" or center_table.name == "Recyclomancy" then + G.GAME.round_resets.discards = G.GAME.round_resets.discards - center_table.extra + ease_discard(-center_table.extra) + end + if center_table.name == "Antimatter" then + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + G.jokers.config.card_limit = G.jokers.config.card_limit - center_table.extra + end + return true + end, + })) + end + if center_table.name == "Hieroglyph" or center_table.name == "Petroglyph" then + ease_ante(center_table.extra) + G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante or G.GAME.round_resets.ante + G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante + center_table.extra + + if center_table.name == "Hieroglyph" then + G.GAME.round_resets.hands = G.GAME.round_resets.hands + center_table.extra + ease_hands_played(center_table.extra) + end + if center_table.name == "Petroglyph" then + G.GAME.round_resets.discards = G.GAME.round_resets.discards + center_table.extra + ease_discard(center_table.extra) + end + end +end + +function cry_edition_to_table(edition) -- look mom i figured it out (this does NOT need to be a function) + if edition then + return { [edition] = true } + end +end + +-- check if Director's Cut or Retcon offers a cheaper reroll price +function cry_cheapest_boss_reroll() + local dcut = G.GAME.cry_voucher_centers["v_directors_cut"].config.extra or 1e308 + local retc = G.GAME.cry_voucher_centers["v_retcon"].config.extra or 1e308 + if dcut < retc then + return dcut + else + return retc + end +end + +-- generate a random edition (e.g. Antimatter Deck) +function cry_poll_random_edition() + local random_edition = pseudorandom_element(G.P_CENTER_POOLS.Edition, pseudoseed("cry_ant_edition")) + while random_edition.key == "e_base" do + random_edition = pseudorandom_element(G.P_CENTER_POOLS.Edition, pseudoseed("cry_ant_edition")) + end + ed_table = { [random_edition.key:sub(3)] = true } + return ed_table +end + +function cry_voucher_debuffed(name) -- simple function but idk + if G.GAME.voucher_sticker_index and G.GAME.voucher_sticker_index.perishable[name] then + if G.GAME.voucher_sticker_index.perishable[name] == 0 then + return true + end + end + return false +end + +function cry_voucher_pinned(name) + if G.GAME.voucher_sticker_index then + if G.GAME.voucher_sticker_index.pinned[name] then + return true + end + end + return false +end + +-- gets a random, valid consumeable (used for Hammerspace, CCD Deck, Blessing, etc.) +function get_random_consumable(seed, excluded_flags, banned_card, pool, no_undiscovered) + -- set up excluded flags - these are the kinds of consumables we DON'T want to have generating + excluded_flags = excluded_flags or { "hidden", "no_doe", "no_grc" } + local selection = "n/a" + local passes = 0 + local tries = 500 + while true do + tries = tries - 1 + passes = 0 + -- create a random consumable naively + local key = pseudorandom_element(pool or G.P_CENTER_POOLS.Consumeables, pseudoseed(seed or "grc")).key + selection = G.P_CENTERS[key] + -- check if it is valid + if selection.discovered or not no_undiscovered then + for k, v in pairs(excluded_flags) do + if not center_no(selection, v, key, true) then + --Makes the consumable invalid if it's a specific card unless it's set to + --I use this so cards don't create copies of themselves (eg potential inf Blessing chain, Hammerspace from Hammerspace...) + if not banned_card or (banned_card and banned_card ~= key) then + passes = passes + 1 + end + end + end + end + -- use it if it's valid or we've run out of attempts + if passes >= #excluded_flags or tries <= 0 then + if tries <= 0 and no_undiscovered then + return G.P_CENTERS["c_strength"] + else + return selection + end + end + end +end + +function cry_get_next_voucher_edition() -- currently only for editions + sticker decks, can be modified if voucher stickering/editioning becomes more important + if G.GAME.modifiers.cry_force_edition then + return cry_edition_to_table(G.GAME.modifiers.cry_force_edition) + elseif G.GAME.modifiers.cry_force_random_edition then + return cry_poll_random_edition() + end +end +-- code to generate Stickers for Vouchers, based on that for Jokers +function cry_get_next_voucher_stickers() + local eternal_perishable_poll = pseudorandom("cry_vet" .. (key_append or "") .. G.GAME.round_resets.ante) + local ret = { eternal = false, perishable = false, rental = false, pinned = false, banana = false } + if + (G.GAME.modifiers.cry_force_sticker == "eternal") + or G.GAME.modifiers.cry_sticker_sheet_plus + or ( + G.GAME.modifiers.cry_any_stickers + and (G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.8) + ) + then + ret.eternal = true + end + if G.GAME.modifiers.enable_perishables_in_shop and G.GAME.modifiers.cry_any_stickers then -- bloated as shit + if + not G.GAME.modifiers.cry_eternal_perishable_compat + and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7)) + then + ret.perishable = true + end + if + G.GAME.modifiers.cry_eternal_perishable_compat + and pseudorandom("cry_vper" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7 + then + ret.perishable = true + end + end + if (G.GAME.modifiers.cry_force_sticker == "perishable") or G.GAME.modifiers.cry_sticker_sheet_plus then + ret.perishable = true + end + if + G.GAME.modifiers.cry_force_sticker == "rental" + or G.GAME.modifiers.cry_sticker_sheet_plus + or ( + G.GAME.modifiers.cry_any_stickers + and ( + G.GAME.modifiers.enable_rentals_in_shop + and pseudorandom("cry_vssjr" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7 + ) + ) + then + ret.rental = true + end + if + G.GAME.modifiers.cry_force_sticker == "pinned" + or G.GAME.modifiers.cry_sticker_sheet_plus + or ( + G.GAME.modifiers.cry_any_stickers + and ( + G.GAME.modifiers.cry_enable_pinned_in_shop + and pseudorandom("cry_vpin" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7 + ) + ) + then + ret.pinned = true + end + if G.GAME.modifiers.cry_force_sticker == "banana" or G.GAME.modifiers.cry_sticker_sheet_plus then + ret.banana = true + end + if + not G.GAME.modifiers.cry_eternal_perishable_compat + and G.GAME.modifiers.enable_banana + and G.GAME.modifiers.cry_any_stickers + and (pseudorandom("cry_bpbanana" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7) + and (eternal_perishable_poll <= 0.7) + then + ret.banana = true + end + if + G.GAME.modifiers.cry_eternal_perishable_compat + and G.GAME.modifiers.enable_banana + and G.GAME.modifiers.cry_any_stickers + and (pseudorandom("cry_bpbanana" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7) + then + ret.banana = true + end + return ret +end + +-- Calculates Rental sticker for Consumables +function Card:cry_calculate_consumeable_rental() + if self.ability.rental then + ease_dollars(-G.GAME.cry_consumeable_rental_rate) + card_eval_status_text(self, "dollars", -G.GAME.cry_consumeable_rental_rate) + end +end + +-- Calculates Perishable sticker for Consumables +function Card:cry_calculate_consumeable_perishable() + if not self.ability.perish_tally then + self.ability.perish_tally = 1 + end + if self.ability.perishable and self.ability.perish_tally > 0 then + self.ability.perish_tally = 0 + card_eval_status_text( + self, + "extra", + nil, + nil, + nil, + { message = localize("k_disabled_ex"), colour = G.C.FILTER, delay = 0.45 } + ) + self:set_debuff() + end +end + +-- Update the Cryptid member count using HTTPS +function update_cry_member_count() + if Cryptid.enabled["HTTPS Module"] == true then + if not GLOBAL_cry_member_update_thread then + -- start up the HTTPS thread if needed + local file_data = assert(NFS.newFileData(mod_path .. "https/thread.lua")) + GLOBAL_cry_member_update_thread = love.thread.newThread(file_data) + GLOBAL_cry_member_update_thread:start() + end + local old = GLOBAL_cry_member_count or 5624 + -- get the HTTPS thread's value for Cryptid members + local ret = love.thread.getChannel("member_count"):pop() + if ret then + GLOBAL_cry_member_count = string.match(ret, '"approximate_member_count"%s*:%s*(%d+)') -- string matching a json is odd but should be fine? + end + if not GLOBAL_cry_member_count then + GLOBAL_cry_member_count = old + -- Something failed, print the error + local error = love.thread.getChannel("member_error"):pop() + if error then + sendDebugMessage(error) + end + end + else + -- Use a fallback value if HTTPS is disabled (you all are awesome) + GLOBAL_cry_member_count = 5624 + end +end +-- deal with Rigged and Fragile when scoring a playing card +local ec = eval_card +function eval_card(card, context) + if not card or card.will_shatter then + return + end + -- Store old probability for later reference + local ggpn = G.GAME.probabilities.normal + if card.ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local ret = ec(card, context) + if card.ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + return ret +end +-- deal wirh Rigged on Consumables +local uc = Card.use_consumeable +function Card:use_consumeable(area, copier) + local ggpn = G.GAME.probabilities.normal + if self.ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local ret = uc(self, area, copier) + if self.ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + return ret +end + +--some functions to minimize the load on calculate_joker itself +function Card:cry_copy_ability() + local orig_ability = {} + if self.ability then + for i, j in pairs(self.ability) do + if (type(j) == "table") and is_number(j) then + orig_ability[i] = to_big(j) + elseif type(j) == "table" then + orig_ability[i] = {} + for i2, j2 in pairs(j) do + orig_ability[i][i2] = j2 + end + else + orig_ability[i] = j + end + end + end + return orig_ability +end +local cj = Card.calculate_joker + +function Card:cry_double_scale_calc(orig_ability, in_context_scaling) + if + self.ability.name ~= "cry-happyhouse" + and self.ability.name ~= "Acrobat" + and self.ability.name ~= "cry-sapling" + and self.ability.name ~= "cry-mstack" + and self.ability.name ~= "cry-notebook" + and self.ability.name ~= "Invisible Joker" + and self.ability.name ~= "cry-Old Invisible Joker" + then + local jkr = self + if jkr.ability and type(jkr.ability) == "table" then + if not G.GAME.cry_double_scale[jkr.sort_id] or not G.GAME.cry_double_scale[jkr.sort_id].ability then + if not G.GAME.cry_double_scale[jkr.sort_id] then + G.GAME.cry_double_scale[jkr.sort_id] = { ability = { double_scale = true } } + end + for k, v in pairs(jkr.ability) do + if type(jkr.ability[k]) ~= "table" then + G.GAME.cry_double_scale[jkr.sort_id].ability[k] = v + else + G.GAME.cry_double_scale[jkr.sort_id].ability[k] = {} + for _k, _v in pairs(jkr.ability[k]) do + G.GAME.cry_double_scale[jkr.sort_id].ability[k][_k] = _v + end + end + end + end + if G.GAME.cry_double_scale[jkr.sort_id] and not G.GAME.cry_double_scale[jkr.sort_id].scaler then + local dbl_info = G.GAME.cry_double_scale[jkr.sort_id] + if jkr.ability.name == "cry-Number Blocks" then + dbl_info.base = { "extra", "money" } + dbl_info.scaler = { "extra", "money_mod" } + dbl_info.scaler_base = jkr.ability.extra.money_mod + dbl_info.offset = 1 + end + if jkr.ability.name == "cry-Exponentia" then + dbl_info.base = { "extra", "Emult" } + dbl_info.scaler = { "extra", "Emult_mod" } + dbl_info.scaler_base = jkr.ability.extra.Emult_mod + dbl_info.offset = 1 + end + if jkr.ability.name == "cry-Redeo" then + dbl_info.base = { "extra", "money_req" } + dbl_info.scaler = { "extra", "money_mod" } + dbl_info.scaler_base = jkr.ability.extra.money_mod + dbl_info.offset = 1 + end + if jkr.ability.name == "cry-Chili Pepper" then + dbl_info.base = { "extra", "Xmult" } + dbl_info.scaler = { "extra", "Xmult_mod" } + dbl_info.scaler_base = jkr.ability.extra.Xmult_mod + dbl_info.offset = 1 + end + if jkr.ability.name == "cry-Scalae" then + dbl_info.base = { "extra", "shadow_scale" } + dbl_info.scaler = { "extra", "shadow_scale_mod" } + dbl_info.scaler_base = jkr.ability.extra.scale_mod + dbl_info.offset = 1 + end + if jkr.ability.name == "cry-mprime" then + dbl_info.base = { "extra", "mult" } + dbl_info.scaler = { "extra", "bonus" } + dbl_info.scaler_base = jkr.ability.extra.bonus + dbl_info.offset = 1 + end + if jkr.ability.name == "Yorick" then + dbl_info.base = { "x_mult" } + dbl_info.scaler = { "extra", "xmult" } --not kidding + dbl_info.scaler_base = 1 + dbl_info.offset = 1 + end + if jkr.ability.name == "Hologram" then + dbl_info.base = { "x_mult" } + dbl_info.scaler = { "extra" } + dbl_info.scaler_base = jkr.ability.extra + dbl_info.offset = 1 + end + if jkr.ability.name == "Gift Card" then + dbl_info.base = { "extra_value" } + dbl_info.scaler = { "extra" } + dbl_info.scaler_base = jkr.ability.extra + dbl_info.offset = 1 + end + if jkr.ability.name == "Throwback" then + dbl_info.base = { "x_mult" } + dbl_info.scaler = { "extra" } + dbl_info.scaler_base = jkr.ability.x_mult or 1 + dbl_info.offset = 1 + end + if jkr.ability.name == "Egg" then + dbl_info.base = { "extra_value" } + dbl_info.scaler = { "extra" } + dbl_info.scaler_base = jkr.ability.extra + dbl_info.offset = 1 + end + local default_modifiers = { + mult = 0, + h_mult = 0, + h_x_mult = 0, + h_dollars = 0, + p_dollars = 0, + t_mult = 0, + t_chips = 0, + x_mult = 1, + h_size = 0, + d_size = 0, + } + for k, v in pairs(jkr.ability) do + --extra_value is ignored because it can be scaled by Gift Card + if + k ~= "extra_value" + and dbl_info.ability[k] ~= v + and is_number(v) + and is_number(dbl_info.ability[k]) + then + dbl_info.base = { k } + local predicted_mod = math.abs(to_number(to_big(v)) - to_number(to_big(dbl_info.ability[k]))) + local best_key = { "" } + local best_coeff = 10 ^ 100 + for l, u in pairs(jkr.ability) do + if not (default_modifiers[l] and default_modifiers[l] == u) then + if l ~= k and is_number(u) then + if + to_number(to_big(predicted_mod / u)) >= 0.999 + and to_number(to_big(predicted_mod / u)) < to_number(to_big(best_coeff)) + then + best_coeff = to_number(to_big(predicted_mod / u)) + best_key = { l } + end + end + if type(jkr.ability[l]) == "table" then + for _l, _u in pairs(jkr.ability[l]) do + if + is_number(_u) + and to_number(to_big(predicted_mod / _u)) >= 0.999 + and to_number(to_big(predicted_mod / _u)) + < to_number(to_big(best_coeff)) + then + best_coeff = to_number(to_big(predicted_mod / _u)) + best_key = { l, _l } + end + end + end + end + end + dbl_info.scaler = best_key + end + if + type(jkr.ability[k]) == "table" + and type(dbl_info.ability) == "table" + and type(dbl_info.ability[k]) == "table" + then + for _k, _v in pairs(jkr.ability[k]) do + if + dbl_info.ability[k][_k] ~= _v + and is_number(_v) + and is_number(dbl_info.ability[k][_k]) + then + dbl_info.base = { k, _k } + local predicted_mod = math.abs(_v - dbl_info.ability[k][_k]) + local best_key = { "" } + local best_coeff = 10 ^ 100 + for l, u in pairs(jkr.ability) do + if is_number(u) and to_number(to_big(predicted_mod / u)) >= 0.999 then + if to_number(to_big(predicted_mod / u)) < to_number(to_big(best_coeff)) then + best_coeff = to_number(to_big(predicted_mod / u)) + best_key = { l } + end + end + if type(jkr.ability[l]) == "table" then + for _l, _u in pairs(jkr.ability[l]) do + if + (l ~= k or _l ~= _k) + and is_number(_u) + and to_number(to_big(predicted_mod / _u)) >= 0.999 + then + if + to_number(to_big(predicted_mod / _u)) + < to_number(to_big(best_coeff)) + then + best_coeff = to_number(to_big(predicted_mod / _u)) + best_key = { l, _l } + end + end + end + end + end + dbl_info.scaler = best_key + end + end + end + end + if dbl_info.scaler then + dbl_info.scaler_base = #dbl_info.scaler == 2 + and orig_ability[dbl_info.scaler[1]][dbl_info.scaler[2]] + or orig_ability[dbl_info.scaler[1]] + dbl_info.offset = 1 + end + end + end + end + local orig_scale_base = nil + local orig_scale_scale = nil + if G.GAME.cry_double_scale[self.sort_id] and G.GAME.cry_double_scale[self.sort_id].scaler then + local jkr = self + local dbl_info = G.GAME.cry_double_scale[self.sort_id] + if #dbl_info.base == 2 then + if + not ( + type(jkr.ability) ~= "table" + or not orig_ability[dbl_info.base[1]] + or type(orig_ability[dbl_info.base[1]]) ~= "table" + or not orig_ability[dbl_info.base[1]][dbl_info.base[2]] + ) + then + orig_scale_base = orig_ability[dbl_info.base[1]][dbl_info.base[2]] + end + else + if jkr.ability[dbl_info.base[1]] then + orig_scale_base = orig_ability[dbl_info.base[1]] + end + end + if #dbl_info.scaler == 2 then + if + not (not orig_ability[dbl_info.scaler[1]] or not orig_ability[dbl_info.scaler[1]][dbl_info.scaler[2]]) + then + orig_scale_scale = orig_ability[dbl_info.scaler[1]][dbl_info.scaler[2]] + end + else + if orig_ability[dbl_info.scaler[1]] then + orig_scale_scale = orig_ability[dbl_info.scaler[1]] + end + end + end + + if orig_scale_base and orig_scale_scale then + local new_scale_base = nil + local true_base = nil + local jkr = self + local dbl_info = G.GAME.cry_double_scale[self.sort_id] + if #dbl_info.base == 2 then + if + not ( + type(jkr.ability) ~= "table" + or not jkr.ability[dbl_info.base[1]] + or type(jkr.ability[dbl_info.base[1]]) ~= "table" + or not jkr.ability[dbl_info.base[1]][dbl_info.base[2]] + ) + then + new_scale_base = jkr.ability[dbl_info.base[1]][dbl_info.base[2]] + end + else + if jkr.ability[dbl_info.base[1]] then + new_scale_base = jkr.ability[dbl_info.base[1]] + end + end + true_base = dbl_info.scaler_base + if + new_scale_base and ((to_big(math.abs(new_scale_base - orig_scale_base)) > to_big(0)) or in_context_scaling) + then + for i = 1, #G.jokers.cards do + local obj = G.jokers.cards[i].config.center + if obj.cry_scale_mod and type(obj.cry_scale_mod) == "function" then + local ggpn = G.GAME.probabilities.normal + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local o = obj:cry_scale_mod( + G.jokers.cards[i], + jkr, + orig_scale_scale, + true_base, + orig_scale_base, + new_scale_base + ) + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if o then + if #dbl_info.scaler == 2 then + if + not ( + not jkr.ability[dbl_info.scaler[1]] + or not jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] + ) + then + jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] = o + orig_scale_scale = o + end + else + if jkr.ability[dbl_info.scaler[1]] then + jkr.ability[dbl_info.scaler[1]] = o + orig_scale_scale = o + end + end + card_eval_status_text( + G.jokers.cards[i], + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex") } + ) + end + local reps = {} + for i2 = 1, #G.jokers.cards do + local _card = G.jokers.cards[i2] + local ggpn = G.GAME.probabilities.normal + if _card.ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local check = + cj(G.jokers.cards[i2], { retrigger_joker_check = true, other_card = G.jokers.cards[i] }) + if _card.ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if type(check) == "table" then + reps[i2] = check and check.repetitions and check or 0 + else + reps[i2] = 0 + end + if + G.jokers.cards[i2] == G.jokers.cards[i] + and G.jokers.cards[i].edition + and G.jokers.cards[i].edition.retriggers + then + local old_repetitions = reps[i] ~= 0 and reps[i].repetitions or 0 + local check = false --G.jokers.cards[i]:calculate_retriggers() + if check and check.repetitions then + check.repetitions = check.repetitions + old_repetitions + reps[i] = check + end + end + end + for i0, j in ipairs(reps) do + if (type(j) == "table") and j.repetitions and (j.repetitions > 0) then + for r = 1, j.repetitions do + card_eval_status_text(j.card, "jokers", nil, nil, nil, j) + local ggpn = G.GAME.probabilities.normal + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local o = obj:cry_scale_mod( + G.jokers.cards[i], + jkr, + orig_scale_scale, + true_base, + orig_scale_base, + new_scale_base + ) + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if o then + if #dbl_info.scaler == 2 then + if + not ( + not jkr.ability[dbl_info.scaler[1]] + or not jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] + ) + then + jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] = o + orig_scale_scale = o + end + else + if jkr.ability[dbl_info.scaler[1]] then + jkr.ability[dbl_info.scaler[1]] = o + orig_scale_scale = o + end + end + card_eval_status_text( + G.jokers.cards[i], + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex") } + ) + end + end + end + end + end + end + end + end +end + +function Card:calculate_joker(context) + --Calculate events + if self == G.jokers.cards[1] then + for k, v in pairs(SMODS.Events) do + if G.GAME.events[k] then + context.pre_jokers = true + v:calculate(context) + context.pre_jokers = nil + end + end + end + local active_side = self + if next(find_joker("cry-Flip Side")) and not context.dbl_side and self.edition and self.edition.cry_double_sided then + self:init_dbl_side() + active_side = self.dbl_side + if context.callback then + local m = context.callback + context.callback = function(card,a,b) + m(self,a,b) + end + context.dbl_side = true + end + end + if active_side.will_shatter then + return + end + local ggpn = G.GAME.probabilities.normal + if not G.GAME.cry_double_scale then + G.GAME.cry_double_scale = { double_scale = true } --doesn't really matter what's in here as long as there's something + end + if active_side.ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local orig_ability = active_side:cry_copy_ability() + local in_context_scaling = false + local callback = context.callback + if active_side.ability.cry_possessed then + if not ((context.individual and not context.repetition) or (context.joker_main) or (context.other_joker and not context.post_trigger)) then + return + end + context.callback = nil + end + local ret, trig = cj(active_side, context) + if active_side.ability.cry_possessed and ret then + if ret.mult_mod then ret.mult_mod = ret.mult_mod * -1 end + if ret.Xmult_mod then ret.Xmult_mod = ret.Xmult_mod ^ -1 end + if ret.mult then ret.mult = ret.mult * -1 end + if ret.x_mult then ret.x_mult = ret.x_mult ^ -1 end + ret.e_mult = nil + ret.ee_mult = nil + ret.eee_mult = nil + ret.hyper_mult = nil + ret.Emult_mod = nil + ret.EEmult_mod = nil + ret.EEEmult_mod = nil + ret.hypermult_mod = nil + if ret.chip_mod then ret.chip_mod = ret.chip_mod * -1 end + if ret.Xchip_mod then ret.Xchip_mod = ret.Xchip_mod ^ -1 end + if ret.chips then ret.chips = ret.chips * -1 end + if ret.x_chips then ret.x_chips = ret.x_chips ^ -1 end + ret.e_chips = nil + ret.ee_chips = nil + ret.eee_chips = nil + ret.hyper_chips = nil + ret.Echip_mod = nil + ret.EEchip_mod = nil + ret.EEEchip_mod = nil + ret.hyperchip_mod = nil + if ret.message then + -- TODO - this is a hacky way to do this, but it works for now + if type(ret.message) == "table" then + ret.message = ret.message[1] + end + if ret.message:sub(1,1) == "+" then + ret.message = "-" .. ret.message:sub(2) + elseif ret.message:sub(1,1) == "X" then + ret.message = "/" .. ret.message:sub(2) + else + ret.message = ret.message .. "?" + end + end + callback(context.blueprint_card or self, ret, context.retrigger_joker) + end + if not context.blueprint and (active_side.ability.set == "Joker") and not active_side.debuff then + if ret or trig then + in_context_scaling = true + end + end + if active_side.ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + active_side:cry_double_scale_calc(orig_ability, in_context_scaling) + --Calculate events + if self == G.jokers.cards[#G.jokers.cards] then + for k, v in pairs(SMODS.Events) do + if G.GAME.events[k] then + context.post_jokers = true + v:calculate(context) + context.post_jokers = nil + end + end + end + return ret, trig +end + +function exponentia_scale_mod(self, orig_scale_scale, orig_scale_base, new_scale_base) + local jkr = self + local dbl_info = G.GAME.cry_double_scale[jkr.sort_id] + if jkr.ability and type(jkr.ability) == "table" then + if not G.GAME.cry_double_scale[jkr.sort_id] or not G.GAME.cry_double_scale[jkr.sort_id].ability then + if not G.GAME.cry_double_scale[jkr.sort_id] then + G.GAME.cry_double_scale[jkr.sort_id] = { ability = { double_scale = true } } + end + for k, v in pairs(jkr.ability) do + if type(jkr.ability[k]) ~= "table" then + G.GAME.cry_double_scale[jkr.sort_id].ability[k] = v + else + G.GAME.cry_double_scale[jkr.sort_id].ability[k] = {} + for _k, _v in pairs(jkr.ability[k]) do + G.GAME.cry_double_scale[jkr.sort_id].ability[k][_k] = _v + end + end + end + end + if G.GAME.cry_double_scale[jkr.sort_id] and not G.GAME.cry_double_scale[jkr.sort_id].scaler then + dbl_info.base = { "extra", "Emult" } + dbl_info.scaler = { "extra", "Emult_mod" } + dbl_info.scaler_base = jkr.ability.extra.Emult_mod + dbl_info.offset = 1 + end + end + local true_base = dbl_info.scaler_base + if true_base then + for i = 1, #G.jokers.cards do + local obj = G.jokers.cards[i].config.center + if obj.cry_scale_mod and type(obj.cry_scale_mod) == "function" then + local ggpn = G.GAME.probabilities.normal + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local o = obj:cry_scale_mod( + G.jokers.cards[i], + jkr, + orig_scale_scale, + true_base, + orig_scale_base, + new_scale_base + ) + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if o then + if #dbl_info.scaler == 2 then + if + not ( + not jkr.ability[dbl_info.scaler[1]] + or not jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] + ) + then + jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] = o + orig_scale_scale = o + end + else + if jkr.ability[dbl_info.scaler[1]] then + jkr.ability[dbl_info.scaler[1]] = o + orig_scale_scale = o + end + end + card_eval_status_text( + G.jokers.cards[i], + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex") } + ) + end + local reps = {} + for i2 = 1, #G.jokers.cards do + local _card = G.jokers.cards[i2] + local ggpn = G.GAME.probabilities.normal + if _card.ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local check = + cj(G.jokers.cards[i2], { retrigger_joker_check = true, other_card = G.jokers.cards[i] }) + if _card.ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if type(check) == "table" then + reps[i2] = check and check.repetitions and check or 0 + else + reps[i2] = 0 + end + if + G.jokers.cards[i2] == G.jokers.cards[i] + and G.jokers.cards[i].edition + and G.jokers.cards[i].edition.retriggers + then + local old_repetitions = reps[i] ~= 0 and reps[i].repetitions or 0 + local check = false --G.jokers.cards[i]:calculate_retriggers() + if check and check.repetitions then + check.repetitions = check.repetitions + old_repetitions + reps[i] = check + end + end + end + for i0, j in ipairs(reps) do + if (type(j) == "table") and j.repetitions and (j.repetitions > 0) then + for r = 1, j.repetitions do + card_eval_status_text(j.card, "jokers", nil, nil, nil, j) + local ggpn = G.GAME.probabilities.normal + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local o = obj:cry_scale_mod( + G.jokers.cards[i], + jkr, + orig_scale_scale, + true_base, + orig_scale_base, + new_scale_base + ) + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if o then + if #dbl_info.scaler == 2 then + if + not ( + not jkr.ability[dbl_info.scaler[1]] + or not jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] + ) + then + jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] = o + orig_scale_scale = o + end + else + if jkr.ability[dbl_info.scaler[1]] then + jkr.ability[dbl_info.scaler[1]] = o + orig_scale_scale = o + end + end + card_eval_status_text( + G.jokers.cards[i], + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex") } + ) + end + end + end + end + end + end + end +end + +function compound_interest_scale_mod(self, orig_scale_scale, orig_scale_base, new_scale_base) + local jkr = self + local dbl_info = G.GAME.cry_double_scale[jkr.sort_id] + if jkr.ability and type(jkr.ability) == "table" then + if not G.GAME.cry_double_scale[jkr.sort_id] or not G.GAME.cry_double_scale[jkr.sort_id].ability then + if not G.GAME.cry_double_scale[jkr.sort_id] then + G.GAME.cry_double_scale[jkr.sort_id] = { ability = { double_scale = true } } + end + for k, v in pairs(jkr.ability) do + if type(jkr.ability[k]) ~= "table" then + G.GAME.cry_double_scale[jkr.sort_id].ability[k] = v + else + G.GAME.cry_double_scale[jkr.sort_id].ability[k] = {} + for _k, _v in pairs(jkr.ability[k]) do + G.GAME.cry_double_scale[jkr.sort_id].ability[k][_k] = _v + end + end + end + end + if G.GAME.cry_double_scale[jkr.sort_id] and not G.GAME.cry_double_scale[jkr.sort_id].scaler then + dbl_info.base = { "extra", "percent" } + dbl_info.scaler = { "extra", "percent_mod" } + dbl_info.scaler_base = jkr.ability.extra.percent_mod + dbl_info.offset = 1 + end + end + local true_base = dbl_info.scaler_base + if true_base then + for i = 1, #G.jokers.cards do + local obj = G.jokers.cards[i].config.center + if obj.cry_scale_mod and type(obj.cry_scale_mod) == "function" then + local ggpn = G.GAME.probabilities.normal + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local o = obj:cry_scale_mod( + G.jokers.cards[i], + jkr, + orig_scale_scale, + true_base, + orig_scale_base, + new_scale_base + ) + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if o then + if #dbl_info.scaler == 2 then + if + not ( + not jkr.ability[dbl_info.scaler[1]] + or not jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] + ) + then + jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] = o + orig_scale_scale = o + end + else + if jkr.ability[dbl_info.scaler[1]] then + jkr.ability[dbl_info.scaler[1]] = o + orig_scale_scale = o + end + end + card_eval_status_text( + G.jokers.cards[i], + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex") } + ) + end + local reps = {} + for i2 = 1, #G.jokers.cards do + local _card = G.jokers.cards[i2] + local ggpn = G.GAME.probabilities.normal + if _card.ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local check = + cj(G.jokers.cards[i2], { retrigger_joker_check = true, other_card = G.jokers.cards[i] }) + if _card.ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if type(check) == "table" then + reps[i2] = check and check.repetitions and check or 0 + else + reps[i2] = 0 + end + if + G.jokers.cards[i2] == G.jokers.cards[i] + and G.jokers.cards[i].edition + and G.jokers.cards[i].edition.retriggers + then + local old_repetitions = reps[i] ~= 0 and reps[i].repetitions or 0 + local check = false --G.jokers.cards[i]:calculate_retriggers() + if check and check.repetitions then + check.repetitions = check.repetitions + old_repetitions + reps[i] = check + end + end + end + for i0, j in ipairs(reps) do + if (type(j) == "table") and j.repetitions and (j.repetitions > 0) then + for r = 1, j.repetitions do + card_eval_status_text(j.card, "jokers", nil, nil, nil, j) + local ggpn = G.GAME.probabilities.normal + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = 1e9 + end + local o = obj:cry_scale_mod( + G.jokers.cards[i], + jkr, + orig_scale_scale, + true_base, + orig_scale_base, + new_scale_base + ) + if G.jokers.cards[i].ability.cry_rigged then + G.GAME.probabilities.normal = ggpn + end + if o then + if #dbl_info.scaler == 2 then + if + not ( + not jkr.ability[dbl_info.scaler[1]] + or not jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] + ) + then + jkr.ability[dbl_info.scaler[1]][dbl_info.scaler[2]] = o + orig_scale_scale = o + end + else + if jkr.ability[dbl_info.scaler[1]] then + jkr.ability[dbl_info.scaler[1]] = o + orig_scale_scale = o + end + end + card_eval_status_text( + G.jokers.cards[i], + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex") } + ) + end + end + end + end + end + end + end +end + +function Card:is_jolly() + local check = false + if self.ability.name == "Jolly Joker" then + check = true + end + if (self.edition and self.edition.key == "e_cry_m") then + check = true + end + + --[[ + Some scenarios/ examples I used for testing this (These DO work as intended if not commented out) + if next(find_joker("cry-mneon")) then + check = true + end + if G.GAME.blind.boss then + check = true + end + ]]-- + return check +end + +function cry_with_deck_effects(card, func) + if not card.added_to_deck then + return func(card) + else + card:remove_from_deck(true) + local ret = func(card) + card:add_to_deck(true) + return ret + end +end + +function cry_deep_copy(obj, seen) + if type(obj) ~= "table" then + return obj + end + if seen and seen[obj] then + return seen[obj] + end + local s = seen or {} + local res = setmetatable({}, getmetatable(obj)) + s[obj] = res + for k, v in pairs(obj) do + res[cry_deep_copy(k, s)] = cry_deep_copy(v, s) + end + return res +end + +G.C.CRY_JOLLY = { 0, 0, 0, 0 } + +-- File loading based on Relic-Jokers +local files = NFS.getDirectoryItems(mod_path .. "Items") +Cryptid.obj_buffer = {} +for _, file in ipairs(files) do + print("Loading file " .. file) + local f, err = SMODS.load_file("Items/" .. file) + if err then + print("Error loading file: " .. err) + else + local curr_obj = f() + if curr_obj.name == "HTTPS Module" and Cryptid_config[curr_obj.name] == nil then + Cryptid_config[curr_obj.name] = false + end + if Cryptid_config[curr_obj.name] == nil then + Cryptid_config[curr_obj.name] = true + Cryptid.enabled[curr_obj.name] = true + end + if Cryptid_config[curr_obj.name] then + if curr_obj.init then + curr_obj:init() + end + if not curr_obj.items then + print("Warning: " .. file .. " has no items") + else + for _, item in ipairs(curr_obj.items) do + if not item.order then + item.order = 0 + end + if curr_obj.order then + item.order = item.order + curr_obj.order + end + if SMODS[item.object_type] then + if not Cryptid.obj_buffer[item.object_type] then + Cryptid.obj_buffer[item.object_type] = {} + end + Cryptid.obj_buffer[item.object_type][#Cryptid.obj_buffer[item.object_type] + 1] = item + else + print("Error loading item " .. item.key .. " of unknown type " .. item.object_type) + end + end + end + end + end +end +for set, objs in pairs(Cryptid.obj_buffer) do + table.sort(objs, function(a, b) + return a.order < b.order + end) + for i = 1, #objs do + if objs[i].post_process and type(objs[i].post_process) == "function" then + objs[i]:post_process() + end + SMODS[set](objs[i]) + end +end +local cryptidTabs = function() return { + { + label = localize("cry_set_features"), + chosen = true, + tab_definition_function = function() + cry_nodes = { + { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + { + n = G.UIT.O, + config = { + object = DynaText({ + string = localize("cry_set_enable_features"), + colours = { G.C.WHITE }, + shadow = true, + scale = 0.4, + }), + }, + }, + }, + }, + } + left_settings = { n = G.UIT.C, config = { align = "tl", padding = 0.05 }, nodes = {} } + right_settings = { n = G.UIT.C, config = { align = "tl", padding = 0.05 }, nodes = {} } + --todo: completely redesign this, make it possible to enable/disable individual items + local ordered_config = {} + for k, _ in pairs(Cryptid_config) do + if localize("cry_feat_"..string.lower(k)) ~= "ERROR" and k ~= "JokerDisplay" then + ordered_config[#ordered_config+1] = k + end + end + table.sort(ordered_config) + for _, k in ipairs(ordered_config) do + if #right_settings.nodes < #left_settings.nodes then + right_settings.nodes[#right_settings.nodes + 1] = + create_toggle({ label = localize("cry_feat_"..string.lower(k)), ref_table = Cryptid_config, ref_value = k }) + else + left_settings.nodes[#left_settings.nodes + 1] = + create_toggle({ label = localize("cry_feat_"..string.lower(k)), ref_table = Cryptid_config, ref_value = k }) + end + end + config = { n = G.UIT.R, config = { align = "tm", padding = 0 }, nodes = { left_settings, right_settings } } + cry_nodes[#cry_nodes + 1] = config + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 10, + align = "cm", + padding = 0.2, + colour = G.C.BLACK, + }, + nodes = cry_nodes, + } + end, + }, + { + label = localize("cry_set_music"), + tab_definition_function = function() + -- TODO: Add a button here to reset all Cryptid achievements. + -- If you want to do that now, add this to the SMODS.InjectItems in Steamodded/loader/loader.lua + --[[fetch_achievements() + for k, v in pairs(SMODS.Achievements) do + G.SETTINGS.ACHIEVEMENTS_EARNED[k] = nil + G.ACHIEVEMENTS[k].earned = nil + end + fetch_achievements()]] + cry_nodes = { + { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + --{n=G.UIT.O, config={object = DynaText({string = "", colours = {G.C.WHITE}, shadow = true, scale = 0.4})}}, + }, + }, + } + settings = { n = G.UIT.C, config = { align = "tl", padding = 0.05 }, nodes = {} } + settings.nodes[#settings.nodes + 1] = create_toggle({ + label = localize("cry_mus_jimball"), + ref_table = Cryptid_config.Cryptid, + ref_value = "jimball_music", + }) + settings.nodes[#settings.nodes + 1] = create_toggle({ + label = localize("cry_mus_code"), + ref_table = Cryptid_config.Cryptid, + ref_value = "code_music", + }) + settings.nodes[#settings.nodes + 1] = create_toggle({ + label = localize("cry_mus_exotic"), + ref_table = Cryptid_config.Cryptid, + ref_value = "exotic_music", + }) + settings.nodes[#settings.nodes + 1] = create_toggle({ + label = localize("cry_mus_high_score"), + ref_table = Cryptid_config.Cryptid, + ref_value = "big_music", + }) + config = { n = G.UIT.R, config = { align = "tm", padding = 0 }, nodes = { settings } } + cry_nodes[#cry_nodes + 1] = config + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 10, + align = "cm", + padding = 0.2, + colour = G.C.BLACK, + }, + nodes = cry_nodes, + } + end, + }, +} end +G.FUNCS.cryptidMenu = function(e) + local tabs = create_tabs({ + snap_to_nav = true, + tabs = cryptidTabs(), + }) + G.FUNCS.overlay_menu({ + definition = create_UIBox_generic_options({ + back_func = "options", + contents = { tabs }, + }), + config = { offset = { x = 0, y = 10 } }, + }) +end + +--[[SMODS.current_mod.config_tab = function() + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 10, + align = "cm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = {UIBox_button{ label = {"Open Cryptid Config"}, button = "cryptidMenu", colour = G.C.DARK_EDITION, minw = 5, minh = 0.7, scale = 0.6}} + } +end--]] +SMODS.current_mod.extra_tabs = cryptidTabs + +-- Modify to display badges for credits +local smcmb = SMODS.create_mod_badges +function SMODS.create_mod_badges(obj, badges) + smcmb(obj, badges) + if obj and obj.cry_credits then + local function calc_scale_fac(text) + local size = 0.9 + local font = G.LANG.font + local max_text_width = 2 - 2 * 0.05 - 4 * 0.03 * size - 2 * 0.03 + local calced_text_width = 0 + -- Math reproduced from DynaText:update_text + for _, c in utf8.chars(text) do + local tx = font.FONT:getWidth(c) * (0.33 * size) * G.TILESCALE * font.FONTSCALE + + 2.7 * 1 * G.TILESCALE * font.FONTSCALE + calced_text_width = calced_text_width + tx / (G.TILESIZE * G.TILESCALE) + end + local scale_fac = calced_text_width > max_text_width and max_text_width / calced_text_width or 1 + return scale_fac + end + if obj.cry_credits.art or obj.cry_credits.code or obj.cry_credits.idea then + local scale_fac = {} + local min_scale_fac = 1 + local strings = {"Cryptid"} + for _, v in ipairs({"idea", "art", "code"}) do + if obj.cry_credits[v] then + for i = 1, #obj.cry_credits[v] do + strings[#strings+1] = localize{type='variable',key='cry_'..v,vars={obj.cry_credits[v][i]}}[1] + end + end + end + for i = 1, #strings do + scale_fac[i] = calc_scale_fac(strings[i]) + min_scale_fac = math.min(min_scale_fac, scale_fac[i]) + end + local ct = {} + for i = 1, #strings do + ct[i] = { + string = strings[i], + } + end + local cry_badge = { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + { + n = G.UIT.R, + config = { + align = "cm", + colour = G.C.CRY_EXOTIC, + r = 0.1, + minw = 2/min_scale_fac, + minh = 0.36, + emboss = 0.05, + padding = 0.03 * 0.9, + }, + nodes = { + { n = G.UIT.B, config = { h = 0.1, w = 0.03 } }, + { + n = G.UIT.O, + config = { + object = DynaText({ + string = ct or "ERROR", + colours = { obj.cry_credits and obj.cry_credits.text_colour or G.C.WHITE }, + silent = true, + float = true, + shadow = true, + offset_y = -0.03, + spacing = 1, + scale = 0.33 * 0.9, + }), + }, + }, + { n = G.UIT.B, config = { h = 0.1, w = 0.03 } }, + }, + }, + }, + } + local function eq_col(x, y) + for i = 1, 4 do + if x[1] ~= y[1] then + return false + end + end + return true + end + for i = 1, #badges do + if eq_col(badges[i].nodes[1].config.colour,HEX("708b91")) then + badges[i].nodes[1].nodes[2].config.object:remove() + badges[i] = cry_badge + break + end + end + end + if obj.cry_credits.jolly then + local scale_fac = {} + local min_scale_fac = 1 + for i = 1, #obj.cry_credits.jolly do + scale_fac[i] = calc_scale_fac(obj.cry_credits.jolly[i]) + min_scale_fac = math.min(min_scale_fac, scale_fac[i]) + end + local ct = {} + for i = 1, #obj.cry_credits.jolly do + ct[i] = { + string = obj.cry_credits.jolly[i], + } + end + badges[#badges + 1] = { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + { + n = G.UIT.R, + config = { + align = "cm", + colour = G.C.CRY_JOLLY, + r = 0.1, + minw = 2, + minh = 0.36, + emboss = 0.05, + padding = 0.03 * 0.9, + }, + nodes = { + { n = G.UIT.B, config = { h = 0.1, w = 0.03 } }, + { + n = G.UIT.O, + config = { + object = DynaText({ + string = ct or "ERROR", + colours = { obj.cry_credits and obj.cry_credits.text_colour_jolly or G.C.WHITE }, + silent = true, + float = true, + shadow = true, + offset_y = -0.03, + spacing = 1, + scale = 0.33 * 0.9, + }), + }, + }, + { n = G.UIT.B, config = { h = 0.1, w = 0.03 } }, + }, + }, + }, + } + end + end +end + +-- This is short enough that I'm fine overriding it +function calculate_reroll_cost(skip_increment) + if G.GAME.current_round.free_rerolls < 0 then + G.GAME.current_round.free_rerolls = 0 + end + if next(find_joker("cry-crustulum")) + or G.GAME.current_round.free_rerolls > 0 then + G.GAME.current_round.reroll_cost = 0 + return + end + if next(find_joker("cry-candybuttons")) then + G.GAME.current_round.reroll_cost = 1 + return + end + if G.GAME.used_vouchers.v_cry_rerollexchange then + G.GAME.current_round.reroll_cost = 2 + return + end + G.GAME.current_round.reroll_cost_increase = G.GAME.current_round.reroll_cost_increase or 0 + if not skip_increment then + G.GAME.current_round.reroll_cost_increase = G.GAME.current_round.reroll_cost_increase + + (G.GAME.modifiers.cry_reroll_scaling or 1) + end + G.GAME.current_round.reroll_cost = (G.GAME.round_resets.temp_reroll_cost or G.GAME.round_resets.reroll_cost) + + G.GAME.current_round.reroll_cost_increase +end + +--Top Gear from The World End with Jimbo has several conflicts with Cryptid items +--Namely, It overrides the edition that edition jokers spawn with, and doesn't work correctly with edition decks +--I'm taking ownership of this, overiding it, and making an implementaion that is compatible with Cryptid + +--Unrelated but kind of related side note: this prevents top gear from showing up in collection, not sure what's up with that +--Is it due to how TWEWJ is Coded? Is it an issue with Steamodded itself? Might be worth looking into, just sayin + +--Ok it's definitely something with steamodded + +if (SMODS.Mods["TWEWY"] or {}).can_load then + SMODS.Joker:take_ownership('twewy_topGear', { + name = "Cry-topGear", + --Stop Top Gear's Old code from working by overriding these + add_to_deck = function(self, card, from_debuff) + end, + remove_from_deck = function(self, card, from_debuff) + end, + rarity = 3, + loc_txt = { + name = 'Top Gear', + text = { + "All {C:blue}Common{C:attention} Jokers{}", + "are {C:dark_edition}Polychrome{}", + } + }, + }) +end + +-- We're modifying so much of this for Brown and Yellow Stake, Equilibrium Deck, etc. that it's fine to override... +function create_card(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append) + local area = area or G.jokers + local pseudo = function(x) + return pseudorandom(pseudoseed(x)) + end + local ps = pseudoseed + if area == "ERROR" then + pseudo = function(x) + return pseudorandom(predict_pseudoseed(x)) + end + ps = predict_pseudoseed + end + local center = G.P_CENTERS.b_red + if (_type == "Joker") and G.GAME and G.GAME.modifiers and G.GAME.modifiers.all_rnj then + forced_key = "j_cry_rnjoker" + end + local function aeqviable(center) + return center.unlocked and not center_no(center, "doe") and not center_no(center, "aeq") and not (center.rarity == 6 or center.rarity == "cry_exotic") + end + if _type == "Joker" and not _rarity then + if not G.GAME.aequilibriumkey then G.GAME.aequilibriumkey = 1 end + local aeqactive = nil + if next(find_joker('Ace Aequilibrium')) and not forced_key then + while not aeqactive or not aeqviable(G.P_CENTER_POOLS.Joker[aeqactive]) do + if math.ceil(G.GAME.aequilibriumkey) > #G.P_CENTER_POOLS["Joker"] then + G.GAME.aequilibriumkey = 1 + end + aeqactive = math.ceil(G.GAME.aequilibriumkey) + G.GAME.aequilibriumkey = math.ceil(G.GAME.aequilibriumkey + 1) + end + end + if aeqactive then + forced_key = G.P_CENTER_POOLS["Joker"][aeqactive].key + end + end + --should pool be skipped with a forced key + if not forced_key and soulable and not G.GAME.banned_keys["c_soul"] then + for _, v in ipairs(SMODS.Consumable.legendaries) do + if + (_type == v.type.key or _type == v.soul_set) + and not (G.GAME.used_jokers[v.key] and not next(find_joker("Showman")) and not v.can_repeat_soul) + then + if pseudo("soul_" .. v.key .. _type .. G.GAME.round_resets.ante) > (1 - v.soul_rate) then + forced_key = v.key + end + end + end + if + (_type == "Tarot" or _type == "Spectral" or _type == "Tarot_Planet") + and not (G.GAME.used_jokers["c_soul"] and not next(find_joker("Showman"))) + then + if pseudo("soul_" .. _type .. G.GAME.round_resets.ante) > 0.997 then + forced_key = "c_soul" + end + end + if + (_type == "Planet" or _type == "Spectral") + and not (G.GAME.used_jokers["c_black_hole"] and not next(find_joker("Showman"))) + then + if pseudo("soul_" .. _type .. G.GAME.round_resets.ante) > 0.997 then + forced_key = "c_black_hole" + end + end + end + + if _type == "Base" then + forced_key = "c_base" + end + + if forced_key then --vanilla behavior change, mainly for M Joker reasons + center = G.P_CENTERS[forced_key] + _type = (center.set ~= "Default" and center.set or _type) + else + gcparea = area + local _pool, _pool_key = get_current_pool(_type, _rarity, legendary, key_append) + gcparea = nil + center = pseudorandom_element(_pool, ps(_pool_key)) + local it = 1 + while center == "UNAVAILABLE" do + it = it + 1 + center = pseudorandom_element(_pool, ps(_pool_key .. "_resample" .. it)) + end + + center = G.P_CENTERS[center] + end + + local front = ( + (_type == "Base" or _type == "Enhanced") + and pseudorandom_element(G.P_CARDS, ps("front" .. (key_append or "") .. G.GAME.round_resets.ante)) + ) or nil + + if area == "ERROR" then + local ret = (front or center) + if not ret.config then + ret.config = {} + end + if not ret.config.center then + ret.config.center = {} + end + if not ret.config.center.key then + ret.config.center.key = "" + end + if not ret.ability then ret.ability = {} end + return ret --the config.center.key stuff prevents a crash with Jen's Almanac hook + end + + local card = Card( + area and (area.T.x + area.T.w / 2) or 0, + area and area.T.y or 0, + G.CARD_W * (center and center.set == "Booster" and 1.27 or 1), + G.CARD_H * (center and center.set == "Booster" and 1.27 or 1), + front, + center, + { + bypass_discovery_center = area == G.shop_jokers + or area == G.pack_cards + or area == G.shop_vouchers + or (G.shop_demo and area == G.shop_demo) + or area == G.jokers + or area == G.consumeables, + bypass_discovery_ui = area == G.shop_jokers + or area == G.pack_cards + or area == G.shop_vouchers + or (G.shop_demo and area == G.shop_demo), + discover = area == G.jokers or area == G.consumeables, + bypass_back = G.GAME.selected_back.pos, + } + ) + if front and G.GAME.modifiers.cry_force_suit then + card:change_suit(G.GAME.modifiers.cry_force_suit) + end + if front and G.GAME.modifiers.cry_force_enhancement then + card:set_ability(G.P_CENTERS[G.GAME.modifiers.cry_force_enhancement]) + end + if front and G.GAME.modifiers.cry_force_edition then + card:set_edition({ [G.GAME.modifiers.cry_force_edition] = true }, true, true) + end + if front and G.GAME.modifiers.cry_force_seal then + card:set_seal(G.GAME.modifiers.cry_force_seal) + end + if card.ability.consumeable and not skip_materialize then + card:start_materialize() + end + for k, v in ipairs(SMODS.Sticker.obj_buffer) do + local sticker = SMODS.Stickers[v] + if + sticker.should_apply + and type(sticker.should_apply) == "function" + and sticker:should_apply(card, center, area) + then + sticker:apply(card, true) + end + end + if + G.GAME.modifiers.cry_force_sticker == "eternal" + or ( + G.GAME.modifiers.cry_sticker_sheet_plus + and not ( + (_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards)) + ) + ) + then -- wow that is long + card:set_eternal(true) + card.ability.eternal = true + end + if + G.GAME.modifiers.cry_force_sticker == "perishable" + or ( + G.GAME.modifiers.cry_sticker_sheet_plus + and not ( + (_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards)) + ) + ) + then + card:set_perishable(true) + card.ability.perish_tally = G.GAME.perishable_rounds -- set_perishable should be doing this? whatever + card.ability.perishable = true + end + if + G.GAME.modifiers.cry_force_sticker == "rental" + or ( + G.GAME.modifiers.cry_sticker_sheet_plus + and not ( + (_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards)) + ) + ) + then + card:set_rental(true) + card.ability.rental = true + end + if + G.GAME.modifiers.cry_force_sticker == "pinned" + or ( + G.GAME.modifiers.cry_sticker_sheet_plus + and not ( + (_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards)) + ) + ) + then + card.pinned = true + end + if + G.GAME.modifiers.cry_force_sticker == "banana" + or ( + G.GAME.modifiers.cry_sticker_sheet_plus + and not ( + (_type == "Base" or _type == "Enhanced") and not ((area == G.shop_jokers) or (area == G.pack_cards)) + ) + ) + then + card.ability.banana = true + end + if G.GAME.modifiers.cry_sticker_sheet_plus and not (_type == "Base" or _type == "Enhanced") then + for k, v in pairs(SMODS.Stickers) do + if v.apply and not v.no_sticker_sheet then + v:apply(card, true) + end + end + end + + if card.ability.name == "cry-Cube" then + card:set_eternal(true) + end + if _type == "Joker" or (G.GAME.modifiers.cry_any_stickers and not G.GAME.modifiers.cry_sticker_sheet) then + if G.GAME.modifiers.all_eternal then + card:set_eternal(true) + end + if G.GAME.modifiers.cry_all_perishable then + card:set_perishable(true) + end + if G.GAME.modifiers.cry_all_rental then + card:set_rental(true) + end + if G.GAME.modifiers.cry_all_pinned then + card.pinned = true + end + if G.GAME.modifiers.cry_all_banana then + card.ability.banana = true + end + if (area == G.shop_jokers) or (area == G.pack_cards) then + local eternal_perishable_poll = pseudorandom("cry_et" .. (key_append or "") .. G.GAME.round_resets.ante) + if G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.7 then + card:set_eternal(true) + end + if G.GAME.modifiers.enable_perishables_in_shop then + if + not G.GAME.modifiers.cry_eternal_perishable_compat + and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7)) + then + card:set_perishable(true) + end + if + G.GAME.modifiers.cry_eternal_perishable_compat + and pseudorandom("cry_per" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7 + then + card:set_perishable(true) + end + end + if + G.GAME.modifiers.enable_rentals_in_shop + and pseudorandom("cry_ssjr" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7 + then + card:set_rental(true) + end + if + G.GAME.modifiers.cry_enable_pinned_in_shop + and pseudorandom("cry_pin" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7 + then + card.pinned = true + end + if + not G.GAME.modifiers.cry_eternal_perishable_compat + and G.GAME.modifiers.enable_banana + and (pseudorandom("cry_banana" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7) + and (eternal_perishable_poll <= 0.7) + then + card.ability.banana = true + end + if + G.GAME.modifiers.cry_eternal_perishable_compat + and G.GAME.modifiers.enable_banana + and (pseudorandom("cry_banana" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7) + then + card.ability.banana = true + end + if G.GAME.modifiers.cry_sticker_sheet then + for k, v in pairs(SMODS.Stickers) do + if v.apply and not v.no_sticker_sheet then + v:apply(card, true) + end + end + end + if + G.GAME.modifiers.cry_enable_flipped_in_shop + and pseudorandom("cry_flip" .. (key_append or "") .. G.GAME.round_resets.ante) > 0.7 + then + card.cry_flipped = true + end + end + if _type == "Joker" and not G.GAME.modifiers.cry_force_edition then + local edition = poll_edition("edi" .. (key_append or "") .. G.GAME.round_resets.ante) + card:set_edition(edition) + check_for_unlock({ type = "have_edition" }) + end + end + if + (card.ability.set == "Code") + and G.GAME.used_vouchers.v_cry_quantum_computing + and pseudorandom("cry_quantum_computing") > 0.7 + then + card:set_edition({ negative = true }) + end + if + G.GAME.modifiers.cry_force_edition + and not G.GAME.modifiers.cry_force_random_edition + and area ~= G.pack_cards + then + card:set_edition(nil, true) + end + if G.GAME.modifiers.cry_force_random_edition and area ~= G.pack_cards then + local edition = cry_poll_random_edition() + card:set_edition(edition, true) + end + if not (card.edition and (card.edition.cry_oversat or card.edition.cry_glitched)) then + cry_misprintize(card) + end + if card.ability.consumeable and card.pinned then -- counterpart is in Sticker.toml + G.GAME.cry_pinned_consumeables = G.GAME.cry_pinned_consumeables + 0 + end + if next(find_joker("Cry-topGear")) and card.config.center.rarity == 1 then + if card.ability.name ~= "cry-meteor" + and card.ability.name ~= "cry-exoplanet" + and card.ability.name ~= "cry-stardust" + and card.ability.name ~= "cry-universe" then + card:set_edition("e_polychrome", true, nil, true) + end + end + if card.ability.name == "cry-meteor" then + card:set_edition("e_foil", true, nil, true) + end + if card.ability.name == "cry-exoplanet" then + card:set_edition("e_holo", true, nil, true) + end + if card.ability.name == "cry-stardust" then + card:set_edition("e_polychrome", true, nil, true) + end + if card.ability.name == "cry-universe" then + card:set_edition("e_cry_astral", true, nil, true) + end + -- Certain jokers such as Steel Joker and Driver's License depend on values set + -- during the update function. Cryptid can create jokers mid-scoring, meaning + -- those values will be unset during scoring unless update() is manually called. + card:update(0.016) -- dt is unused in the base game, but we're providing a realistic value anyway + + --Debuff jokers if certain boss blinds are active + if G.GAME and G.GAME.blind and not G.GAME.blind.disabled then + if G.GAME.blind.name == "cry-box" + or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_box"] == true) then + if card.config.center.rarity == 1 and not card.debuff then + card.debuff = true + card.debuffed_by_blind = true + end + end + if G.GAME.blind.name == "cry-windmill" + or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_windmill"] == true) then + if card.config.center.rarity == 2 and not card.debuff then + card.debuff = true + card.debuffed_by_blind = true + end + end + if G.GAME.blind.name == "cry-striker" + or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_striker"] == true) then + if card.config.center.rarity == 3 and not card.debuff then + card.debuff = true + card.debuffed_by_blind = true + end + end + if G.GAME.blind.name == "cry-shackle" + or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_shackle"] == true) then + if (card.edition and card.edition.negative == true) and not card.debuff then + card.debuff = true + card.debuffed_by_blind = true + end + end + if G.GAME.blind.name == "cry-pin" + or (G.GAME.blind.name == "cry-Obsidian Orb" and G.GAME.defeated_blinds["bl_cry_pin"] == true) then + if (card.config.center.rarity ~= 3 + and card.config.center.rarity ~= 2 + and card.config.center.rarity ~= 1 + and card.config.center.rarity ~= 5) then + card.debuff = true + card.debuffed_by_blind = true + end + end + end + return card +end + +-- Make tags fit if there's more than 13 of them +local at = add_tag +function add_tag(tag) + at(tag) + if #G.HUD_tags > 13 then + for i = 2, #G.HUD_tags do + G.HUD_tags[i].config.offset.y = 0.9 - 0.9 * 13 / #G.HUD_tags + end + end +end + +--add calculation context and callback to tag function +local at2 = add_tag +function add_tag(tag, from_skip, no_copy) + if no_copy then + at2(tag) + return + end + local added_tags = 1 + for i = 1, #G.jokers.cards do + local ret = G.jokers.cards[i]:calculate_joker({ cry_add_tag = true }) + if ret and ret.tags then + added_tags = added_tags + ret.tags + end + end + if added_tags >= 1 then + at2(tag) + end + for i = 2, added_tags do + local tag_table = tag:save() + local new_tag = Tag(tag.key) + new_tag:load(tag_table) + at2(new_tag) + end +end + +local tr = Tag.remove +function Tag:remove() + tr(self) + if #G.HUD_tags >= 13 then + for i = 2, #G.HUD_tags do + G.HUD_tags[i].config.offset.y = 0.9 - 0.9 * 13 / #G.HUD_tags + end + end +end + +local nr = new_round +function new_round() + G.hand:change_size(0) + nr() +end + +local gfcfbs = G.FUNCS.check_for_buy_space +G.FUNCS.check_for_buy_space = function(card) + if (card.ability.name == "cry-Negative Joker" and card.ability.extra >= 1) or + (card.ability.name == "cry-soccer" and card.ability.extra.holygrail >= 1) or + (card.ability.name == "cry-Tenebris" and card.ability.extra.slots >= 1) then + return true + end + return gfcfbs(card) +end + +local gfcsc = G.FUNCS.can_select_card +G.FUNCS.can_select_card = function(e) + if (e.config.ref_table.ability.name == "cry-Negative Joker" and e.config.ref_table.ability.extra >= 1) or + (e.config.ref_table.ability.name == "cry-soccer" and e.config.ref_table.ability.extra.holygrail >= 1) or + (e.config.ref_table.ability.name == "cry-Tenebris" and e.config.ref_table.ability.extra.slots >= 1) then + e.config.colour = G.C.GREEN + e.config.button = 'use_card' + else + gfcsc(e) + end +end + +--Redefine these here because they're always used +Cryptid.base_values = {} +function cry_misprintize_tbl(name, ref_tbl, ref_value, clear, override, stack) + if name and ref_tbl and ref_value then + tbl = cry_deep_copy(ref_tbl[ref_value]) + for k, v in pairs(tbl) do + if (type(tbl[k]) ~= "table") or is_number(tbl[k]) then + if + is_number(tbl[k]) + and not (k == "id") + and not (k == "perish_tally") + and not (k == "colour") + and not (k == "suit_nominal") + and not (k == "base_nominal") + and not (k == "face_nominal") + and not (k == "qty") + and not (k == "x_mult" and v == 1 and not tbl.override_x_mult_check) + and not (k == "selected_d6_face") + then --Temp fix, even if I did clamp the number to values that wouldn't crash the game, the fact that it did get randomized means that there's a higher chance for 1 or 6 than other values + if not Cryptid.base_values[name] then + Cryptid.base_values[name] = {} + end + if not Cryptid.base_values[name][k] then + Cryptid.base_values[name][k] = tbl[k] + end + tbl[k] = cry_sanity_check( + clear and Cryptid.base_values[name][k] + or cry_format( + (stack and tbl[k] or Cryptid.base_values[name][k]) + * cry_log_random( + pseudoseed("cry_misprint" .. G.GAME.round_resets.ante), + override and override.min or G.GAME.modifiers.cry_misprint_min, + override and override.max or G.GAME.modifiers.cry_misprint_max + ), + "%.2g" + ) + ) + end + else + for _k, _v in pairs(tbl[k]) do + if + is_number(tbl[k][_k]) + and not (_k == "id") + and not (k == "perish_tally") + and not (k == "colour") + and not (_k == "suit_nominal") + and not (_k == "base_nominal") + and not (_k == "face_nominal") + and not (_k == "qty") + and not (k == "x_mult" and v == 1 and not tbl[k].override_x_mult_check) + and not (_k == "selected_d6_face") + then --Refer to above + if not Cryptid.base_values[name] then + Cryptid.base_values[name] = {} + end + if not Cryptid.base_values[name][k] then + Cryptid.base_values[name][k] = {} + end + if not Cryptid.base_values[name][k][_k] then + Cryptid.base_values[name][k][_k] = tbl[k][_k] + end + tbl[k][_k] = cry_sanity_check( + clear and Cryptid.base_values[name][k][_k] + or cry_format( + (stack and tbl[k][_k] or Cryptid.base_values[name][k][_k]) + * cry_log_random( + pseudoseed("cry_misprint" .. G.GAME.round_resets.ante), + override and override.min or G.GAME.modifiers.cry_misprint_min, + override and override.max or G.GAME.modifiers.cry_misprint_max + ), + "%.2g" + ) + ) + end + end + end + end + ref_tbl[ref_value] = tbl + end +end +function cry_misprintize_val(val, override) + if is_number(val) then + val = cry_sanity_check( + cry_format( + val + * cry_log_random( + pseudoseed("cry_misprint" .. G.GAME.round_resets.ante), + override and override.min or G.GAME.modifiers.cry_misprint_min, + override and override.max or G.GAME.modifiers.cry_misprint_max + ), + "%.2g" + ) + ) + end + return val +end +function cry_sanity_check(val) + if not val or type(val) == "number" and (val ~= val or val > 1e300 or val < -1e300) then + return 1e300 + end + return val +end +function cry_misprintize(card, override, force_reset, stack) + --infinifusion compat + if card.infinifusion then + if card.config.center == card.infinifusion_center or card.config.center.key == 'j_infus_fused' then + calculate_infinifusion(card, nil, function(i) + cry_misprintize(card, override, force_reset, stack) + end) + end + end + if + (not force_reset or G.GAME.modifiers.cry_jkr_misprint_mod) + and (G.GAME.modifiers.cry_misprint_min or override or card.ability.set == "Joker") + and not stack or (not Card.no(card, "immune_to_chemach", true) and not Card.no(card, "immutable", true)) + then + if G.GAME.modifiers.cry_jkr_misprint_mod and card.ability.set == "Joker" then + if not override then + override = {} + end + override.min = override.min or G.GAME.modifiers.cry_misprint_min or 1 + override.max = override.max or G.GAME.modifiers.cry_misprint_max or 1 + override.min = override.min * G.GAME.modifiers.cry_jkr_misprint_mod + override.max = override.max * G.GAME.modifiers.cry_jkr_misprint_mod + end + if G.GAME.modifiers.cry_misprint_min or override and override.min then + cry_misprintize_tbl(card.config.center_key, card, "ability", nil, override, stack) + if card.base then + cry_misprintize_tbl(card.config.card_key, card, "base", nil, override, stack) + end + end + if G.GAME.modifiers.cry_misprint_min then + --card.cost = cry_format(card.cost / cry_log_random(pseudoseed('cry_misprint'..G.GAME.round_resets.ante),override and override.min or G.GAME.modifiers.cry_misprint_min,override and override.max or G.GAME.modifiers.cry_misprint_max),"%.2f") + card.misprint_cost_fac = 1 + / cry_log_random( + pseudoseed("cry_misprint" .. G.GAME.round_resets.ante), + override and override.min or G.GAME.modifiers.cry_misprint_min, + override and override.max or G.GAME.modifiers.cry_misprint_max + ) + card:set_cost() + end + else + cry_misprintize_tbl(card.config.center_key, card, "ability", true) + end + if card.ability.consumeable then + for k, v in pairs(card.ability.consumeable) do + card.ability.consumeable[k] = cry_deep_copy(card.ability[k]) + end + end +end +function cry_log_random(seed, min, max) + math.randomseed(seed) + local lmin = math.log(min, 2.718281828459045) + local lmax = math.log(max, 2.718281828459045) + local poll = math.random() * (lmax - lmin) + lmin + return math.exp(poll) +end +function cry_format(number, str) + if math.abs(to_big(number)) >= to_big(1e300) then + return number + end + return tonumber(str:format((Big and to_number(to_big(number)) or number))) +end +--use ID to work with glitched/misprint +function Card:get_nominal(mod) + local mult = 1 + local rank_mult = 1 + if mod == "suit" then + mult = 1000000 + end + if self.ability.effect == "Stone Card" or (self.config.center.no_suit and self.config.center.no_rank) then + mult = -10000 + elseif self.config.center.no_suit then + mult = 0 + elseif self.config.center.no_rank then + rank_mult = 0 + end + return 10 * (self.base.id or 0.1) * rank_mult + + self.base.suit_nominal * mult + + (self.base.suit_nominal_original or 0) * 0.0001 * mult + + 10 * self.base.face_nominal * rank_mult + + 0.000001 * self.unique_val +end + +--Cryptid (THE MOD) localization +local function parse_loc_txt(center) + center.text_parsed = {} + if not center.text then else + for _, line in ipairs(center.text) do + center.text_parsed[#center.text_parsed+1] = loc_parse_string(line) + end + center.name_parsed = {} + for _, line in ipairs(type(center.name) == 'table' and center.name or {center.name}) do + center.name_parsed[#center.name_parsed+1] = loc_parse_string(line) + end + if center.unlock then + center.unlock_parsed = {} + for _, line in ipairs(center.unlock) do + center.unlock_parsed[#center.unlock_parsed+1] = loc_parse_string(line) + end + end + end +end +local il = init_localization +function init_localization() + il() + if G.SETTINGS.language == "en-us" then + G.localization.descriptions.Spectral.c_cryptid.text[2] = "{C:attention}#2#{} selected card" + G.localization.descriptions.Spectral.c_talisman.text[2] = "to {C:attention}#1#{} selected" + G.localization.descriptions.Spectral.c_trance.text[2] = "to {C:attention}#1#{} selected" + G.localization.descriptions.Spectral.c_medium.text[2] = "to {C:attention}#1#{} selected" + G.localization.descriptions.Spectral.c_deja_vu.text[2] = "to {C:attention}#1#{} selected" + G.localization.descriptions.Spectral.c_deja_vu.text[2] = "to {C:attention}#1#{} selected" + G.localization.descriptions.Spectral.c_deja_vu.text[2] = "to {C:attention}#1#{} selected" + G.localization.descriptions.Voucher.v_antimatter.text[1] = "{C:dark_edition}+#1#{} Joker Slot" + G.localization.descriptions.Voucher.v_overstock_norm.text[1] = "{C:attention}+#1#{} card slot" + G.localization.descriptions.Voucher.v_overstock_plus.text[1] = "{C:attention}+#1#{} card slot" + G.localization.descriptions.Voucher.v_crystal_ball.text[1] = "{C:attention}+#1#{} consumable slot" + G.localization.descriptions.Joker.j_seance.text[1] = "If {C:attention}played hand{} contains a" -- damnit seance + end + if Cryptid.obj_buffer.Stake then + for i = 1, #Cryptid.obj_buffer.Stake do + local key = Cryptid.obj_buffer.Stake[i].key + local color = G.localization.descriptions.Stake[key] and G.localization.descriptions.Stake[key].colour + if color then + local sticker_key = key:sub(7).."_sticker" + if not G.localization.descriptions.Other[sticker_key] then + G.localization.descriptions.Other[sticker_key] = { + name = localize{type='variable',key='cry_sticker_name',vars={color}}[1], + text = localize{type='variable',key='cry_sticker_desc',vars={color,"{C:attention}","{}"}}, + } + parse_loc_txt(G.localization.descriptions.Other[sticker_key]) + end + end + end + end +end + +G.FUNCS.cry_asc_UI_set = function(e) +end + +-- this is a hook to make funny "x of a kind"/"flush x" display text +local pokerhandinforef = G.FUNCS.get_poker_hand_info +function G.FUNCS.get_poker_hand_info(_cards) + local text, loc_disp_text, poker_hands, scoring_hand, disp_text = pokerhandinforef(_cards) + if G.SETTINGS.language == "en-us" then + if #scoring_hand > 5 and (text == 'Flush Five' or text == 'Five of a Kind') then + local rank_array = {} + local county = 0 + for i = 1, #scoring_hand do + local val = scoring_hand[i]:get_id() + rank_array[val] = (rank_array[val] or 0) + 1 + if rank_array[val] > county then county = rank_array[val] end + end + local function create_num_chunk(int) -- maybe useful enough to not be local? but tbh this function is probably some common coding exercise + if int >= 1000 then int = 999 end + local ones = {["1"] = "One", ["2"] = "Two", ["3"] = "Three", ["4"] = "Four", ["5"] = "Five", ["6"] = "Six", ["7"] = "Seven", ["8"] = "Eight", ["9"] = "Nine"} + local tens = {["1"] = "Ten", ["2"] = "Twenty", ["3"] = "Thirty", ["4"] = "Forty", ["5"] = "Fifty", ["6"] = "Sixty", ["7"] = "Seventy", ["8"] = "Eighty", ["9"] = "Ninety"} + local str_int = string.reverse(int.."") -- ehhhh whatever + local str_ret = "" + for i = 1, string.len(str_int) do + local place = str_int:sub(i, i) + if place ~= "0" then + if i == 1 then str_ret = ones[place] + elseif i == 2 then + if place == "1" and str_ret ~= "" then -- admittedly not my smartest moment, i dug myself into a hole here... + if str_ret == "One" then str_ret = "Eleven" + elseif str_ret == "Two" then str_ret = "Twelve" + elseif str_ret == "Three" then str_ret = "Thirteen" + elseif str_ret == "Five" then str_ret = "Fifteen" + elseif str_ret == "Eight" then str_ret = "Eighteen" + else str_ret = str_ret.."teen" end + else + str_ret = tens[place]..((string.len(str_ret) > 0 and " " or "")..str_ret) + end + elseif i == 3 then str_ret = ones[place]..(" Hundred"..((string.len(str_ret) > 0 and " and " or "")..str_ret)) end -- this line is wild + end + end + return str_ret + end + -- text gets stupid small at 100+ anyway + loc_disp_text = (text == 'Flush Five' and "Flush " or "")..((county < 1000 and create_num_chunk(county) or "Thousand")..(text == 'Five of a Kind' and " of a Kind" or "")) + end + end + + + + + + + local hand_table = { + ['High Card'] = G.GAME.used_vouchers.v_cry_hyperspacetether and 1 or nil, + ['Pair'] = G.GAME.used_vouchers.v_cry_hyperspacetether and 2 or nil, + ['Two Pair'] = 4, + ['Three of a Kind'] = G.GAME.used_vouchers.v_cry_hyperspacetether and 3 or nil, + ['Straight'] = 5, + ['Flush'] = 5, + ['Full House'] = 5, + ['Four of a Kind'] = G.GAME.used_vouchers.v_cry_hyperspacetether and 4 or nil, + ['Straight Flush'] = 5, + ['cry_Bulwark'] = 5, + ['Five of a Kind'] = 5, + ['Flush House'] = 5, + ['Flush Five'] = 5, + ['cry_Clusterfuck'] = 8, + ['cry_UltPair'] = 8, + ['cry_WholeDeck'] = 52, + } + + -- this is where all the logic for asc hands is. currently it's very simple but if you want more complex logic, here's the place to do it + if hand_table[text] then + G.GAME.current_round.current_hand.cry_asc_num = G.GAME.used_vouchers.v_cry_hyperspacetether and #_cards - hand_table[text] or #scoring_hand - hand_table[text] + else + G.GAME.current_round.current_hand.cry_asc_num = 0 + end + + + + G.GAME.current_round.current_hand.cry_asc_num_text = (G.GAME.current_round.current_hand.cry_asc_num and G.GAME.current_round.current_hand.cry_asc_num > 0) and " (+"..G.GAME.current_round.current_hand.cry_asc_num..")" or "" + return text, loc_disp_text, poker_hands, scoring_hand, disp_text +end + +function cry_ascend(num) -- edit this function at your leisure + return num*((1.25 + (0.05 * (G.GAME.sunnumber or 0)))^G.GAME.current_round.current_hand.cry_asc_num or 0) +end + +function cry_pulse_flame(duration, intensity) -- duration is in seconds, intensity is in idfk honestly, but it increases pretty quickly + G.cry_flame_override = G.cry_flame_override or {} + G.cry_flame_override["duration"] = duration or 0.01 + G.cry_flame_override["intensity"] = intensity or 2 +end + +--Will be moved to D20 file when that gets added +function roll_dice(seed, min, max, config) + local val + while not val or (config and config.ignore_value == val) do + val = pseudorandom(seed, min, max) + end + return val +end + +function SMODS.current_mod.reset_game_globals(run_start) + G.GAME.cry_ach_conditions = G.GAME.cry_ach_conditions or {} +end + +--Fix a corrupted game state +function Controller:queue_L_cursor_press(x, y) + if self.locks.frame then + return + end + if G.STATE == G.STATES.SPLASH then + if not G.HUD then + self:key_press("escape") + else + G.STATE = G.STATES.BLIND_SELECT + end + end + self.L_cursor_queue = { x = x, y = y } +end + +--Used to check to play the exotic music +function cry_has_exotic() + if G.jokers then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].config.center.rarity == "cry_exotic" then + return true + end + end + end +end +--Used for m vouchers, perhaps this can have more applications in the future +function get_m_jokers() + local mcount = 0 + if G.jokers then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.effect == "M Joker" then + mcount = mcount + 1 + end + if G.jokers.cards[i].ability.name == "cry-mprime" then + mcount = mcount + 1 + end + end + end + return mcount +end + +-- Check G.GAME as well as joker info for banned keys +function Card:no(m, no_no) + if no_no then + -- Infinifusion Compat + if self.infinifusion then + for i = 1, #self.infinifusion do + if G.P_CENTERS[self.infinifusion[i].key][m] or (G.GAME and G.GAME[m] and G.GAME[m][self.infinifusion[i].key]) then + return true + end + end + return false + end + if not self.config then + --assume this is from one component of infinifusion + return G.P_CENTERS[self.key][m] or (G.GAME and G.GAME[m] and G.GAME[m][self.key]) + end + + return self.config.center[m] or (G.GAME and G.GAME[m] and G.GAME[m][self.config.center_key]) or false + end + return Card.no(self, "no_"..m, true) +end + +function center_no(center, m, key, no_no) + if no_no then + return center[m] or (G.GAME and G.GAME[m] and G.GAME[m][key]) or false + end + return center_no(center, "no_"..m, key, true) +end + +-- Fix a CCD-related crash +local cuc = Card.can_use_consumeable +function Card:can_use_consumeable(any_state, skip_check) + if not self.ability.consumeable then + return false + end + return cuc(self, any_state, skip_check) +end + +--make this always active to prevent crashes +function cry_apply_ante_tax() + if G.GAME.modifiers.cry_ante_tax then + local tax = math.max( + 0, + math.min(G.GAME.modifiers.cry_ante_tax_max, math.floor(G.GAME.modifiers.cry_ante_tax * G.GAME.dollars)) + ) + ease_dollars(-1 * tax) + return true + end + return false +end + +--Stickers and modifiers used by Challenges+Stakes +SMODS.Atlas({ + key = "sticker", + path = "sticker_cry.png", + px = 71, + py = 95, + inject = function(self) + local file_path = type(self.path) == "table" + and (self.path[G.SETTINGS.language] or self.path["default"] or self.path["en-us"]) + or self.path + if file_path == "DEFAULT" then + return + end + -- language specific sprites override fully defined sprites only if that language is set + if self.language and not (G.SETTINGS.language == self.language) then + return + end + if not self.language and self.obj_table[("%s_%s"):format(self.key, G.SETTINGS.language)] then + return + end + self.full_path = (self.mod and self.mod.path or SMODS.path) + .. "assets/" + .. G.SETTINGS.GRAPHICS.texture_scaling + .. "x/" + .. file_path + local file_data = + assert(NFS.newFileData(self.full_path), ("Failed to collect file data for Atlas %s"):format(self.key)) + self.image_data = assert( + love.image.newImageData(file_data), + ("Failed to initialize image data for Atlas %s"):format(self.key) + ) + self.image = + love.graphics.newImage(self.image_data, { mipmaps = true, dpiscale = G.SETTINGS.GRAPHICS.texture_scaling }) + G[self.atlas_table][self.key_noloc or self.key] = self + G.shared_sticker_banana = + Sprite(0, 0, G.CARD_W, G.CARD_H, G[self.atlas_table][self.key_noloc or self.key], { x = 5, y = 2 }) + G.shared_sticker_pinned = + Sprite(0, 0, G.CARD_W, G.CARD_H, G[self.atlas_table][self.key_noloc or self.key], { x = 5, y = 0 }) + end, +}) +function Card:set_perishable(_perishable) + self.ability.perishable = nil + if + (self.config.center.perishable_compat or G.GAME.modifiers.cry_any_stickers) + and (not self.ability.eternal or G.GAME.modifiers.cry_eternal_perishable_compat) + then + self.ability.perishable = true + self.ability.perish_tally = G.GAME.perishable_rounds or 5 + end +end +function Card:set_eternal(_eternal) + self.ability.eternal = nil + if + (self.config.center.eternal_compat or G.GAME.modifiers.cry_any_stickers) + and (not self.ability.perishable or G.GAME.modifiers.cry_eternal_perishable_compat) + then + self.ability.eternal = _eternal + end +end +function Card:calculate_banana() + if not self.ability.extinct then + if self.ability.banana and (pseudorandom("banana") < G.GAME.probabilities.normal / 10) then + self.ability.extinct = true + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + if self.area then + self.area:remove_card(self) + end + self:remove() + self = nil + return true + end, + })) + return true + end, + })) + card_eval_status_text(self, "jokers", nil, nil, nil, { message = localize("k_extinct_ex"), delay = 0.1 }) + return true + elseif self.ability.banana then + card_eval_status_text(self, "jokers", nil, nil, nil, { message = localize("k_safe_ex"), delay = 0.1 }) + return false + end + end + return false +end +function Card:set_banana(_banana) + self.ability.banana = _banana +end +function Card:set_pinned(_pinned) + self.pinned = _pinned +end + +--Gradients based on Balatrostuck code +local upd = Game.update +Cryptid.C = { + EXOTIC = { HEX("708b91"), HEX("1e9eba") }, + TWILIGHT = { HEX("0800ff"), HEX("aa00ff") }, + VERDANT = { HEX("00ff22"), HEX("f4ff57") }, + EMBER = { HEX("ff0000"), HEX("ffae00") }, + DAWN = { HEX("00aaff"), HEX("ff00e3") }, + HORIZON = { HEX("c8fd09"), HEX("1ee7d9") }, + BLOSSOM = { HEX("ff09da"), HEX("ffd121") }, + AZURE = { HEX("0409ff"), HEX("63dcff") }, + ASCENDANT = { HEX("2e00f5"), HEX("e5001d") }, + JOLLY = { HEX("6ec1f5"), HEX("456b84") }, +} +function Game:update(dt) + upd(self, dt) + local anim_timer = self.TIMERS.REAL * 1.5 + local p = 0.5 * (math.sin(anim_timer) + 1) + for k, c in pairs(Cryptid.C) do + if not G.C["CRY_" .. k] then + G.C["CRY_" .. k] = { 0, 0, 0, 0 } + end + for i = 1, 4 do + G.C["CRY_" .. k][i] = c[1][i] * p + c[2][i] * (1 - p) + end + end + G.C.RARITY["cry_exotic"] = G.C.CRY_EXOTIC + if Incantation and not CryptidIncanCompat then + AllowStacking("Code") + AllowDividing("Code") + CryptidIncanCompat = true + end +end + +local jokers = { + "j_gros_michel", + "j_egg", + "j_ice_cream", + "j_cavendish", + "j_turtle_bean", + "j_diet_cola", + "j_popcorn", + "j_ramen", + "j_selzer", +} +if Cryptid.enabled["Misc. Jokers"] then + jokers[#jokers + 1] = "j_cry_pickle" + jokers[#jokers + 1] = "j_cry_chili_pepper" +end +if Cryptid.enabled["Epic Jokers"] then + jokers[#jokers + 1] = "j_cry_oldcandy" + jokers[#jokers + 1] = "j_cry_caramel" +end +if Cryptid.enabled["M Jokers"] then + jokers[#jokers + 1] = "j_cry_foodm" +end +if Cryptid.enabled["Spooky"] then + jokers[#jokers + 1] = "j_cry_cotton_candy" + jokers[#jokers + 1] = "j_cry_wrapped" + jokers[#jokers + 1] = "j_cry_candy_cane" + jokers[#jokers + 1] = "j_cry_candy_buttons" + jokers[#jokers + 1] = "j_cry_jawbreaker" + jokers[#jokers + 1] = "j_cry_mellowcreme" + jokers[#jokers + 1] = "j_cry_brittle" +end +for i = 1, #jokers do + Cryptid.food[#Cryptid.food+1] = jokers[i] +end +function Cryptid.get_food(seed) + local food_keys = {} + for k, v in pairs(Cryptid.food) do + if not G.GAME.banned_keys[v] and G.P_CENTERS[v] then + table.insert(food_keys, v) + end + end + if #food_keys <= 0 then + return "j_reserved_parking" + else + return pseudorandom_element(food_keys, pseudoseed(seed)) + end +end +SMODS.Sound({ + key = "meow1", + path = "meow1.ogg", +}) +SMODS.Sound({ + key = "meow2", + path = "meow2.ogg", +}) +SMODS.Sound({ + key = "meow3", + path = "meow3.ogg", +}) +SMODS.Sound({ + key = "meow4", + path = "meow4.ogg", +}) +SMODS.Sound({ + key = "e_mosaic", + path = "e_mosaic.ogg", +}) +SMODS.Sound({ + key = "e_glitched", + path = "e_glitched.ogg", +}) +SMODS.Sound({ + key = "e_oversaturated", + path = "e_oversaturated.ogg", +}) +SMODS.Sound({ + key = "e_blur", + path = "e_blur.ogg", +}) +SMODS.Sound({ + key = "e_double_sided", + path = "e_double_sided.ogg", +}) +SMODS.Sound({ + key = "e_jolly", + path = "e_jolly.ogg", +}) +SMODS.Sound({ + key = "e_noisy", + path = "e_noisy.ogg", +}) +SMODS.Sound({ + key = "e_fragile", + path = "e_fragile.ogg", +}) +SMODS.Sound({ + key = "e_golden", + path = "e_golden.ogg", +}) +SMODS.Sound({ + key = "studiofromhelsinki", + path = "studiofromhelsinki.ogg", +}) +SMODS.Sound({ + key = "music_jimball", + path = "music_jimball.ogg", + sync = false, + pitch = 1, + select_music_track = function() + return next(find_joker("cry-Jimball")) and Cryptid_config.Cryptid.jimball_music and 1.57e308 + end, +}) +SMODS.Sound({ + key = "music_code", + path = "music_code.ogg", + select_music_track = function() + return Cryptid_config.Cryptid.code_music + and ( + ( + G.pack_cards + and G.pack_cards.cards + and G.pack_cards.cards[1] + and G.pack_cards.cards[1].ability.set == "Code" + ) or (G.GAME and G.GAME.USING_CODE) + ) + end, +}) +SMODS.Sound({ + key = "music_big", + path = "music_big.ogg", + select_music_track = function() + return Cryptid_config.Cryptid.big_music and to_big(G.GAME.round_scores["hand"].amt) > to_big(10) ^ 1000000 + end, +}) +SMODS.Sound({ + key = "music_exotic", + path = "music_exotic.ogg", + volume = 0.4, + select_music_track = function() + return Cryptid_config.Cryptid.exotic_music and cry_has_exotic() + end, +}) + +--Requires Malverk Mod +if (SMODS.Mods["malverk"] or {}).can_load then + AltTexture({ + key = 'jolly_jokers', + set = 'Joker', + path = 'jolly.png', + loc_txt = { + name = 'Jolly Jokers' + } + }) + TexturePack{ -- HD Texture Pack + key = 'jolly_texture', + textures = { + 'cry_jolly_jokers', + }, + loc_txt = { + name = 'Jolly', + text = { + 'Jolly Jokers lmao', + 'Art by B' + } + } + } +end +--Make Ortalab's Locked jokers not show up on Deck of Equilibrium and Antimatter Deck +if (SMODS.Mods["ortalab"] or {}).can_load then + for i = 1, 150 do + print(i) + SMODS.Joker:take_ownership('ortalab_temp_' .. i, { + name = "Cry-skibidi", + no_doe = true + }) + end +end +SMODS.Atlas({ + key = "modicon", + path = "cry_icon.png", + px = 32, + py = 32, +}):register() +SMODS.Atlas({ + key = "placeholders", + path = "placeholders.png", + px = 71, + py = 95, +}):register() +SMODS.Atlas({ + key = "atlasepic", + path = "atlasepic.png", + px = 71, + py = 95, +}):register() +SMODS.Atlas({ + key = "atlasone", + path = "atlasone.png", + px = 71, + py = 95, +}):register() +SMODS.Atlas({ + key = "atlastwo", + path = "atlastwo.png", + px = 71, + py = 95, +}):register() +SMODS.Atlas({ + key = "atlasthree", + path = "atlasthree.png", + px = 71, + py = 95, +}):register() +SMODS.Atlas({ + key = "atlasspooky", + path = "atlasspooky.png", + px = 71, + py = 95, +}):register() +SMODS.Atlas({ + key = "atlasexotic", + path = "atlasexotic.png", + px = 71, + py = 95, +}):register() +SMODS.Atlas({ + key = "atlasnotjokers", --this is easier to spell then consumables + path = "atlasnotjokers.png", + px = 71, + py = 95, +}):register() +SMODS.Atlas({ + key = "tag_cry", + path = "tag_cry.png", + px = 34, + py = 34, +}):register() +--Enchancements, seals, other misc things etc +SMODS.Atlas({ + key = "cry_misc", + path = "cry_misc.png", + px = 71, + py = 95, +}):register() +SMODS.Sticker:take_ownership("perishable", { + atlas = "sticker", + pos = { x = 4, y = 4 }, + prefix_config = { key = false }, + loc_vars = function(self, info_queue, card) + if card.ability.consumeable then + return { key = "cry_perishable_consumeable" } + elseif card.ability.set == "Voucher" then + return { + key = "cry_perishable_voucher", + vars = { + G.GAME.cry_voucher_perishable_rounds or 1, + card.ability.perish_tally or G.GAME.cry_voucher_perishable_rounds, + }, + } + elseif card.ability.set == "Booster" then + return { key = "cry_perishable_booster" } + else + return { vars = { G.GAME.perishable_rounds or 1, card.ability.perish_tally or G.GAME.perishable_rounds } } + end + end, +}) +SMODS.Sticker:take_ownership("pinned", { + atlas = "sticker", + pos = { x = 5, y = 0 }, + prefix_config = { key = false }, + loc_vars = function(self, info_queue, card) + if card.ability.consumeable then + return { key = "cry_pinned_consumeable" } -- this doesn't work. i want this to work :( + elseif card.ability.set == "Voucher" then + return { key = "cry_pinned_voucher" } + elseif card.ability.set == "Booster" then + return { key = "cry_pinned_booster" } + end + end, +}) +SMODS.Sticker:take_ownership("eternal", { + loc_vars = function(self, info_queue, card) + if card.ability.set == "Voucher" then + return { key = "cry_eternal_voucher" } + elseif card.ability.set == "Booster" then + return { key = "cry_eternal_booster" } + end + end, +}) +SMODS.Sticker:take_ownership("rental", { + loc_vars = function(self, info_queue, card) + if card.ability.consumeable then + return { key = "cry_rental_consumeable", vars = { G.GAME.cry_consumeable_rental_rate or 1 } } + elseif card.ability.set == "Voucher" then + return { key = "cry_rental_voucher", vars = { G.GAME.cry_voucher_rental_rate or 1 } } + elseif card.ability.set == "Booster" then + return { key = "cry_rental_booster" } + else + return { vars = { G.GAME.rental_rate or 1 } } + end + end, +}) + +--Sticker calc for playing cards +local ec = eval_card +function eval_card(card, context) + local ret = ec(card, context) + if card and card.area == G.hand or card.area == G.play or card.area == G.discard or card.area == G.deck then + for k, v in pairs(SMODS.Stickers) do + if card.ability[k] and v.calculate and type(v.calculate) == "function" then + context.from_playing_card = true + context.ret = ret + v:calculate(card, context) + end + end + end + return ret +end +function create_cryptid_notif_overlay(key) + if not G.SETTINGS.cryptid_notifs then -- I want this to be across profiles + G.SETTINGS.cryptid_notifs = {} + end + if not G.SETTINGS.cryptid_notifs[key] then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + no_delete = true, + func = (function() + if not G.OVERLAY_MENU then + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_cryptid_notif(key), + } + play_sound('foil1', 0.7, 0.3) + play_sound('gong', 1.4, 0.15) + G.SETTINGS.cryptid_notifs[key] = true + G:save_settings() + return true + end + end) + }), 'unlock') + end +end + +function create_UIBox_cryptid_notif(key) + local t = create_UIBox_generic_options({padding = 0,back_label = localize('b_continue'), no_pip = true, snap_back = true, back_func = 'continue_unlock', minw = 4.5, contents = { + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('cry_notif_'..key..'_1')}, colours = {G.C.BLUE},shadow = true, rotate = true, bump = true, pop_in = 0.3, pop_in_rate = 2, scale = 1.2})}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('cry_notif_'..key..'_2')}, colours = {G.C.RED},shadow = true, rotate = true, bump = true, pop_in = 0.6, pop_in_rate = 2, scale = 0.8})}} + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.2}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05, emboss = 0.05, colour = G.C.WHITE, r = 0.1}, nodes={ + Cryptid.notifications[key].nodes() + }} + }} + }}, + Cryptid.notifications[key].cta and {n=G.UIT.R, config={id = 'overlay_menu_back_button', align = "cm", minw = 2.5, padding =0.1, r = 0.1, hover = true, colour = G.C.BLUE, button = "notif_"..key, shadow = true, focus_args = {nav = 'wide', button = 'b'}}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.T, config={text = localize(Cryptid.notifications[key].cta.label), scale = 0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true, func = 'set_button_pip', focus_args = {button = 'b'}}} + }} + }} or nil + }} + }}) + return t + end + +-- I couldn't figure out how to use localization for this, so this implementation is pretty scuffed +Cryptid.notifications = { + jimball = { + nodes = function() return {n=G.UIT.R, config={align = "cm", colour = empty and G.C.CLEAR or G.C.UI.BACKGROUND_WHITE, r = 0.1, padding = 0.04, minw = 2, minh = 0.8, emboss = not empty and 0.05 or nil, filler = true}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.03}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize('cry_notif_jimball_d1'), scale = 0.5, colour = G.C.BLACK}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize('cry_notif_jimball_d2'), scale = 0.5, colour = G.C.BLACK}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize('cry_notif_jimball_d3'), scale = 0.5, colour = G.C.BLACK}}, + }}, + }} + }} end, + cta = { + label = "k_disable_music" + } + } +} +---------------------------------------------- +------------MOD CODE END---------------------- diff --git a/Cryptid/Items/Achievements.lua b/Cryptid/Items/Achievements.lua new file mode 100644 index 0000000..03b68af --- /dev/null +++ b/Cryptid/Items/Achievements.lua @@ -0,0 +1,450 @@ +local achievement_atlas = { + object_type = "Atlas", + key = "achievements", + path = "cry_achievements.png", + px = 66, + py = 66, +} + +local break_infinity = { + object_type = "Achievement", + key = "break_infinity", + order = 1, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "chip_score" and to_big(args.chips) >= to_big(2) ^ to_big(1024) then + return true + end + end, +} + +local used_crash = { + object_type = "Achievement", + key = "used_crash", + order = 2, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if + args.type == "ach_cry_used_crash" + and G.PROFILES[G.SETTINGS.profile].consumeable_usage["c_cry_crash"] + and G.PROFILES[G.SETTINGS.profile].consumeable_usage["c_cry_crash"].count > 0 + then + return true + end + end, +} + +local haxxor = { + object_type = "Achievement", + key = "haxxor", + order = 3, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "cheat_used" then + return true + end + end, +} + +local googol_play_pass = { + object_type = "Achievement", + key = "googol_play_pass", + order = 4, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "googol_play_rigged" then + return true + end + end, +} + +local bullet_hell = { + object_type = "Achievement", + key = "bullet_hell", + order = 5, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "modify_jokers" then + local ap_joker_count = 0 + + if G.jokers then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].config.center.key == "j_cry_apjoker" then + ap_joker_count = ap_joker_count + 1 + end + end + end + + if ap_joker_count >= 15 then + return true + end + end + end, +} + +local what_have_you_done = { + object_type = "Achievement", + key = "what_have_you_done", + order = 6, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "what_have_you_done" then + return true + end + end, +} + +local cryptid_the_cryptid = { + object_type = "Achievement", + key = "cryptid_the_cryptid", + order = 7, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "cryptid_the_cryptid" then + return true + end + end, +} + +local niw_uoy = { + object_type = "Achievement", + key = "niw_uoy", + order = 8, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "ante_up" and args.ante <= -8 then + return true + end + end, +} + +local jokes_on_you = { + object_type = "Achievement", + key = "jokes_on_you", + order = 9, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) --NOTE: Might be buggy due to G.GAME saving + if args.type == "win" and G.GAME.cry_ach_conditions.the_jokes_on_you_triggered == true then + return true + end + end, +} + +local now_the_fun_begins = { + object_type = "Achievement", + key = "now_the_fun_begins", + order = 10, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "modify_jokers" then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].config.center.key == "j_cry_canvas" then + return true + end + end + end + end, +} + +local blurred_blurred_joker = { + object_type = "Achievement", + key = "blurred_blurred_joker", + order = 11, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "modify_jokers" then + for i = 1, #G.jokers.cards do + if + G.jokers.cards[i].config.center.key == "j_cry_blurred" + and (G.jokers.cards[i].edition and G.jokers.cards[i].edition.cry_blur) + then + return true + end + end + end + end, +} + +local exodia = { + object_type = "Achievement", + key = "exodia", + order = 12, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "modify_jokers" then + local exotic_count = 0 + + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].config.center.rarity == "cry_exotic" then + exotic_count = exotic_count + 1 + end + end + + if exotic_count >= 5 then + return true + end + end + end, +} + +local freak_house = { + object_type = "Achievement", + key = "freak_house", + order = 13, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) -- NOTE: I hate doing checks like this. Unscuff later + if args.type == "hand" then + -- Do you have Nice + local has_nice + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].config.center.key == "j_cry_nice" then + has_nice = true + end + end + + --Is it a hearts flush house + local total_6s = 0 + local total_9s = 0 + local total_hearts = 0 + for k, v in ipairs(args.scoring_hand) do + if v:is_suit("Hearts", nil, true) then + total_hearts = total_hearts + 1 + end + if v:get_id() == 6 then + total_6s = total_6s + 1 + elseif v:get_id() == 9 then + total_9s = total_9s + 1 + end + end + + if + has_nice + and ((total_6s == 3 and total_9s == 2) or (total_6s == 2 and total_9s == 3)) + and total_hearts == 5 + and args.disp_text == "Flush House" + then + return true + end + end + end, +} + +local ult_full_skip = { + object_type = "Achievement", + key = "ult_full_skip", + order = 14, + bypass_all_unlocked = true, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "win" and G.GAME.round == 1 then + return true + end + end, +} + +local patience_virtue = { + object_type = "Achievement", + key = "patience_virtue", + order = 15, + bypass_all_unlocked = true, + hidden_text = true, + pos = { x = 2, y = 0 }, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "round_win" then + if + G.GAME.blind.config.blind.key == "bl_cry_lavender_loop" + and G.GAME.cry_ach_conditions.patience_virtue_earnable == true + then + return true + end + if G.GAME.cry_ach_conditions.patience_virtue_earnable then + G.GAME.cry_ach_conditions.patience_virtue_earnable = nil + end + if G.GAME.cry_ach_conditions.patience_virtue_timer then + G.GAME.cry_ach_conditions.patience_virtue_timer = nil + end + end + end, +} + +local pull_request = { + object_type = "Achievement", + key = "pull_request", + order = 16, + bypass_all_unlocked = true, + hidden_text = true, + pos = { x = 2, y = 0 }, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "pr_unlock" then + return true + end + end, +} + +local ace_through_crash = { + object_type = "Achievement", + key = "ace_in_crash", + order = 17, + bypass_all_unlocked = true, + pos = { x = 2, y = 0 }, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "ace_in_crash" then + return true + end + end, +} + +local home_realtor = { + object_type = "Achievement", + key = "home_realtor", + order = 18, + bypass_all_unlocked = true, + hidden_text = true, + pos = { x = 2, y = 0 }, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + --todo: check for doe/antimatter sleeves + if args.type == "home_realtor" then + return true + end + end, +} + +local traffic_jam = { + object_type = "Achievement", + key = "traffic_jam", + order = 19, + bypass_all_unlocked = true, + hidden_text = true, + pos = { x = 2, y = 0 }, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "win_challenge" or args.type == "win_challenge_startup" then + local rush_hours_beaten = 0 + + if G.PROFILES[G.SETTINGS.profile].challenge_progress.completed["c_cry_rush_hour"] then + rush_hours_beaten = rush_hours_beaten + 1 + end + if G.PROFILES[G.SETTINGS.profile].challenge_progress.completed["c_cry_rush_hour_ii"] then + rush_hours_beaten = rush_hours_beaten + 1 + end + if G.PROFILES[G.SETTINGS.profile].challenge_progress.completed["c_cry_rush_hour_iii"] then + rush_hours_beaten = rush_hours_beaten + 1 + end + + if rush_hours_beaten == 3 then + return true + end + end + end, +} + +local perfectly_balanced = { + object_type = "Achievement", + key = "perfectly_balanced", + order = 20, + bypass_all_unlocked = true, + hidden_text = true, + pos = { x = 2, y = 0 }, + atlas = "cry_achievements", + --reset_on_startup = true, + unlock_condition = function(self, args) + if args.type == "win_stake" or args.type == "win_stake_startup" then + if + (G.GAME.selected_back.effect.center.key == "b_cry_very_fair" and G.GAME.stake == 32) + or get_deck_win_stake("b_cry_very_fair") == 32 + then + return true + end + end + end, +} + +-- TODO: Add new Achievements. +-- Current Ideas (Normal): +-- Cry: Win a run with only Sob and Obelisk +-- Overtuned: Have any Glitched item give either 100x or 0.01x its original values +-- Current Ideas (Platinum): + +-- Implemented (Normal) +-- Break Infinity: Score more than 1.57e308 in one hand +-- H4xx0r: Use a cheat code +-- We Told You Not To: Use ://CRASH +-- Googol Play Pass: Rig a Googol Play Card +-- Bullet Hell: Have 15 copies of AP Joker +-- !niW uoY: Reach Ante -8 +-- Now the Fun Begins: Obtain Canvas +-- Exodia: Have 5 Exotic Jokers +-- WHAT HAVE YOU DONE: Delete or Sacrifice an Exotic Joker +-- Joke's on You, Pal!: Trigger The Joke's effect on Ante 1 and win the run +-- Freak House: Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice +-- Ultimate Full Skip: Win in 1 round +-- Legally Blind: Obtain a Blurred Blurred Joker +-- Cryptid the Cryptid: Use Cryptid on Cryptid +-- Implemented (Platinum) +-- Patience is a Virtue: Wait out Lavender Loop for 2 minutes before playing first hand and beat it +-- Pull Request: Have ://COMMIT spawn the same Joker that it destroyed +-- Pocket ACE: Only unlockable through using ACE to unlock +-- Home Realtor: Activate Happy House before Ante 8 (without DoE/Antimatter) +-- Traffic Jam: Win all Rush Hour challenges +-- Perfectly Balanced: Beat Very Fair Deck on Ascendant Stake + +local achievement_objects = { + achievement_atlas, + break_infinity, + bullet_hell, + cryptid_the_cryptid, + now_the_fun_begins, + blurred_blurred_joker, + exodia, + what_have_you_done, + used_crash, + haxxor, + googol_play_pass, + pull_request, + niw_uoy, + jokes_on_you, + freak_house, + ult_full_skip, + patience_virtue, + ace_through_crash, + home_realtor, + traffic_jam, + perfectly_balanced, +} +return { name = "Achievements", init = function() end, items = achievement_objects } diff --git a/Cryptid/Items/Antimatter.lua b/Cryptid/Items/Antimatter.lua new file mode 100644 index 0000000..243028e --- /dev/null +++ b/Cryptid/Items/Antimatter.lua @@ -0,0 +1,214 @@ +local blank = { + object_type = "Back", + name = "cry-Blank", + key = "blank", + order = 75, + pos = { x = 1, y = 0 }, + atlas = "blank", +} +local blank_sprite = { + object_type = "Atlas", + key = "blank", + path = "atlasdeck.png", + px = 71, + py = 95, +} +local antimatter = { + object_type = "Back", + name = "cry-Antimatter", + order = 76, + key = "antimatter", + config = { + cry_antimatter = true, + discards = 1, --Red Deck: 1 + hands = 1, --Blue Deck: 1 + dollars = 10, --Yellow Deck + extra_hand_bonus = 2, + extra_discard_bonus = 1, --Green Deck + joker_slot = 1, --Black Deck: 1 + vouchers = { + "v_crystal_ball", + "v_telescope", + "v_tarot_merchant", + "v_planet_merchant", + "v_overstock_norm", + "v_overstock_plus", + }, --Vouchers from all decks + consumables = { "c_fool", "c_fool", "c_hex" }, --Consumables from all decks + spectral_rate = 2, --Ghost Deck + remove_faces = true, --Abandoned Deck + hand_size = 3, --Painted Deck & Infinite deck + randomize_rank_suit = true, --Erratic Deck + cry_equilibrium = true, --Deck of Equilibrium + cry_misprint_min = 1, + cry_misprint_max = 10, --Misprint Deck + cry_highlight_limit = 1e20, --Infinite Deck + cry_wormhole = true, + cry_negative_rate = 20, --Wormhole Deck + cry_redeemed = true, --Redeemed Deck + cry_crit_rate = 0.25, --Critical Deck + cry_encoded = true, --Encoded Deck + cry_legendary = true, + cry_legendary_rate = 0.2, --Legendary Deck + cry_spooky = true, --Spooky Deck + cry_curse_rate = 0, + -- Enhanced Decks + cry_force_enhancement = "random", + cry_force_edition = "random", + cry_force_seal = "random", + cry_boss_blocked = { "bl_goad", "bl_window", "bl_club", "bl_head" }, + cry_forced_draw_amount = 5, + }, + pos = { x = 2, y = 0 }, + trigger_effect = function(self, args) + if args.context == "final_scoring_step" then + --Critical Deck + local crit_poll = pseudorandom(pseudoseed("cry_critical")) + crit_poll = crit_poll / (G.GAME.probabilities.normal or 1) + if crit_poll < self.config.cry_crit_rate then + args.mult = args.mult ^ 2 + update_hand_text({ delay = 0 }, { mult = args.mult, chips = args.chips }) + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("talisman_emult", 1) + attention_text({ + scale = 1.4, + text = localize("cry_critical_hit_ex"), + hold = 4, + align = "cm", + offset = { x = 0, y = -1.7 }, + major = G.play, + }) + return true + end, + })) + end + delay(0.6) + --Plasma Deck + local tot = args.chips + args.mult + args.chips = math.floor(tot / 2) + args.mult = math.floor(tot / 2) + update_hand_text({ delay = 0 }, { mult = args.mult, chips = args.chips }) + + G.E_MANAGER:add_event(Event({ + func = function() + local text = localize("k_balanced") + play_sound("gong", 0.94, 0.3) + play_sound("gong", 0.94 * 1.5, 0.2) + play_sound("tarot1", 1.5) + ease_colour(G.C.UI_CHIPS, { 0.8, 0.45, 0.85, 1 }) + ease_colour(G.C.UI_MULT, { 0.8, 0.45, 0.85, 1 }) + attention_text({ + scale = 1.4, + text = text, + hold = 2, + align = "cm", + offset = { x = 0, y = -2.7 }, + major = G.play, + }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + blockable = false, + blocking = false, + delay = 4.3, + func = function() + ease_colour(G.C.UI_CHIPS, G.C.BLUE, 2) + ease_colour(G.C.UI_MULT, G.C.RED, 2) + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + blockable = false, + blocking = false, + no_delete = true, + delay = 6.3, + func = function() + G.C.UI_CHIPS[1], G.C.UI_CHIPS[2], G.C.UI_CHIPS[3], G.C.UI_CHIPS[4] = + G.C.BLUE[1], G.C.BLUE[2], G.C.BLUE[3], G.C.BLUE[4] + G.C.UI_MULT[1], G.C.UI_MULT[2], G.C.UI_MULT[3], G.C.UI_MULT[4] = + G.C.RED[1], G.C.RED[2], G.C.RED[3], G.C.RED[4] + return true + end, + })) + return true + end, + })) + + delay(0.6) + return args.chips, args.mult + end + --Glowing Deck & Legendary Deck + if args.context == "eval" and G.GAME.last_blind and G.GAME.last_blind.boss then + --Glowing Deck + for i = 1, #G.jokers.cards do + cry_with_deck_effects(G.jokers.cards[i], function(card) + cry_misprintize(card, { min = 1.25, max = 1.25 }, nil, true) + end) + end + --Legendary Deck + if G.jokers then + if #G.jokers.cards < G.jokers.config.card_limit then + local legendary_poll = pseudorandom(pseudoseed("cry_legendary")) + legendary_poll = legendary_poll / (G.GAME.probabilities.normal or 1) + if legendary_poll < self.config.cry_legendary_rate then + local card = create_card("Joker", G.jokers, true, 4, nil, nil, nil, "") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + return true + else + card_eval_status_text( + G.jokers, + "jokers", + nil, + nil, + nil, + { message = localize("k_nope_ex"), colour = G.C.RARITY[4] } + ) + end + else + card_eval_status_text( + G.jokers, + "jokers", + nil, + nil, + nil, + { message = localize("k_no_room_ex"), colour = G.C.RARITY[4] } + ) + end + end + --Anaglyph Deck + G.E_MANAGER:add_event(Event({ + func = (function() + add_tag(Tag('tag_double')) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + return true + end) + })) + end + end, + apply = function(self) + --Checkered Deck + G.E_MANAGER:add_event(Event({ + func = function() + for k, v in pairs(G.playing_cards) do + if v.base.suit == 'Clubs' then + v:change_suit('Spades') + end + if v.base.suit == 'Diamonds' then + v:change_suit('Hearts') + end + end + return true + end + })) + end, + atlas = "blank", +} +return { + name = "Antimatter Deck", + init = function() end, + items = { blank_sprite, blank, antimatter }, +} diff --git a/Cryptid/Items/Blinds.lua b/Cryptid/Items/Blinds.lua new file mode 100644 index 0000000..b4941f5 --- /dev/null +++ b/Cryptid/Items/Blinds.lua @@ -0,0 +1,1474 @@ +--extra blind functions for use by bosses +function Blind:cry_ante_base_mod(dt) + if not self.disabled then + local obj = self.config.blind + if obj.cry_ante_base_mod and type(obj.cry_ante_base_mod) == "function" then + return obj:cry_ante_base_mod(dt) + end + end + return 0 +end +function Blind:cry_round_base_mod(dt) + if not self.disabled then + local obj = self.config.blind + if obj.cry_round_base_mod and type(obj.cry_round_base_mod) == "function" then + return obj:cry_round_base_mod(dt) + end + end + return 1 +end +function Blind:cry_cap_score(score) + if not self.disabled then + local obj = self.config.blind + if obj.cry_cap_score and type(obj.cry_cap_score) == "function" then + return obj:cry_cap_score(score) + end + end + return score +end +function Blind:cry_after_play() + if not self.disabled then + local obj = self.config.blind + if obj.cry_after_play and type(obj.cry_after_play) == "function" then + return obj:cry_after_play() + end + end +end +function Blind:cry_before_play() + if not self.disabled then + local obj = self.config.blind + if obj.cry_before_play and type(obj.cry_before_play) == "function" then + return obj:cry_before_play() + end + end +end +function Blind:cry_calc_ante_gain() + if G.GAME.modifiers.cry_spooky then --here is the best place to check when spooky should apply + local card + if pseudorandom(pseudoseed("cry_spooky_curse")) < G.GAME.modifiers.cry_curse_rate then + card = create_card("Joker", G.jokers, nil, "cry_cursed", nil, nil, nil, "cry_spooky") + else + card = create_card("Joker", G.jokers, nil, "cry_candy", nil, nil, nil, "cry_spooky") + end + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + end + if not self.disabled then + local obj = self.config.blind + if obj.cry_calc_ante_gain and type(obj.cry_calc_ante_gain) == "function" then + return obj:cry_calc_ante_gain() + end + end + return 1 +end + +local oldox = { + object_type = "Blind", + name = "cry-oldox", + key = "oldox", + pos = { x = 0, y = 0 }, + boss = { + min = 2, + max = 10, + }, + atlas = "nostalgia", + order = 4, + boss_colour = HEX("4f6367"), + modify_hand = function(self, cards, poker_hands, text, mult, hand_chips) + if to_big(hand_chips) ~= to_big(0) then + G.GAME.blind.triggered = true + return mult, to_big(0), true + end + return mult, to_big(0), false + end, +} +local oldhouse = { + object_type = "Blind", + name = "cry-oldhouse", + key = "oldhouse", + pos = { x = 0, y = 2 }, + boss = { + min = 3, + max = 10, + }, + atlas = "nostalgia", + order = 5, + boss_colour = HEX("4f6367"), + debuff_hand = function(self, cards, hand, handname, check) + if handname == "Full House" and not G.GAME.blind.disabled then + G.GAME.blind.triggered = true + return true + end + return false + end, + get_loc_debuff_text = function(self) + return localize("cry_debuff_oldhouse") + end, +} +local oldarm = { + object_type = "Blind", + name = "cry-oldarm", + key = "oldarm", + pos = { x = 0, y = 3 }, + boss = { + min = 3, + max = 10, + }, + atlas = "nostalgia", + order = 6, + boss_colour = HEX("4f6367"), + debuff_hand = function(self, cards, hand, handname, check) + if #cards > 4 and not G.GAME.blind.disabled then + G.GAME.blind.triggered = true + return true + end + return false + end, + get_loc_debuff_text = function(self) + return localize("cry_debuff_oldarm") + end, +} +local oldfish = { + object_type = "Blind", + name = "cry-oldfish", + key = "oldfish", + pos = { x = 0, y = 4 }, + boss = { + min = 2, + max = 10, + }, + atlas = "nostalgia", + order = 7, + boss_colour = HEX("4f6367"), + modify_hand = function(self, cards, poker_hands, text, mult, hand_chips) + if to_big(mult) ~= to_big(1) then + G.GAME.blind.triggered = true + return to_big(1), hand_chips, true + end + return to_big(1), hand_chips, false + end, +} +local oldmanacle = { + object_type = "Blind", + name = "cry-oldmanacle", + key = "oldmanacle", + pos = { x = 0, y = 5 }, + boss = { + min = 1, + max = 10, + }, + atlas = "nostalgia", + order = 8, + boss_colour = HEX("4f6367"), + modify_hand = function(self, cards, poker_hands, text, mult, hand_chips) + if G.GAME.current_round.discards_left > 1 then + G.GAME.blind.triggered = true + return math.floor(mult / G.GAME.current_round.discards_left), hand_chips, true + end + return mult, hand_chips, false + end, +} +local oldserpent = { + object_type = "Blind", + name = "cry-oldserpent", + key = "oldserpent", + pos = { x = 0, y = 6 }, + boss = { + min = 5, + max = 10, + }, + atlas = "nostalgia", + order = 9, + boss_colour = HEX("4f6367"), + modify_hand = function(self, cards, poker_hands, text, mult, hand_chips) + if G.GAME.hands[text].level > 1 then + G.GAME.blind.triggered = true + return math.floor(mult / G.GAME.hands[text].level), hand_chips, true + end + return mult, hand_chips, false + end, +} +local oldpillar = { + object_type = "Blind", + name = "cry-oldpillar", + key = "oldpillar", + pos = { x = 0, y = 7 }, + boss = { + min = 3, + max = 10, + }, + atlas = "nostalgia", + order = 10, + boss_colour = HEX("4f6367"), + debuff_hand = function(self, cards, hand, handname, check) + if handname == "Straight" and not G.GAME.blind.disabled then + G.GAME.blind.triggered = true + return true + end + return false + end, + get_loc_debuff_text = function(self) + return localize("cry_debuff_oldpillar") + end, +} +local oldflint = { + object_type = "Blind", + name = "cry-oldflint", + key = "oldflint", + pos = { x = 0, y = 8 }, + boss = { + min = 3, + max = 10, + }, + atlas = "nostalgia", + order = 11, + boss_colour = HEX("4f6367"), + debuff_hand = function(self, cards, hand, handname, check) + if handname == "Flush" and not G.GAME.blind.disabled then + G.GAME.blind.triggered = true + return true + end + return false + end, + get_loc_debuff_text = function(self) + return localize("cry_debuff_oldflint") + end, +} +local oldmark = { + object_type = "Blind", + name = "cry-oldmark", + key = "oldmark", + pos = { x = 0, y = 1 }, + boss = { + min = 4, + max = 10, + }, + atlas = "nostalgia", + order = 12, + boss_colour = HEX("4f6367"), + debuff_hand = function(self, cards, hand, handname, check) + if next(hand["Pair"]) then + G.GAME.blind.triggered = true + return true + end + return false + end, + get_loc_debuff_text = function(self) + return localize("cry_debuff_oldmark") + end, +} +local tax = { + object_type = "Blind", + name = "cry-Tax", + key = "tax", + pos = { x = 0, y = 0 }, + boss = { + min = 1, + max = 10, + }, + atlas = "blinds", + order = 2, + boss_colour = HEX("40ff40"), + cry_cap_score = function(self, score) + return math.floor(math.min(0.4 * G.GAME.blind.chips, score) + 0.5) + end, + in_pool = function() + return G.GAME.round_resets.hands >= 3 + end, +} +local box = { + object_type = "Blind", + name = "cry-box", + key = "box", + pos = { x = 0, y = 8 }, + boss = { + min = 1, + max = 10, + }, + atlas = "blinds", + order = 13, + boss_colour = HEX("883a3b"), + recalc_debuff = function(self, card, from_blind) + if (card.area == G.jokers) and not G.GAME.blind.disabled and card.config.center.rarity == 1 then + return true + end + return false + end, +} +local clock = { + object_type = "Blind", + name = "cry-Clock", + key = "clock", + pos = { x = 0, y = 1 }, + mult = 0, + boss = { + min = 1, + max = 10, + }, + config = { + tw_bl = { + ignore = true, + }, + }, + atlas = "blinds", + order = 3, + boss_colour = HEX("853455"), + defeat = function(self, silent) + G.P_BLINDS.bl_cry_clock.mult = 0 + end, + disable = function(self, silent) + G.GAME.blind.chips = get_blind_amount(G.GAME.round_resets.ante) * G.GAME.starting_params.ante_scaling * 2 + G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) + end, + cry_ante_base_mod = function(self, dt) + return 0.1 * dt / 3 + end, +} +local trick = { + object_type = "Blind", + name = "cry-Trick", + key = "trick", + pos = { x = 0, y = 3 }, + boss = { + min = 1, + max = 10, + }, + atlas = "blinds", + order = 14, + boss_colour = HEX("babd24"), + cry_after_play = function(self) + --flip and shuffle all cards held in hand + for k, v in ipairs(G.hand.cards) do + if v.facing == "front" then + v:flip() + end + end + --[[if #G.hand.cards > 1 then + G.E_MANAGER:add_event(Event({ trigger = 'after', delay = 0.2, func = function() + G.E_MANAGER:add_event(Event({ func = function() G.hand:shuffle('cry_trick'); play_sound('cardSlide1', 0.85);return true end })) + delay(0.15) + G.E_MANAGER:add_event(Event({ func = function() G.hand:shuffle('cry_trick'); play_sound('cardSlide1', 1.15);return true end })) + delay(0.15) + G.E_MANAGER:add_event(Event({ func = function() G.hand:shuffle('cry_trick'); play_sound('cardSlide1', 1);return true end })) + delay(0.5) + return true end })) + end--]] + end, +} + +local joke = { + object_type = "Blind", + name = "cry-Joke", + key = "joke", + pos = { x = 0, y = 4 }, + boss = { + min = 1, + max = 10, + }, + atlas = "blinds", + order = 15, + boss_colour = HEX("00ffaa"), + loc_vars = function(self, info_queue, card) + return { vars = { G.GAME.win_ante or 8 } } + end, + cry_calc_ante_gain = function(self) + if to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) * 2 then + if G.GAME.round_resets.ante == 1 then + G.GAME.cry_ach_conditions.the_jokes_on_you_triggered = true + end + return G.GAME.win_ante - G.GAME.round_resets.ante % G.GAME.win_ante + end + return 1 + end, +} +local hammer = { + object_type = "Blind", + name = "cry-hammer", + key = "hammer", + pos = { x = 0, y = 9 }, + boss = { + min = 2, + max = 10, + }, + atlas = "blinds", + order = 19, + boss_colour = HEX("ffabd6"), + recalc_debuff = function(self, card, from_blind) + if card.area ~= G.jokers and not G.GAME.blind.disabled then + if + card.ability.effect ~= "Stone Card" + and ( + card.base.value == "3" + or card.base.value == "5" + or card.base.value == "7" + or card.base.value == "9" + or card.base.value == "Ace" + ) + then + return true + end + return false + end + end, +} +local magic = { + object_type = "Blind", + name = "cry-magic", + key = "magic", + pos = { x = 0, y = 12 }, + boss = { + min = 2, + max = 10, + }, + atlas = "blinds", + order = 20, + boss_colour = HEX("009eff"), + recalc_debuff = function(self, card, from_blind) + if card.area ~= G.jokers and not G.GAME.blind.disabled then + if + card.ability.effect ~= "Stone Card" + and ( + card.base.value == "2" + or card.base.value == "4" + or card.base.value == "6" + or card.base.value == "8" + or card.base.value == "10" + ) + then + return true + end + return false + end + end, +} +local windmill = { + object_type = "Blind", + name = "cry-windmill", + key = "windmill", + pos = { x = 0, y = 10 }, + boss = { + min = 4, + max = 10, + }, + atlas = "blinds", + order = 16, + boss_colour = HEX("f70000"), + recalc_debuff = function(self, card, from_blind) + if (card.area == G.jokers) and not G.GAME.blind.disabled and card.config.center.rarity == 2 then + return true + end + return false + end, +} +local striker = { + object_type = "Blind", + name = "cry-striker", + key = "striker", + pos = { x = 0, y = 13 }, + boss = { + min = 4, + max = 10, + }, + atlas = "blinds", + order = 1, + boss_colour = HEX("505e5c"), + recalc_debuff = function(self, card, from_blind) + if (card.area == G.jokers) and not G.GAME.blind.disabled and card.config.center.rarity == 3 then + return true + end + return false + end, +} +local shackle = { + object_type = "Blind", + name = "cry-shackle", + key = "shackle", + pos = { x = 0, y = 15 }, + boss = { + min = 1, + max = 10, + }, + atlas = "blinds", + order = 18, + boss_colour = HEX("010466"), + in_pool = function() + if not G.jokers then + return false + end + for i, j in pairs(G.jokers.cards) do + if j.edition and j.edition.negative == true then + return true + end + end + return false + end, + recalc_debuff = function(self, card, from_blind) + if (card.area == G.jokers) and not G.GAME.blind.disabled and card.edition and card.edition.negative == true then + return true + end + return false + end, +} +local pin = { + object_type = "Blind", + name = "cry-pin", + key = "pin", + pos = { x = 0, y = 14 }, + boss = { + min = 4, + max = 10, + }, + atlas = "blinds", + order = 17, + boss_colour = HEX("452703"), + in_pool = function() + if not G.jokers then + return false + end + for i, j in pairs(G.jokers.cards) do + if + not ((j.config.center.rarity == 1) or (j.config.center.rarity == 2) or (j.config.center.rarity == 3) or (j.config.center.rarity == 5)) + then + return true + end + end + return false + end, + recalc_debuff = function(self, card, from_blind) + if + (card.area == G.jokers) + and not G.GAME.blind.disabled + and (card.config.center.rarity ~= 3 and card.config.center.rarity ~= 2 and card.config.center.rarity ~= 1 and card.config.center.rarity ~= 5) + then + return true + end + return false + end, +} + +--It seems Showdown blind order is seperate from normal blind collection order? convenient for me at least +--Nvm they changed it + +local pinkbow = { --TODO: Add effect for this later. NOTE TO SELF: DO NOT FORGET!!! + object_type = "Blind", + name = "cry-pinkbow", + key = "pinkbow", + pos = { x = 0, y = 11 }, + dollars = 8, + boss = { + min = 3, + max = 10, + showdown = true, + }, + atlas = "blinds", + boss_colour = HEX("ff00cc"), +} +local lavender_loop = { + object_type = "Blind", + name = "cry-Lavender Loop", + key = "lavender_loop", + pos = { x = 0, y = 2 }, + mult = 1, + dollars = 8, + boss = { + min = 3, + max = 10, + showdown = true, + }, + atlas = "blinds", + order = 91, + boss_colour = HEX("ae00ff"), + set_blind = function(self, reset, silent) + G.GAME.cry_ach_conditions.patience_virtue_timer = 120 + end, + disable = function(self, silent) + G.GAME.blind.chips = get_blind_amount(G.GAME.round_resets.ante) * G.GAME.starting_params.ante_scaling * 2 + G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) + G.GAME.cry_ach_conditions.patience_virtue_earnable = false + G.GAME.cry_ach_conditions.patience_virtue_earnable = nil + end, + cry_round_base_mod = function(self, dt) + if + G.GAME.cry_ach_conditions.patience_virtue_timer > 0 + and G.GAME.cry_ach_conditions.patience_virtue_earnable ~= true + then + G.GAME.cry_ach_conditions.patience_virtue_timer = G.GAME.cry_ach_conditions.patience_virtue_timer + - dt * (G.GAME.modifiers.cry_rush_hour_iii and 0.5 or 1) + elseif G.GAME.current_round.hands_played == 0 then + G.GAME.cry_ach_conditions.patience_virtue_earnable = true + end + return 1.25 ^ (dt / 1.5) + end, +} +local tornado = { + object_type = "Blind", + name = "cry-tornado", + key = "tornado", + pos = { x = 0, y = 16 }, + mult = 0.8, + dollars = 8, + boss = { + min = 3, + max = 10, + showdown = true, + }, + atlas = "blinds", + order = 94, + boss_colour = HEX("3dd9ca"), + loc_vars = function(self) + return { vars = { "" .. ((G.GAME and G.GAME.probabilities.normal or 1) * 2), 3 } } + end, + set_blind = function(self, reset, silent) + if not reset then + G.GAME.blind.tornado_guarantee = pseudorandom(pseudoseed("tornado"),1,G.GAME.round_resets.hands) + end + end, + in_pool = function() + if not G.jokers then + return true + end + for i, j in pairs(G.jokers.cards) do + if j.ability.name == "Oops! All 6s" and j.ability.eternal == true then + return false + end + end + return true + end, + collection_loc_vars = function(self) + return { vars = { "" .. ((G.GAME and G.GAME.probabilities.normal or 1) * 2), 3 } } + end, + debuff_hand = function(self, cards, hand, handname, check) + if + not check + and (pseudorandom(pseudoseed("tornado")) < ((G.GAME.probabilities.normal * 2) / 3)) + and not G.GAME.blind.disabled + then + --check for guarantee + if G.GAME.probabilities.normal <= 1 and G.GAME.current_round.hands_left+1 == G.GAME.blind.tornado_guarantee then + return false + end + + G.GAME.blind.triggered = true + return true + end + return false + end, +} +--todo: disable get_local_debuff_text for this +local vermillion_virus = { + object_type = "Blind", + name = "cry-Vermillion Virus", + key = "vermillion_virus", + pos = { x = 0, y = 5 }, + dollars = 8, + boss = { + min = 3, + max = 10, + showdown = true, + }, + atlas = "blinds", + order = 90, + boss_colour = HEX("f65d34"), + cry_before_play = function(self) + if G.jokers.cards[1] then + local idx = pseudorandom(pseudoseed("cry_vermillion_virus"), 1, #G.jokers.cards) + if G.jokers.cards[idx] then + if G.jokers.cards[idx].config.center.immune_to_vermillion then + card_eval_status_text(G.jokers.cards[idx], 'extra', nil, nil, nil, {message = localize('k_nope_ex'), colour = G.C.JOKER_GREY}) + else + _card = create_card("Joker", G.jokers, nil, nil, nil, nil, nil, "cry_vermillion_virus_gen") + G.jokers.cards[idx]:remove_from_deck() + _card:add_to_deck() + _card:start_materialize() + G.jokers.cards[idx] = _card + _card:set_card_area(G.jokers) + G.jokers:set_ranks() + G.jokers:align_cards() + end + end + end + end, +} + +local sapphire_stamp = { + object_type = "Blind", + name = "cry-Sapphire Stamp", + key = "sapphire_stamp", + pos = { x = 0, y = 6 }, + dollars = 8, + boss = { + min = 3, + max = 10, + showdown = true, + }, + atlas = "blinds", + order = 92, + boss_colour = HEX("4057d6"), + cry_before_play = function(self) + local idx = pseudorandom(pseudoseed("cry_sapphire_stamp"), 1, #G.hand.highlighted) + G.hand:remove_from_highlighted(G.hand.highlighted[idx]) + end, + set_blind = function(self, reset, silent) + if not reset then + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit + 1 + end + end, + defeat = function(self, silent) + if not G.GAME.blind.disabled then + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit - 1 + end + end, + disable = function(self, silent) + if not G.GAME.blind.disabled then + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit - 1 + end + end, +} + +local obsidian_orb = { + object_type = "Blind", + name = "cry-Obsidian Orb", + key = "obsidian_orb", + pos = { x = 0, y = 7 }, + dollars = 8, + boss = { + min = 3, + max = 10, + showdown = true, + }, + atlas = "blinds", + order = 93, + boss_colour = HEX("290759"), + set_blind = function(self, reset, silent) + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.set_blind then + s:set_blind(reset, silent) + end + if s.name == "The Eye" and not reset then + G.GAME.blind.hands = { + ["Flush Five"] = false, + ["Flush House"] = false, + ["Five of a Kind"] = false, + ["Straight Flush"] = false, + ["Four of a Kind"] = false, + ["Full House"] = false, + ["Flush"] = false, + ["Straight"] = false, + ["Three of a Kind"] = false, + ["Two Pair"] = false, + ["Pair"] = false, + ["High Card"] = false, + } + end + if s.name == "The Mouth" and not reset then + G.GAME.blind.only_hand = false + end + if s.name == "The Fish" and not reset then + G.GAME.blind.prepped = nil + end + if s.name == "The Water" and not reset then + G.GAME.blind.discards_sub = G.GAME.current_round.discards_left + ease_discard(-G.GAME.blind.discards_sub) + end + if s.name == "The Needle" and not reset then + G.GAME.blind.hands_sub = G.GAME.round_resets.hands - 1 + ease_hands_played(-G.GAME.blind.hands_sub) + end + if s.name == "The Manacle" and not reset then + G.hand:change_size(-1) + end + if s.name == "Amber Acorn" and not reset and #G.jokers.cards > 0 then + G.jokers:unhighlight_all() + for k, v in ipairs(G.jokers.cards) do + v:flip() + end + if #G.jokers.cards > 1 then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + G.E_MANAGER:add_event(Event({ + func = function() + G.jokers:shuffle("aajk") + play_sound("cardSlide1", 0.85) + return true + end, + })) + delay(0.15) + G.E_MANAGER:add_event(Event({ + func = function() + G.jokers:shuffle("aajk") + play_sound("cardSlide1", 1.15) + return true + end, + })) + delay(0.15) + G.E_MANAGER:add_event(Event({ + func = function() + G.jokers:shuffle("aajk") + play_sound("cardSlide1", 1) + return true + end, + })) + delay(0.5) + return true + end, + })) + end + end + + --add new debuffs + for _, v in ipairs(G.playing_cards) do + self:debuff_card(v) + end + for _, v in ipairs(G.jokers.cards) do + if not reset then + self:debuff_card(v, true) + end + end + end + end, + defeat = function(self, silent) + for k, _ in pairs(G.GAME.defeated_blinds) do + if G.P_BLINDS[k].defeat then + G.P_BLINDS[k]:defeat(silent) + end + if G.P_BLINDS[k].name == "The Manacle" and not self.disabled then + G.hand:change_size(1) + end + end + end, + disable = function(self, silent) + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.disable then + s:disable(silent) + end + if s.name == "The Water" then + ease_discard(G.GAME.blind.discards_sub) + end + if s.name == "The Wheel" or s.name == "The House" or s.name == "The Mark" or s.name == "The Fish" then + for i = 1, #G.hand.cards do + if G.hand.cards[i].facing == "back" then + G.hand.cards[i]:flip() + end + end + for k, v in pairs(G.playing_cards) do + v.ability.wheel_flipped = nil + end + end + if s.name == "The Needle" then + ease_hands_played(G.GAME.blind.hands_sub) + end + if s.name == "The Wall" then + G.GAME.blind.chips = G.GAME.blind.chips / 2 + G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) + end + if s.name == "Cerulean Bell" then + for k, v in ipairs(G.playing_cards) do + v.ability.forced_selection = nil + end + end + if s.name == "The Manacle" then + G.hand:change_size(1) + + G.FUNCS.draw_from_deck_to_hand(1) + end + if s.name == "Violet Vessel" then + G.GAME.blind.chips = G.GAME.blind.chips / 3 + G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) + end + end + end, + press_play = function(self) + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.press_play then + s:press_play() + end + if s.name == "The Hook" then + G.E_MANAGER:add_event(Event({ + func = function() + local any_selected = nil + local _cards = {} + for k, v in ipairs(G.hand.cards) do + _cards[#_cards + 1] = v + end + for i = 1, 2 do + if G.hand.cards[i] then + local selected_card, card_key = pseudorandom_element(_cards, pseudoseed("ObsidianOrb")) + G.hand:add_to_highlighted(selected_card, true) + table.remove(_cards, card_key) + any_selected = true + play_sound("card1", 1) + end + end + if any_selected then + G.FUNCS.discard_cards_from_highlighted(nil, true) + end + return true + end, + })) + G.GAME.blind.triggered = true + delay(0.7) + end + if s.name == "Crimson Heart" then + if G.jokers.cards[1] then + G.GAME.blind.triggered = true + G.GAME.blind.prepped = true + end + end + if s.name == "The Fish" then + G.GAME.blind.prepped = true + end + if s.name == "The Tooth" then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + for i = 1, #G.play.cards do + G.E_MANAGER:add_event(Event({ + func = function() + G.play.cards[i]:juice_up() + return true + end, + })) + ease_dollars(-1) + delay(0.23) + end + return true + end, + })) + G.GAME.blind.triggered = true + end + end + end, + modify_hand = function(self, cards, poker_hands, text, mult, hand_chips) + local new_mult = mult + local new_chips = hand_chips + local trigger = false + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.modify_hand then + local this_trigger = false + new_mult, new_chips, this_trigger = s:modify_hand(cards, poker_hands, text, new_mult, new_chips) + trigger = trigger or this_trigger + end + if s.name == "The Flint" then + G.GAME.blind.triggered = true + new_mult = math.max(math.floor(new_mult * 0.5 + 0.5), 1) + new_chips = math.max(math.floor(new_chips * 0.5 + 0.5), 0) + trigger = true + end + end + return new_mult or mult, new_chips or hand_chips, trigger + end, + debuff_hand = function(self, cards, hand, handname, check) + G.GAME.blind.debuff_boss = nil + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.debuff_hand and s:debuff_hand(cards, hand, handname, check) then + G.GAME.blind.debuff_boss = s + return true + end + if s.debuff then + G.GAME.blind.triggered = false + if s.debuff.hand and next(hand[s.debuff.hand]) then + G.GAME.blind.triggered = true + G.GAME.blind.debuff_boss = s + return true + end + if s.debuff.h_size_ge and #cards < s.debuff.h_size_ge then + G.GAME.blind.triggered = true + G.GAME.blind.debuff_boss = s + return true + end + if s.debuff.h_size_le and #cards > s.debuff.h_size_le then + G.GAME.blind.triggered = true + G.GAME.blind.debuff_boss = s + return true + end + if s.name == "The Eye" then + if G.GAME.blind.hands[handname] then + G.GAME.blind.triggered = true + G.GAME.blind.debuff_boss = s + return true + end + if not check then + G.GAME.blind.hands[handname] = true + end + end + if s.name == "The Mouth" then + if s.only_hand and s.only_hand ~= handname then + G.GAME.blind.triggered = true + G.GAME.blind.debuff_boss = s + return true + end + if not check then + s.only_hand = handname + end + end + end + if s.name == "The Arm" then + G.GAME.blind.triggered = false + if G.GAME.hands[handname].level > 1 then + G.GAME.blind.triggered = true + if not check then + level_up_hand(G.GAME.blind.children.animatedSprite, handname, nil, -1) + G.GAME.blind:wiggle() + end + end + end + if s.name == "The Ox" then + G.GAME.blind.triggered = false + if handname == G.GAME.current_round.most_played_poker_hand then + G.GAME.blind.triggered = true + if not check then + ease_dollars(-G.GAME.dollars, true) + G.GAME.blind:wiggle() + end + end + end + end + return false + end, + drawn_to_hand = function(self) + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.drawn_to_hand then + s:drawn_to_hand() + end + if s.name == "Cerulean Bell" then + local any_forced = nil + for k, v in ipairs(G.hand.cards) do + if v.ability.forced_selection then + any_forced = true + end + end + if not any_forced then + G.hand:unhighlight_all() + local forced_card = pseudorandom_element(G.hand.cards, pseudoseed("ObsidianOrb")) + forced_card.ability.forced_selection = true + G.hand:add_to_highlighted(forced_card) + end + end + if s.name == "Crimson Heart" and G.GAME.blind.prepped and G.jokers.cards[1] then + local jokers = {} + for i = 1, #G.jokers.cards do + if not G.jokers.cards[i].debuff or #G.jokers.cards < 2 then + jokers[#jokers + 1] = G.jokers.cards[i] + end + G.jokers.cards[i]:set_debuff(false) + end + local _card = pseudorandom_element(jokers, pseudoseed("ObsidianOrb")) + if _card then + _card:set_debuff(true) + _card:juice_up() + G.GAME.blind:wiggle() + end + end + end + end, + stay_flipped = function(self, area, card) + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.stay_flipped and s:stay_flipped(area, card) then + return true + end + if area == G.hand then + if + s.name == "The Wheel" + and pseudorandom(pseudoseed("ObsidianOrb")) < G.GAME.probabilities.normal / 7 + then + return true + end + if + s.name == "The House" + and G.GAME.current_round.hands_played == 0 + and G.GAME.current_round.discards_used == 0 + then + return true + end + if s.name == "The Mark" and card:is_face(true) then + return true + end + if s.name == "The Fish" and G.GAME.blind.prepped then + return true + end + end + end + end, + debuff_card = function(self, card, from_blind) + if card and type(card) == "table" and card.area then + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.debuff_card then + s:debuff_card(card, from_blind) + end + if s.debuff and not G.GAME.blind.disabled and card.area ~= G.jokers then + --this part is buggy for some reason + if s.debuff.suit and Card.is_suit(card, s.debuff.suit, true) then + card:set_debuff(true) + return + end + if s.debuff.is_face == "face" and Card.is_face(card, true) then + card:set_debuff(true) + return + end + if s.name == "The Pillar" and card.ability.played_this_ante then + card:set_debuff(true) + return + end + if s.debuff.value and s.debuff.value == card.base.value then + card:set_debuff(true) + return + end + if s.debuff.nominal and s.debuff.nominal == card.base.nominal then + card:set_debuff(true) + return + end + end + if s.name == "Crimson Heart" and not G.GAME.blind.disabled and card.area == G.jokers then + return + end + if s.name == "Verdant Leaf" and not G.GAME.blind.disabled and card.area ~= G.jokers then + card:set_debuff(true) + return + end + end + end + end, + cry_ante_base_mod = function(self, dt) + local mod = 0 + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.cry_ante_base_mod then + mod = mod + s:cry_ante_base_mod(dt) + end + end + return mod + end, + cry_round_base_mod = function(self, dt) + local mod = 1 + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.cry_round_base_mod then + mod = mod * s:cry_round_base_mod(dt) + end + end + return mod + end, + cry_cap_score = function(self, score) + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.cry_cap_score then + score = s:cry_cap_score(score) + end + end + return score + end, + cry_calc_ante_gain = function(self) + local ante = 1 + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.cry_calc_ante_gain then + ante = math.max(ante, s:cry_calc_ante_gain()) + end + end + return ante + end, + cry_before_play = function(self) + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.cry_before_play then + s:cry_before_play() + end + end + end, + cry_after_play = function(self) + for k, _ in pairs(G.GAME.defeated_blinds) do + s = G.P_BLINDS[k] + if s.cry_after_play then + s:cry_after_play() + end + end + end, + get_loc_debuff_text = function(self) + if not G.GAME.blind.debuff_boss then + return localize("cry_debuff_obsidian_orb") + end + local loc_vars = nil + if G.GAME.blind.debuff_boss.name == "The Ox" then + loc_vars = { localize(G.GAME.current_round.most_played_poker_hand, "poker_hands") } + end + local loc_target = + localize({ type = "raw_descriptions", key = G.GAME.blind.debuff_boss.key, set = "Blind", vars = loc_vars }) + local loc_debuff_text = "" + for k, v in ipairs(loc_target) do + loc_debuff_text = loc_debuff_text .. v .. (k <= #loc_target and " " or "") + end + local disp_text = (G.GAME.blind.debuff_boss.name == "The Wheel" and G.GAME.probabilities.normal or "") + .. loc_debuff_text + if (G.GAME.blind.debuff_boss.name == "The Mouth") and G.GAME.blind.only_hand then + disp_text = disp_text .. " [" .. localize(G.GAME.blind.only_hand, "poker_hands") .. "]" + end + return disp_text + end, +} + +local blind_sprites = { + object_type = "Atlas", + key = "blinds", + atlas_table = "ANIMATION_ATLAS", + path = "bl_cry.png", + px = 34, + py = 34, + frames = 21, +} +local nostalgia_sprites = { + object_type = "Atlas", + key = "nostalgia", + atlas_table = "ANIMATION_ATLAS", + path = "bl_nostalgia.png", + px = 34, + py = 34, + frames = 21, +} + +--this list contains all of the blinds to be registered, if Blinds are enabled-- +--to disable a blind, comment it out or remove it from this list-- +local items_togo = { + oldox, + oldhouse, + oldarm, + oldfish, + oldmanacle, + oldserpent, + oldpillar, + oldflint, + oldmark, + tax, + trick, + joke, + hammer, + magic, + box, + windmill, + striker, + shackle, + pin, + vermillion_virus, + tornado, + sapphire_stamp, + obsidian_orb, + blind_sprites, + nostalgia_sprites, +} + +if Cryptid.enabled["Timer Mechanics"] then + table.insert(items_togo, clock) + table.insert(items_togo, lavender_loop) +end + +--Fix an issue with adding bosses mid-run +local gnb = get_new_boss +function get_new_boss() + for k, v in pairs(G.P_BLINDS) do + if not G.GAME.bosses_used[k] then + G.GAME.bosses_used[k] = 0 + end + end + local bl = gnb() + if G.GAME.modifiers.cry_beta and Cryptid.enabled["Blinds"] then + local bl_key = string.sub(bl,4) + local nostalgicblinds = { + arm = true, + fish = true, + flint = true, + house = true, + manacle = true, + mark = true, + ox = true, + pillar = true, + serpent = true + } + if nostalgicblinds[bl_key] then + return "bl_cry_old"..bl_key + end + end + return bl +end + +return { + name = "Blinds", + init = function() + --Clock Patches + local upd = Game.update + function Game:update(dt) + upd(self, dt) + local choices = { "Small", "Big", "Boss" } + G.GAME.CRY_BLINDS = G.GAME.CRY_BLINDS or {} + for _, c in pairs(choices) do + if + G.GAME + and G.GAME.round_resets + and G.GAME.round_resets.blind_choices + and G.GAME.round_resets.blind_choices[c] + and G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].cry_ante_base_mod + then + if + G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult ~= 0 + and G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult_ante ~= G.GAME.round_resets.ante + then + if G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].name == "cry-Obsidian Orb" then + for i = 1, #G.GAME.defeated_blinds do + G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult = G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult + * G.P_BLINDS[G.GAME.defeated_blinds[i]] + / 2 + end + else + G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult = 0 + end + G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult_ante = G.GAME.round_resets.ante + end + if + G.GAME.round_resets.blind_states[c] ~= "Current" + and G.GAME.round_resets.blind_states[c] ~= "Defeated" + then + G.GAME.CRY_BLINDS[c] = ( + G.GAME.CRY_BLINDS[c] or G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].mult + ) + + ( + G.P_BLINDS[G.GAME.round_resets.blind_choices[c]].cry_ante_base_mod + and G.P_BLINDS[G.GAME.round_resets.blind_choices[c]]:cry_ante_base_mod( + dt * (G.GAME.modifiers.cry_rush_hour_iii and 2 or 1) + ) + or 0 + ) + --Update UI + --todo: in blinds screen, too + if G.blind_select_opts then + local blind_UI = + G.blind_select_opts[string.lower(c)].definition.nodes[1].nodes[1].nodes[1].nodes[1] + local chip_text_node = blind_UI.nodes[1].nodes[3].nodes[1].nodes[2].nodes[2].nodes[3] + if chip_text_node then + chip_text_node.config.text = number_format( + get_blind_amount(G.GAME.round_resets.blind_ante) + * G.GAME.starting_params.ante_scaling + * G.GAME.CRY_BLINDS[c] + ) + chip_text_node.config.scale = score_number_scale( + 0.9, + get_blind_amount(G.GAME.round_resets.blind_ante) + * G.GAME.starting_params.ante_scaling + * G.GAME.CRY_BLINDS[c] + ) + end + G.blind_select_opts[string.lower(c)]:recalculate() + end + elseif + G.GAME.round_resets.blind_states[c] ~= "Defeated" + and not G.GAME.blind.disabled + and to_big(G.GAME.chips) < to_big(G.GAME.blind.chips) + then + G.GAME.blind.chips = G.GAME.blind.chips + + G.GAME.blind:cry_ante_base_mod(dt * (G.GAME.modifiers.cry_rush_hour_iii and 2 or 1)) + * get_blind_amount(G.GAME.round_resets.ante) + * G.GAME.starting_params.ante_scaling + G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) + end + end + if + G.GAME.round_resets.blind_states[c] == "Current" + and G.GAME + and G.GAME.blind + and not G.GAME.blind.disabled + and to_big(G.GAME.chips) < to_big(G.GAME.blind.chips) + then + G.GAME.blind.chips = G.GAME.blind.chips + * G.GAME.blind:cry_round_base_mod(dt * (G.GAME.modifiers.cry_rush_hour_iii and 2 or 1)) + G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) + end + end + end + --Trick Patches + local gfep = G.FUNCS.evaluate_play + function G.FUNCS.evaluate_play(e) + gfep(e) + G.GAME.blind:cry_after_play() + end + --Sapphire Stamp Patches + local pcfh = G.FUNCS.play_cards_from_highlighted + function G.FUNCS.play_cards_from_highlighted(e) + G.GAME.blind:cry_before_play() + pcfh(e) + end + --Obsidian Orb Patches + local dft = Blind.defeat + function Blind:defeat(s) + dft(self, s) + local obj = self.config.blind + if obj.boss and (obj.boss.no_orb or obj.boss.epic or obj.loc_vars) then + return + end + if + self.name ~= "cry-Obsidian Orb" + and (self.name ~= "cry-oldarm" or not G.GAME.defeated_blinds["bl_psychic"]) + and (self.name ~= "The Psychic" or not G.GAME.defeated_blinds["bl_cry_oldarm"]) + and (self.name ~= "The Eye" or not G.GAME.defeated_blinds["bl_mouth"]) + and (self.name ~= "The Mouth" or not G.GAME.defeated_blinds["bl_eye"]) + and (self.name ~= "cry-Lavender Loop" or not G.GAME.defeated_blinds["bl_cry_tax"]) + and (self.name ~= "cry-Tax" or not G.GAME.defeated_blinds["bl_cry_lavender_loop"]) + and (self.name ~= "The Needle" or not G.GAME.defeated_blinds["bl_cry_tax"]) + and (self.name ~= "cry-Tax" or not G.GAME.defeated_blinds["bl_needle"]) + then + G.GAME.defeated_blinds[self.config.blind.key] = true + end + end + local sr = Game.start_run + function Game:start_run(args) + sr(self, args) + if G.P_BLINDS.bl_cry_clock then + G.P_BLINDS.bl_cry_clock.mult = 0 + end + if not G.GAME.defeated_blinds then + G.GAME.defeated_blinds = {} + end + end + --patch for multiple Clocks to tick separately and load separately + local bsb = Blind.set_blind + function Blind:set_blind(blind, y, z) + local c = "Boss" + if string.sub(G.GAME.subhash or "", -1) == "S" then + c = "Small" + end + if string.sub(G.GAME.subhash or "", -1) == "B" then + c = "Big" + end + if + G.GAME.CRY_BLINDS + and G.GAME.CRY_BLINDS[c] + and not y + and blind + and blind.mult + and blind.cry_ante_base_mod + then + blind.mult = G.GAME.CRY_BLINDS[c] + end + bsb(self, blind, y, z) + end + local rb = reset_blinds + function reset_blinds() + if G.GAME.round_resets.blind_states.Boss == "Defeated" then + G.GAME.CRY_BLINDS = {} + if G.P_BLINDS.bl_cry_clock then + G.P_BLINDS.bl_cry_clock.mult = 0 + end + end + rb() + end + end, + items = items_togo, +} diff --git a/Cryptid/Items/Challenges.lua b/Cryptid/Items/Challenges.lua new file mode 100644 index 0000000..5e3a40e --- /dev/null +++ b/Cryptid/Items/Challenges.lua @@ -0,0 +1,576 @@ +local sticker_sheet = { + object_type = "Challenge", + key = "sticker_sheet", + order = 9, + rules = { + custom = { + { id = "all_eternal" }, + { id = "cry_all_perishable" }, + { id = "cry_all_rental" }, + { id = "cry_all_pinned" }, + { id = "cry_all_banana" }, + { id = "cry_eternal_perishable_compat" }, + { id = "cry_any_stickers" }, + { id = "cry_sticker_sheet" }, + }, + modifiers = {}, + }, + restrictions = { + banned_cards = {}, + banned_other = {}, + }, + deck = { + type = "Challenge Deck", + }, +} +local sticker_sheet_plus = { + object_type = "Challenge", + key = "sticker_sheet_plus", + order = 10, + rules = { + custom = { + { id = "cry_sticker_sheet_plus" }, + { id = "cry_eternal_perishable_compat" }, + }, + modifiers = {}, + }, + restrictions = { + banned_cards = {}, + banned_other = {}, + }, + deck = { + type = "Challenge Deck", + }, +} +local ballin = { + object_type = "Challenge", + key = "ballin", + order = 1, + rules = { + custom = {}, + modifiers = { + { id = "joker_slots", value = 3 }, + }, + }, + jokers = { + { id = "j_cry_jimball", eternal = true }, + }, + deck = { + type = "Challenge Deck", + enhancement = "m_stone", + }, + restrictions = { + banned_cards = { + { id = "j_vampire" }, + { id = "c_magician" }, + { id = "c_empress" }, + { id = "c_heirophant" }, + { id = "c_lovers" }, + { id = "c_chariot" }, + { id = "c_justice" }, + { id = "c_devil" }, + { id = "c_tower" }, + { id = "c_familiar" }, + { id = "c_grim" }, + { id = "c_incantation" }, + }, + banned_other = {}, + }, +} +local rush_hour = { + object_type = "Challenge", + key = "rush_hour", + order = 3, + rules = { + custom = { + { id = "cry_rush_hour" }, --this just explains the rule + }, + modifiers = {}, + }, + deck = { + type = "Challenge Deck", + }, + restrictions = { + banned_cards = { + { id = "j_luchador" }, + { id = "j_chicot" }, + }, + banned_other = {}, + }, +} +local rush_hour_ii = { + object_type = "Challenge", + key = "rush_hour_ii", + order = 6, + rules = { + custom = { + { id = "cry_rush_hour" }, + { id = "cry_rush_hour_ii" }, + { id = "cry_no_tags" }, + }, + modifiers = {}, + }, + deck = { + type = "Challenge Deck", + }, + restrictions = { + banned_cards = { + { id = "j_luchador" }, + { id = "j_chicot" }, + { id = "j_throwback" }, + { id = "j_diet_cola" }, + { id = "v_directors_cut" }, + { id = "v_retcon" }, + }, + banned_other = {}, + }, +} + +local rush_hour_iii = { + object_type = "Challenge", + key = "rush_hour_iii", + order = 8, + rules = { + custom = { + { id = "cry_rush_hour" }, + { id = "cry_rush_hour_ii" }, + { id = "cry_rush_hour_iii" }, + { id = "cry_no_tags" }, + }, + modifiers = {}, + }, + jokers = { + { id = "j_hit_the_road", eternal = true, edition = "negative" }, + }, + deck = { + type = "Challenge Deck", + cards = { + { s = "D", r = "2" }, + { s = "D", r = "3" }, + { s = "D", r = "4" }, + { s = "D", r = "5" }, + { s = "D", r = "6" }, + { s = "D", r = "7" }, + { s = "D", r = "8" }, + { s = "D", r = "9" }, + { s = "D", r = "T" }, + { s = "D", r = "J" }, + { s = "D", r = "J" }, + { s = "D", r = "Q" }, + { s = "D", r = "K" }, + { s = "D", r = "A" }, + { s = "C", r = "2" }, + { s = "C", r = "3" }, + { s = "C", r = "4" }, + { s = "C", r = "5" }, + { s = "C", r = "6" }, + { s = "C", r = "7" }, + { s = "C", r = "8" }, + { s = "C", r = "9" }, + { s = "C", r = "T" }, + { s = "C", r = "J" }, + { s = "C", r = "J" }, + { s = "C", r = "Q" }, + { s = "C", r = "K" }, + { s = "C", r = "A" }, + { s = "H", r = "2" }, + { s = "H", r = "3" }, + { s = "H", r = "4" }, + { s = "H", r = "5" }, + { s = "H", r = "6" }, + { s = "H", r = "7" }, + { s = "H", r = "8" }, + { s = "H", r = "9" }, + { s = "H", r = "T" }, + { s = "H", r = "J" }, + { s = "H", r = "J" }, + { s = "H", r = "Q" }, + { s = "H", r = "K" }, + { s = "H", r = "A" }, + { s = "S", r = "2" }, + { s = "S", r = "3" }, + { s = "S", r = "4" }, + { s = "S", r = "5" }, + { s = "S", r = "6" }, + { s = "S", r = "7" }, + { s = "S", r = "8" }, + { s = "S", r = "9" }, + { s = "S", r = "T" }, + { s = "S", r = "J" }, + { s = "S", r = "J" }, + { s = "S", r = "Q" }, + { s = "S", r = "K" }, + { s = "S", r = "A" }, + }, + }, + restrictions = { + banned_cards = { + { id = "j_luchador" }, + { id = "j_chicot" }, + { id = "j_throwback" }, + { id = "j_diet_cola" }, + { id = "v_directors_cut" }, + { id = "v_retcon" }, + }, + banned_other = {}, + }, +} +local boss_rush = { + object_type = "Challenge", + key = "boss_rush", + order = 2, + rules = { + custom = { + { id = "cry_rush_hour_ii" }, + { id = "cry_no_tags" }, + }, + modifiers = {}, + }, + jokers = { + { id = "j_cry_apjoker", eternal = true }, + }, + deck = { + type = "Challenge Deck", + }, + restrictions = { + banned_cards = { + { id = "j_luchador" }, + { id = "j_chicot" }, + { id = "j_throwback" }, + { id = "j_diet_cola" }, + { id = "v_directors_cut" }, + { id = "v_retcon" }, + }, + banned_other = {}, + }, +} +local rng = { + object_type = "Challenge", + key = "rng", + order = 7, + rules = { + custom = { + { id = "all_rnj" }, + }, + modifiers = {}, + }, + jokers = {}, + deck = { + type = "Challenge Deck", + }, + restrictions = { + banned_tags = { + { id = "tag_uncommon" }, + { id = "tag_rare" }, + { id = "tag_top_up" }, + }, + banned_cards = {}, + banned_other = {}, + }, +} +local dagger_war = { + object_type = "Challenge", + key = "dagger_war", + order = 4, + rules = { + custom = {}, + modifiers = {}, + }, + restrictions = { + banned_cards = {}, + banned_other = {}, + }, + jokers = { + { id = "j_cry_cryptidmoment", edition = "negative" }, + { id = "j_cry_cryptidmoment", edition = "negative" }, + { id = "j_gift", edition = "negative" }, + { id = "j_gift", edition = "negative" }, + { id = "j_ceremonial", eternal = true }, + { id = "j_cry_unjust_dagger", eternal = true }, + { id = "j_cry_monkey_dagger", eternal = true }, + { id = "j_cry_pirate_dagger", eternal = true }, + }, + deck = { + type = "Challenge Deck", + }, +} +local onlycard = { + object_type = "Challenge", + key = "onlycard", + order = 5, + rules = { + custom = {}, + modifiers = { + {id = 'dollars', value = 10}, + }, + }, + restrictions = { + banned_tags = { + { id = "tag_charm" }, + { id = "tag_meteor" }, + { id = "tag_buffoon" }, + { id = "tag_ethereal" } + }, + banned_cards = { + { id = "j_marble" }, + { id = "j_dna" }, + { id = "j_certificate" }, + { id = "c_familiar" }, + { id = "c_grim" }, + { id = "c_incantation" }, + { id = "c_cryptid" }, + {id = 'p_celestial_normal_1', ids = { + 'p_celestial_normal_1','p_celestial_normal_2', + 'p_celestial_normal_3','p_celestial_normal_4', + 'p_celestial_jumbo_1','p_celestial_jumbo_2', + 'p_celestial_mega_1','p_celestial_mega_2',} + }, + {id = 'p_arcana_normal_1', ids = { + 'p_arcana_normal_1','p_arcana_normal_2', + 'p_arcana_normal_3','p_arcana_normal_4', + 'p_arcana_jumbo_1','p_arcana_jumbo_2', + 'p_arcana_mega_1','p_arcana_mega_2',} + }, + {id = 'p_spectral_normal_1', ids = { + 'p_spectral_normal_1','p_spectral_normal_2', + 'p_spectral_jumbo_1','p_spectral_mega_1',} + }, + {id = 'p_buffoon_normal_1', ids = { + 'p_buffoon_normal_1','p_buffoon_normal_2', + 'p_buffoon_jumbo_1','p_buffoon_mega_1',} + }, + }, + banned_other = { + { id = 'bl_house', type = 'blind' }, + }, + }, + jokers = { + { id = "j_popcorn" }, + }, + deck = { + type = "Challenge Deck", + cards = { + { s = "C", r = "A", g='Blue' }, + }, + }, +} +local joker_poker = { + object_type = "Challenge", + key = "joker_poker", + order = 11, + rules = { + custom = { + { id = "cry_no_tags" }, + { id = "cry_no_rerolls" }, + { id = "cry_no_vouchers" }, + { id = "cry_no_boosters" }, + { id = "cry_no_consumables" }, + }, + modifiers = { + {id = "consumable_slots", value = 0}, + {id = "discards", value = 0} + }, + }, + deck = { + type = "Challenge Deck", + }, + restrictions = { + banned_cards = { + {id = "j_banner"}, + {id = "j_8_ball"}, + {id = "j_chaos"}, + {id = "j_delayed_grat"}, + {id = "j_sixth_sense"}, + {id = "j_faceless"}, + {id = "j_superposition"}, + {id = "j_red_card"}, + {id = "j_seance"}, + {id = "j_vagabond"}, + {id = "j_mail"}, + {id = "j_hallucination"}, + {id = "j_fortune_teller"}, + {id = "j_drunkard"}, + {id = "j_trading"}, + {id = "j_flash"}, + {id = "j_castle"}, + {id = "j_merry_andy"}, + {id = "j_hit_the_road"}, + {id = "j_satellite"}, + {id = "j_cartomancer"}, + {id = "j_astronomer"}, + {id = "j_burnt"}, + {id = "j_yorick"}, + {id = "j_perkeo"}, + {id = "j_constellation"} + }, + banned_other = { + { id = 'bl_hook', type = 'blind' }, + { id = 'bl_arm', type = 'blind' }, + { id = 'bl_water', type = 'blind' }, + }, + }, +} +local gfcr = G.FUNCS.can_reroll +function G.FUNCS.can_reroll(e) + if G.GAME.modifiers.cry_no_rerolls then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + return gfcr(e) + end +end +local gsr = Game.start_run +function Game:start_run(args) + gsr(self, args) + if G.GAME.modifiers.cry_no_consumables then + G.GAME.joker_rate = 1e300 + end +end +--Add banned cards when specific features/mods are enabled here +--TODO other mods +if Cryptid.enabled["Blinds"] then + joker_poker.restrictions.banned_other[#joker_poker.restrictions.banned_other + 1] = { id = 'bl_cry_oldmanacle', type = 'blind' } +end +if Cryptid.enabled["Tags"] then + onlycard.restrictions.banned_tags[#onlycard.restrictions.banned_tags + 1] = { id = "tag_cry_bundle" } + onlycard.restrictions.banned_tags[#onlycard.restrictions.banned_tags + 1] = { id = "tag_cry_loss" } + onlycard.restrictions.banned_tags[#onlycard.restrictions.banned_tags + 1] = { id = "tag_cry_gambler" } + onlycard.restrictions.banned_tags[#onlycard.restrictions.banned_tags + 1] = { id = "tag_cry_empowered" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "p_cry_empowered" } + if Cryptid.enabled["Epic Jokers"] then + rng.restrictions.banned_tags[#rng.restrictions.banned_tags + 1] = { id = "tag_cry_epic" } + end +end +if Cryptid.enabled["Misc."] then + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_cry_eclipse" } + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = {id = 'p_cry_meme_1', ids = {'p_cry_meme_1','p_cry_meme_two','p_cry_meme_three'}} + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = {id = 'p_cry_meme_1', ids = {'p_cry_meme_1','p_cry_meme_two','p_cry_meme_three'}} +end +if Cryptid.enabled["Misc. Jokers"] then + rush_hour_ii.restrictions.banned_cards[#rush_hour_ii.restrictions.banned_cards + 1] = { id = "j_cry_pickle" } + rush_hour_iii.restrictions.banned_cards[#rush_hour_iii.restrictions.banned_cards + 1] = { id = "j_cry_pickle" } + boss_rush.restrictions.banned_cards[#boss_rush.restrictions.banned_cards + 1] = { id = "j_cry_pickle" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_booster" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_wheelhope" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_hunger" } +end +if Cryptid.enabled["M Jokers"] then + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_sacrifice" } + if Cryptid.enabled["Epic Jokers"] then + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_doodlem" } + end +end +if Cryptid.enabled["Epic Jokers"] then + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "j_cry_multjoker" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_multjoker" } +end +if Cryptid.enabled["Exotic Jokers"] then + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = { id = "j_cry_equilib" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_stella_mortis" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_crustulum" } +end +if Cryptid.enabled["Code Cards"] then + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_cry_class" } + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = { id = "c_cry_delete" } + onlycard.restrictions.banned_tags[#onlycard.restrictions.banned_tags + 1] = { id = "tag_cry_console" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = {id = 'p_cry_code_normal_1', ids = {'p_cry_code_normal_1','p_cry_code_normal_2','p_cry_code_jumbo_1','p_cry_code_mega_1',}} + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_cut" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_CodeJoker" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_copypaste" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_blender" } + joker_poker.restrictions.banned_cards[#joker_poker.restrictions.banned_cards + 1] = { id = "j_cry_python" } +end +if Cryptid.enabled["Spectrals"] then + sticker_sheet.restrictions.banned_cards[#sticker_sheet.restrictions.banned_cards + 1] = { id = "c_cry_lock" } + sticker_sheet_plus.restrictions.banned_cards[#sticker_sheet_plus.restrictions.banned_cards + 1] = { id = "c_cry_lock" } + dagger_war.restrictions.banned_cards[#dagger_war.restrictions.banned_cards + 1] = { id = "c_cry_lock" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_cry_replica" } +end +if Cryptid.enabled["Vouchers"] then + rush_hour_ii.restrictions.banned_cards[#rush_hour_ii.restrictions.banned_cards + 1] = { id = "v_cry_copies" } + rush_hour_iii.restrictions.banned_cards[#rush_hour_iii.restrictions.banned_cards + 1] = { id = "v_cry_copies" } + boss_rush.restrictions.banned_cards[#boss_rush.restrictions.banned_cards + 1] = { id = "v_cry_copies" } + rush_hour_ii.restrictions.banned_cards[#rush_hour_ii.restrictions.banned_cards + 1] = { id = "v_cry_tag_printer" } + rush_hour_iii.restrictions.banned_cards[#rush_hour_iii.restrictions.banned_cards + 1] = { id = "v_cry_tag_printer" } + boss_rush.restrictions.banned_cards[#boss_rush.restrictions.banned_cards + 1] = { id = "v_cry_tag_printer" } + rush_hour_ii.restrictions.banned_cards[#rush_hour_ii.restrictions.banned_cards + 1] = { id = "v_cry_clone_machine" } + rush_hour_iii.restrictions.banned_cards[#rush_hour_iii.restrictions.banned_cards + 1] = { id = "v_cry_clone_machine" } + boss_rush.restrictions.banned_cards[#boss_rush.restrictions.banned_cards + 1] = { id = "v_cry_clone_machine" } +end +if (SMODS.Mods["jen"] or {}).can_load then + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_chance" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_cry_bundle" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_magician" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_empress" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_heirophant" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_lovers" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_chariot" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_justice" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_devil" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_tower" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_star" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_moon" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_sun" } + ballin.restrictions.banned_cards[#ballin.restrictions.banned_cards + 1] = { id = "c_jen_reverse_world" } + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = { id = "c_jen_jokerinatarot" } + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_uncommon" } + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_rare" } + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_top_up" } + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_cry_epic" } + rng.restrictions.banned_cards[#rng.restrictions.banned_cards + 1] = { id = "c_jen_wraith_ex" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "j_jen_shikigami" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_charm" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_meteor" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_buffoon" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_ethereal" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_token_tag_cry_bundle" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_magician" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_empress" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_heirophant" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_lovers" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_chariot" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_justice" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_devil" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_tower" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_high_priestess" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_emperor" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_death" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_star" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_moon" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_sun" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_world" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_reverse_judgement" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_mischief" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_wonder" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_familiar_ex" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_grim_ex" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_incantation_ex" } + onlycard.restrictions.banned_cards[#onlycard.restrictions.banned_cards + 1] = { id = "c_jen_cryptid_ex" } +end +--end of banned cards +local challenges = { sticker_sheet, sticker_sheet_plus, onlycard } +if Cryptid.enabled["Misc. Jokers"] then + challenges[#challenges + 1] = ballin + challenges[#challenges + 1] = boss_rush + challenges[#challenges + 1] = rng + challenges[#challenges + 1] = dagger_war +end +if Cryptid.enabled["Blinds"] and Cryptid.enabled["Timer Mechanics"] then + challenges[#challenges + 1] = rush_hour + challenges[#challenges + 1] = rush_hour_ii + challenges[#challenges + 1] = rush_hour_iii +end +if Cryptid.enabled["Misc. Decks"] then --yoinking vfd code here + challenges[#challenges + 1] = joker_poker +end + +for k, v in pairs(G.P_CENTERS) do + if v.set == "Joker" then + if not v.perishable_compat or not v.eternal_compat then + sticker_sheet.restrictions.banned_cards[#sticker_sheet.restrictions.banned_cards + 1] = { id = k } + sticker_sheet_plus.restrictions.banned_cards[#sticker_sheet_plus.restrictions.banned_cards + 1] = { id = k } + end + end +end + +return { name = "Challenges", init = function() end, items = challenges } diff --git a/Cryptid/Items/CodeCards.lua b/Cryptid/Items/CodeCards.lua new file mode 100644 index 0000000..2cf11da --- /dev/null +++ b/Cryptid/Items/CodeCards.lua @@ -0,0 +1,4225 @@ +--note to self: refer to https://docs.google.com/document/d/1LNaIouU3vrtWIuPBdFCqLyjYAjVtq7t64xjHnckEY50/edit for order of remaining consumables +local code = { + object_type = "ConsumableType", + key = "Code", + primary_colour = HEX("14b341"), + secondary_colour = HEX("12f254"), + collection_rows = { 4, 4 }, -- 4 pages for all code cards + shop_rate = 0.0, + loc_txt = {}, + default = (SMODS.Mods['jen'] or {}).can_load and "c_cry_oboe" or "c_cry_crash", + can_stack = true, + can_divide = true, +} +local code_atlas = { + object_type = "Atlas", + key = "code", + path = "c_cry_code.png", + px = 71, + py = 95, +} +SMODS.UndiscoveredSprite({ + key = "Code", + atlas = "code", + path = "c_cry_code.png", + pos = { x = 2, y = 5 }, + px = 71, + py = 95, +}):register() +SMODS.UndiscoveredSprite({ --todo change? + key = "Unique", + atlas = "code", + path = "c_cry_code.png", + pos = { x = 2, y = 5 }, + px = 71, + py = 95, +}):register() +local pack_atlas = { + object_type = "Atlas", + key = "pack", + path = "pack_cry.png", + px = 71, + py = 95, +} +local pack1 = { + object_type = "Booster", + key = "code_normal_1", + kind = "Code", + atlas = "pack", + pos = { x = 0, y = 0 }, + config = { extra = 2, choose = 1 }, + cost = 4, + order = 1, + weight = 0.96, + create_card = function(self, card) + return create_card("Code", G.pack_cards, nil, nil, true, true, nil, "cry_program") + end, + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.SET.Code) + ease_background_colour({ new_colour = G.C.SET.Code, special_colour = G.C.BLACK, contrast = 2 }) + end, + loc_vars = function(self, info_queue, card) + return { vars = { card.config.center.config.choose, card.ability.extra } } + end, + group_key = "k_cry_program_pack", +} +local pack2 = { + object_type = "Booster", + key = "code_normal_2", + kind = "Code", + atlas = "pack", + pos = { x = 1, y = 0 }, + config = { extra = 2, choose = 1 }, + cost = 4, + order = 2, + weight = 0.96, + create_card = function(self, card) + return create_card("Code", G.pack_cards, nil, nil, true, true, nil, "cry_program") + end, + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.SET.Code) + ease_background_colour({ new_colour = G.C.SET.Code, special_colour = G.C.BLACK, contrast = 2 }) + end, + loc_vars = function(self, info_queue, card) + return { vars = { card.config.center.config.choose, card.ability.extra } } + end, + group_key = "k_cry_program_pack", +} +local packJ = { + object_type = "Booster", + key = "code_jumbo_1", + kind = "Code", + atlas = "pack", + pos = { x = 2, y = 0 }, + config = { extra = 4, choose = 1 }, + cost = 6, + order = 3, + weight = 0.48, + create_card = function(self, card) + return create_card("Code", G.pack_cards, nil, nil, true, true, nil, "cry_program") + end, + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.SET.Code) + ease_background_colour({ new_colour = G.C.SET.Code, special_colour = G.C.BLACK, contrast = 2 }) + end, + loc_vars = function(self, info_queue, card) + return { vars = { card.config.center.config.choose, card.ability.extra } } + end, + group_key = "k_cry_program_pack", +} +local packM = { + object_type = "Booster", + key = "code_mega_1", + kind = "Code", + atlas = "pack", + pos = { x = 3, y = 0 }, + config = { extra = 4, choose = 2 }, + cost = 8, + order = 4, + weight = 0.12, + create_card = function(self, card) + return create_card("Code", G.pack_cards, nil, nil, true, true, nil, "cry_program") + end, + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.SET.Code) + ease_background_colour({ new_colour = G.C.SET.Code, special_colour = G.C.BLACK, contrast = 2 }) + end, + loc_vars = function(self, info_queue, card) + return { vars = { card.config.center.config.choose, card.ability.extra } } + end, + group_key = "k_cry_program_pack", +} +local console = { + object_type = "Tag", + atlas = "tag_cry", + name = "cry-Console Tag", + order = 26, + pos = { x = 3, y = 2 }, + config = { type = "new_blind_choice" }, + key = "console", + min_ante = 2, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = { set = "Other", key = "p_cry_code_normal_1", specific_vars = { 1, 2 } } + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "new_blind_choice" then + tag:yep("+", G.C.SECONDARY_SET.Code, function() + local key = "p_cry_code_normal_" .. math.random(1, 2) + local card = Card( + G.play.T.x + G.play.T.w / 2 - G.CARD_W * 1.27 / 2, + G.play.T.y + G.play.T.h / 2 - G.CARD_H * 1.27 / 2, + G.CARD_W * 1.27, + G.CARD_H * 1.27, + G.P_CARDS.empty, + G.P_CENTERS[key], + { bypass_discovery_center = true, bypass_discovery_ui = true } + ) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({ config = { ref_table = card } }) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + return true + end) + tag.triggered = true + return true + end + end, +} +local crash = { + object_type = "Consumable", + set = "Code", + name = "cry-Crash", + key = "crash", + pos = { x = 0, y = 0 }, + config = {}, + cost = 4, + atlas = "code", + order = 1, + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + if not G.PROFILES[G.SETTINGS.profile].consumeable_usage["c_cry_crash"] then + set_consumeable_usage(card) + end + -- I'm being VERY safe here, game gets really weird and sometimes does and doesn't save ://CRASH use + G:save_settings() + G:save_progress() + local f = pseudorandom_element(crashes, pseudoseed("cry_crash")) + f(self, card, area, copier) + end, +} + +local payload = { + object_type = "Consumable", + set = "Code", + name = "cry-Payload", + key = "payload", + pos = { x = 1, y = 0 }, + config = { interest_mult = 3 }, + loc_vars = function(self, info_queue, center) + return { vars = { self.config.interest_mult } } + end, + cost = 4, + atlas = "code", + order = 2, + can_use = function(self, card) + return true + end, + can_bulk_use = true, + use = function(self, card, area, copier) + G.GAME.cry_payload = (G.GAME.cry_payload or 1) * card.ability.interest_mult + end, + bulk_use = function(self, card, area, copier, number) + G.GAME.cry_payload = (G.GAME.cry_payload or 1) * card.ability.interest_mult ^ number + end, +} +local reboot = { + object_type = "Consumable", + set = "Code", + name = "cry-Reboot", + key = "reboot", + pos = { x = 2, y = 0 }, + config = {}, + cost = 4, + atlas = "code", + order = 3, + can_use = function(self, card) + return G.STATE == G.STATES.SELECTING_HAND + end, + use = function(self, card, area, copier) + G.FUNCS.draw_from_hand_to_discard() + G.FUNCS.draw_from_discard_to_deck() + ease_discard( + math.max(0, G.GAME.round_resets.discards + G.GAME.round_bonus.discards) - G.GAME.current_round.discards_left + ) + ease_hands_played( + math.max(1, G.GAME.round_resets.hands + G.GAME.round_bonus.next_hands) - G.GAME.current_round.hands_left + ) + for k, v in pairs(G.playing_cards) do + v.ability.wheel_flipped = nil + end + G.E_MANAGER:add_event(Event({ + trigger = "immediate", + func = function() + G.STATE = G.STATES.DRAW_TO_HAND + G.deck:shuffle("cry_reboot" .. G.GAME.round_resets.ante) + G.deck:hard_set_T() + G.STATE_COMPLETE = false + return true + end, + })) + end, +} + +local revert = { + object_type = "Consumable", + set = "Code", + name = "cry-Revert", + key = "revert", + pos = { x = 3, y = 0 }, + config = {}, + cost = 4, + atlas = "code", + order = 4, + can_use = function(self, card) + return G.GAME.cry_revert + end, + use = function(self, card, area, copier) + G.E_MANAGER:add_event( + Event({ + trigger = "after", + delay = G.SETTINGS.GAMESPEED, + func = function() + G:delete_run() + G:start_run({ + savetext = STR_UNPACK(G.GAME.cry_revert), + }) + end, + }), + "other" + ) + end, +} + +local semicolon = { + object_type = "Consumable", + set = "Code", + name = "cry-Semicolon", + key = "semicolon", + pos = { + x = 0, + y = 1, + }, + config = {}, + cost = 4, + atlas = "code", + order = 32, + can_use = function(self, card) + return G.STATE == G.STATES.SELECTING_HAND and not G.GAME.blind.boss + end, + use = function(self, card, area, copier) + G.E_MANAGER:add_event( + Event({ + trigger = "immediate", + func = function() + if G.STATE ~= G.STATES.SELECTING_HAND then + return false + end + G.GAME.current_round.semicolon = true + G.STATE = G.STATES.HAND_PLAYED + G.STATE_COMPLETE = true + end_round() + return true + end, + }), + "other" + ) + end, +} + +local malware = { + object_type = "Consumable", + set = "Code", + name = "cry-Malware", + key = "malware", + pos = { + x = 1, + y = 1, + }, + config = {}, + cost = 4, + atlas = "code", + order = 9, + can_use = function(self, card) + return #G.hand.cards > 0 + end, + use = function(self, card, area, copier) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + card:juice_up(0.3, 0.5) + return true + end, + })) + for i = 1, #G.hand.cards do + local percent = 1.15 - (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.cards[i]:flip() + play_sound("card1", percent) + G.hand.cards[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.hand.cards do + local CARD = G.hand.cards[i] + local percent = 0.85 + (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + CARD:set_edition({ + cry_glitched = true, + }) + play_sound("tarot2", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + end, +} + +local seed = { + object_type = "Consumable", + set = "Code", + name = "cry-Seed", + key = "seed", + pos = { + x = 3, + y = 1, + }, + config = {}, + cost = 4, + atlas = "code", + order = 12, + can_use = function(self, card) + --the card itself and one other card + return #G.jokers.highlighted + + #G.hand.highlighted + + #G.consumeables.highlighted + + (G.pack_cards and #G.pack_cards.highlighted or 0) + == 2 + end, + loc_vars = function(self, info_queue, card) + info_queue[#info_queue + 1] = { key = "cry_rigged", set = "Other", vars = {} } + end, + use = function(self, card, area, copier) + if area then + area:remove_from_highlighted(card) + end + if G.jokers.highlighted[1] then + G.jokers.highlighted[1].ability.cry_rigged = true + if G.jokers.highlighted[1].config.center.key == "j_cry_googol_play" then + check_for_unlock({ type = "googol_play_rigged" }) + end + end + if G.hand.highlighted[1] then + G.hand.highlighted[1].ability.cry_rigged = true + end + if G.consumeables.highlighted[1] then + G.consumeables.highlighted[1].ability.cry_rigged = true + end + if G.pack_cards and G.pack_cards.highlighted[1] then + G.pack_cards.highlighted[1].ability.cry_rigged = true + end + end, +} +local rigged = { + object_type = "Sticker", + atlas = "sticker", + pos = { x = 5, y = 1 }, + key = "cry_rigged", + no_sticker_sheet = true, + prefix_config = { key = false }, + badge_colour = HEX("14b341"), + draw = function(self, card) --don't draw shine + G.shared_stickers[self.key].role.draw_major = card + G.shared_stickers[self.key]:draw_shader("dissolve", nil, nil, nil, card.children.center) + end, +} + +local hook = { + object_type = "Consumable", + set = "Code", + name = "cry-Hook", + key = "hook", + pos = { + x = 0, + y = 4, + }, + config = {}, + cost = 4, + atlas = "code", + order = 14, + can_use = function(self, card) + return #G.jokers.highlighted == 2 + end, + loc_vars = function(self, info_queue, card) + info_queue[#info_queue + 1] = { key = "cry_hooked", set = "Other", vars = { "hooked Joker" } } + end, + use = function(self, card, area, copier) + G.jokers.highlighted[1].ability.cry_hooked = true + G.jokers.highlighted[2].ability.cry_hooked = true + G.jokers.highlighted[1].hook_id = G.jokers.highlighted[2].sort_id + G.jokers.highlighted[2].hook_id = G.jokers.highlighted[1].sort_id + end, +} +local hooked = { + object_type = "Sticker", + atlas = "sticker", + pos = { x = 5, y = 3 }, + loc_vars = function(self, info_queue, card) + local var + if not card or not card.hook_id then + var = "["..localize("k_joker").."]" + else + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].sort_id == card.hook_id then + var = localize({ type = "name_text", set = "Joker", key = G.jokers.cards[i].config.center.key }) + end + end + var = var or "[no joker found - " .. (card.hook_id or "nil") .. "]" + end + return { vars = { var or "hooked Joker" } } + end, + key = "cry_hooked", + no_sticker_sheet = true, + prefix_config = { key = false }, + badge_colour = HEX("14b341"), + draw = function(self, card) --don't draw shine + G.shared_stickers[self.key].role.draw_major = card + G.shared_stickers[self.key]:draw_shader("dissolve", nil, nil, nil, card.children.center) + end, +} + +local variable = { + object_type = "Consumable", + set = "Code", + key = "variable", + name = "cry-Variable", + atlas = "code", + pos = { + x = 2, + y = 1, + }, + cost = 4, + order = 8, + config = { max_highlighted = 2, extra = { enteredrank = "" } }, + loc_vars = function(self, info_queue, card) + return { vars = { self.config.max_highlighted } } + end, + use = function(self, card, area, copier) + G.GAME.USING_CODE = true + G.ENTERED_RANK = "" + G.CHOOSE_RANK = UIBox({ + definition = create_UIBox_variable(card), + config = { + align = "cm", + offset = { x = 0, y = 10 }, + major = G.ROOM_ATTACH, + bond = "Weak", + instance_type = "POPUP", + }, + }) + G.CHOOSE_RANK.alignment.offset.y = 0 + G.ROOM.jiggle = G.ROOM.jiggle + 1 + G.CHOOSE_RANK:align_to_major() + end, +} +local class = { + object_type = "Consumable", + set = "Code", + key = "class", + name = "cry-Class", + atlas = "code", + pos = { + x = 4, + y = 1, + }, + cost = 4, + order = 16, + config = { max_highlighted = 1, extra = { enteredrank = "" } }, + loc_vars = function(self, info_queue, card) + return { vars = { self.config.max_highlighted } } + end, + use = function(self, card, area, copier) + G.GAME.USING_CODE = true + G.ENTERED_ENH = "" + G.CHOOSE_ENH = UIBox({ + definition = create_UIBox_class(card), + config = { + align = "cm", + offset = { x = 0, y = 10 }, + major = G.ROOM_ATTACH, + bond = "Weak", + instance_type = "POPUP", + }, + }) + G.CHOOSE_ENH.alignment.offset.y = 0 + G.ROOM.jiggle = G.ROOM.jiggle + 1 + G.CHOOSE_ENH:align_to_major() + end, +} +local commit = { + object_type = "Consumable", + set = "Code", + key = "commit", + name = "cry-Commit", + atlas = "code", + pos = { + x = 1, + y = 2, + }, + cost = 4, + order = 31, + can_use = function(self, card) + return #G.jokers.highlighted == 1 + and not G.jokers.highlighted[1].ability.eternal + and not ( + type(G.jokers.highlighted[1].config.center.rarity) == "number" + and G.jokers.highlighted[1].config.center.rarity >= 5 + ) + end, + use = function(self, card, area, copier) + local deleted_joker_key = G.jokers.highlighted[1].config.center.key + local rarity = G.jokers.highlighted[1].config.center.rarity + local legendary = nil + --please someone add a rarity api to steamodded + if rarity == 1 then + rarity = 0 + elseif rarity == 2 then + rarity = 0.9 + elseif rarity == 3 then + rarity = 0.99 + elseif rarity == 4 then + rarity = nil + legendary = true + end -- Deleted check for "cry epic" it was giving rare jokers by setting rarity to 1 + local _first_dissolve = nil + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.75, + func = function() + G.jokers.highlighted[1]:start_dissolve(nil, _first_dissolve) + _first_dissolve = true + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("timpani") + local card = create_card("Joker", G.jokers, legendary, rarity, nil, nil, nil, "cry_commit") + card:add_to_deck() + G.jokers:emplace(card) + card:juice_up(0.3, 0.5) + if card.config.center.key == deleted_joker_key then + check_for_unlock({ type = "pr_unlock" }) + end + return true + end, + })) + end, +} +local merge = { + object_type = "Consumable", + set = "Code", + key = "merge", + name = "cry-Merge", + atlas = "code", + pos = { + x = 0, + y = 2, + }, + cost = 4, + order = 21, + can_use = function(self, card) + if #G.hand.highlighted ~= 1 + (card.area == G.hand and 1 or 0) then + return false + end + if #G.consumeables.highlighted ~= 1 + (card.area == G.consumeables and 1 or 0) then + return false + end + local n = 1 + if G.hand.highlighted[1] == card then + n = 2 + end + if G.hand.highlighted[n].ability.consumeable then + return false + end + local m = 1 + if G.consumeables.highlighted[1] == card then + m = 2 + end + if G.consumeables.highlighted[m].ability.eternal or G.consumeables.highlighted[m].ability.set == "Unique" or not G.consumeables.highlighted[m].ability.consumeable then + return false + end + return true + end, + use = function(self, card, area, copier) + G.E_MANAGER:add_event(Event({ + trigger = "immediate", + func = function() + G.cry_mergearea1 = + CardArea(G.play.T.x, G.play.T.y, G.play.T.w, G.play.T.h, { type = "play", card_limit = 5 }) + G.cry_mergearea2 = + CardArea(G.play.T.x, G.play.T.y, G.play.T.w, G.play.T.h, { type = "play", card_limit = 5 }) + area:remove_from_highlighted(card) + local key = G.consumeables.highlighted[1].config.center.key + local c = G.consumeables.highlighted[1] + local CARD = G.hand.highlighted[1] + card:start_dissolve() + play_sound("card1") + G.consumeables:remove_from_highlighted(c) + CARD.area = G.cry_mergearea1 + c.area = G.cry_mergearea2 + draw_card(G.hand, G.cry_mergearea1, 1, "up", true, CARD) + draw_card(G.consumeables, G.cry_mergearea2, 1, "up", true, c) + delay(0.2) + CARD:flip() + c:flip() + delay(0.2) + local percent = 0.85 + (1 - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("timpani") + c:start_dissolve(nil, nil, 0) + CARD:flip() + CARD:set_ability(G.P_CENTERS[key], true, nil) + play_sound("tarot2", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + delay(0.5) + draw_card(G.cry_mergearea1, G.hand, 1, "up", true, CARD) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.5, + func = function() + G.cry_mergearea2:remove_card(c) + G.cry_mergearea2:remove() + G.cry_mergearea1:remove() + G.cry_mergearea1 = nil + G.cry_mergearea2 = nil + return true + end, + })) + return true + end, + })) + end, +} +local multiply = { + object_type = "Consumable", + set = "Code", + key = "multiply", + name = "cry-Multiply", + atlas = "code", + order = 24, + pos = { + x = 3, + y = 2, + }, + cost = 4, + can_use = function(self, card) + return #G.jokers.highlighted == 1 and not Card.no(G.jokers.highlighted[1], "immune_to_chemach", true) and not Card.no(G.jokers.highlighted[1], "immutable", true) + end, + use = function(self, card, area, copier) + if not G.jokers.highlighted[1].cry_multiply then + G.jokers.highlighted[1].cry_multiply = 1 + end + G.jokers.highlighted[1].cry_multiply = G.jokers.highlighted[1].cry_multiply * 2 + cry_with_deck_effects(G.jokers.highlighted[1], function(card) + cry_misprintize(card, { min = 2, max = 2 }, nil, true) + end) + end, +} +local divide = { + object_type = "Consumable", + set = "Code", + key = "divide", + name = "cry-Divide", + atlas = "code", + order = 23, + pos = { + x = 2, + y = 2, + }, + cost = 4, + can_use = function(self, card) + return G.STATE == G.STATES.SHOP + end, + can_bulk_use = true, + use = function(self, card, area, copier) + for i = 1, #G.shop_jokers.cards do + local c = G.shop_jokers.cards[i] + c.misprint_cost_fac = (c.misprint_cost_fac or 1) * 0.5 + c:set_cost() + end + for i = 1, #G.shop_booster.cards do + local c = G.shop_booster.cards[i] + c.misprint_cost_fac = (c.misprint_cost_fac or 1) * 0.5 + c:set_cost() + end + for i = 1, #G.shop_vouchers.cards do + local c = G.shop_vouchers.cards[i] + c.misprint_cost_fac = (c.misprint_cost_fac or 1) * 0.5 + c:set_cost() + end + end, + bulk_use = function(self, card, area, copier, number) + for i = 1, #G.shop_jokers.cards do + local c = G.shop_jokers.cards[i] + c.misprint_cost_fac = (c.misprint_cost_fac or 1) / (2 ^ number) + c:set_cost() + end + for i = 1, #G.shop_booster.cards do + local c = G.shop_booster.cards[i] + c.misprint_cost_fac = (c.misprint_cost_fac or 1) / (2 ^ number) + c:set_cost() + end + for i = 1, #G.shop_vouchers.cards do + local c = G.shop_vouchers.cards[i] + c.misprint_cost_fac = (c.misprint_cost_fac or 1) / (2 ^ number) + c:set_cost() + end + end, +} +local delete = { + object_type = "Consumable", + set = "Code", + key = "delete", + name = "cry-Delete", + atlas = "code", + order = 18, + pos = { + x = 4, + y = 2, + }, + cost = 4, + can_use = function(self, card) + return G.STATE == G.STATES.SHOP + and card.area == G.consumeables + and #G.shop_jokers.highlighted + #G.shop_booster.highlighted + #G.shop_vouchers.highlighted == 1 + and G.shop_jokers.highlighted[1] ~= self + and G.shop_booster.highlighted[1] ~= self + and G.shop_vouchers.highlighted[1] ~= self + end, + use = function(self, card, area, copier) + if not G.GAME.banned_keys then + G.GAME.banned_keys = {} + end -- i have no idea if this is always initialised already tbh + local a = nil + local c = nil + if G.shop_jokers.highlighted[1] then + a = G.shop_jokers + c = G.shop_jokers.highlighted[1] + end + if G.shop_booster.highlighted[1] then + a = G.shop_booster + c = G.shop_booster.highlighted[1] + end + if G.shop_vouchers.highlighted[1] then + a = G.shop_vouchers + c = G.shop_vouchers.highlighted[1] + if c.shop_voucher then + G.GAME.current_round.voucher = nil + G.GAME.current_round.cry_voucher_edition = nil + G.GAME.current_round.cry_voucher_stickers = + { eternal = false, perishable = false, rental = false, pinned = false, banana = false } + end + end + if c.config.center.rarity == "cry_exotic" then + check_for_unlock({ type = "what_have_you_done" }) + end + G.GAME.banned_keys[c.config.center.key] = true + c:start_dissolve() + end, +} +local spaghetti = { + object_type = "Consumable", + set = "Code", + key = "spaghetti", + name = "cry-Spaghetti", + atlas = "code", + order = 13, + pos = { + x = 5, + y = 2, + }, + cost = 4, + loc_vars = function(self, info_queue, card) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_glitched + info_queue[#info_queue + 1] = { set = "Other", key = "food_jokers" } + end, + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + local card = create_card( + "Joker", + G.jokers, + nil, + nil, + nil, + nil, + Cryptid.get_food("cry_spaghetti") + ) + card:set_edition({ + cry_glitched = true, + }) + card:add_to_deck() + G.jokers:emplace(card) + end, +} +local machinecode = { + object_type = "Consumable", + set = "Code", + name = "cry-Machine Code", + key = "machinecode", + pos = { x = 0, y = 3 }, + cost = 3, + atlas = "code", + order = 19, + can_use = function(self, card) + return true + end, + can_bulk_use = true, + use = function(self, card, area, copier) + local card = create_card("Consumeables", G.consumables, nil, nil, nil, nil, nil, "cry_machinecode") + card:set_edition({ cry_glitched = true }) + card:add_to_deck() + G.consumeables:emplace(card) + end, +} +local run = { + object_type = "Consumable", + set = "Code", + name = "cry-Run", + key = "run", + pos = { x = 5, y = 0 }, + cost = 3, + atlas = "code", + order = 6, + can_use = function(self, card) + return G.GAME.blind and G.GAME.blind.in_blind + end, + can_bulk_use = true, + use = function(self, card, area, copier) + G.cry_runarea = CardArea( + G.discard.T.x, + G.discard.T.y, + G.discard.T.w, + G.discard.T.h, + { type = "discard", card_limit = 1e100 } + ) + local hand_count = #G.hand.cards + for i = 1, hand_count do + draw_card(G.hand, G.cry_runarea, i * 100 / hand_count, "down", nil, nil, 0.07) + end + G.E_MANAGER:add_event(Event({ + trigger = "immediate", + func = function() + G.GAME.current_round.jokers_purchased = 0 + G.STATE = G.STATES.SHOP + G.GAME.USING_CODE = true + G.GAME.USING_RUN = true + G.GAME.RUN_STATE_COMPLETE = 0 + G.GAME.shop_free = nil + G.GAME.shop_d6ed = nil + G.STATE_COMPLETE = false + return true + end, + })) + end, +} +local exploit = { + object_type = "Consumable", + set = "Code", + key = "exploit", + name = "cry-Exploit", + atlas = "code", + pos = { + x = 1, + y = 3, + }, + cost = 4, + order = 28, + config = { extra = { enteredhand = "" } }, -- i don't think this ever uses config...? + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + G.GAME.USING_CODE = true + G.ENTERED_HAND = "" + G.CHOOSE_HAND = UIBox({ + definition = create_UIBox_exploit(card), + config = { + align = "cm", + offset = { x = 0, y = 10 }, + major = G.ROOM_ATTACH, + bond = "Weak", + instance_type = "POPUP", + }, + }) + G.CHOOSE_HAND.alignment.offset.y = 0 + G.ROOM.jiggle = G.ROOM.jiggle + 1 + G.CHOOSE_HAND:align_to_major() + end, +} +local oboe = { + object_type = "Consumable", + set = "Code", + key = "oboe", + name = "cry-oboe", + atlas = "code", + order = 10, + config = { extra = { choices = 1 } }, + pos = { + x = 2, + y = 3, + }, + cost = 4, + can_bulk_use = true, + loc_vars = function(self, info_queue, card) + if not card then + return { vars = { self.config.extra.choices, (G.GAME and G.GAME.cry_oboe or 0) } } + end + return { vars = { card.ability.extra.choices, (G.GAME and G.GAME.cry_oboe or 0) } } + end, + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + G.GAME.cry_oboe = (G.GAME.cry_oboe or 0) + card.ability.extra.choices + end, + bulk_use = function(self, card, area, copier, number) + G.GAME.cry_oboe = (G.GAME.cry_oboe or 0) + (card.ability.extra.choices * number) + end, +} +local rework = { + object_type = "Consumable", + set = "Code", + key = "rework", + name = "cry-Rework", + atlas = "code", + order = 25, + pos = { + x = 3, + y = 3, + }, + cost = 4, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = + { set = "Tag", key = "tag_cry_rework", specific_vars = { "[edition]", "[joker]" } } + return { vars = {} } + end, + can_use = function(self, card) + --todo: nostalgic deck compat + return #G.jokers.highlighted == 1 and not G.jokers.highlighted[1].ability.eternal + and G.jokers.highlighted[1].ability.name ~= "cry-meteor" + and G.jokers.highlighted[1].ability.name ~= "cry-exoplanet" + and G.jokers.highlighted[1].ability.name ~= "cry-stardust" + and G.jokers.highlighted[1].config.center.rarity ~= "cry_cursed" + end, + use = function(self, card, area, copier) + local jkr = G.jokers.highlighted[1] + local found_index = 1 + if jkr.edition then + for i, v in ipairs(G.P_CENTER_POOLS.Edition) do + if v.key == jkr.edition.key then + found_index = i + break + end + end + end + found_index = found_index + 1 + if found_index > #G.P_CENTER_POOLS.Edition then + found_index = found_index - #G.P_CENTER_POOLS.Edition + end + local tag = Tag("tag_cry_rework") + if not tag.ability then + tag.ability = {} + end + tag.ability.rework_key = jkr.config.center.key + tag.ability.rework_edition = G.P_CENTER_POOLS.Edition[found_index].key + add_tag(tag) + --SMODS.Tags.tag_cry_rework.apply(tag, {type = "store_joker_create"}) + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.75, + func = function() + jkr:start_dissolve() + return true + end, + })) + end, +} +local rework_tag = { + object_type = "Tag", + atlas = "tag_cry", + name = "cry-Rework Tag", + order = 19, + pos = { x = 0, y = 3 }, + config = { type = "store_joker_create" }, + key = "rework", + ability = { rework_edition = nil, rework_key = nil }, + apply = function(self, tag, context) + if context.type == "store_joker_create" then + local card = create_card("Joker", context.area, nil, nil, nil, nil, (tag.ability.rework_key or "j_scholar")) + create_shop_card_ui(card, "Joker", context.area) + card:set_edition((tag.ability.rework_edition or "e_foil"), true, nil, true) + card.states.visible = false + tag:yep("+", G.C.FILTER, function() + card:start_materialize() + return true + end) + tag.triggered = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.5, + func = function() + save_run() --fixes savescum bugs hopefully? + return true + end, + })) + return card + end + end, + in_pool = function() + return false + end, +} + +local patch = { + object_type = "Consumable", + set = "Code", + key = "patch", + name = "cry-patch", + atlas = "code", + order = 26, + config = { }, + pos = { + x = 1, + y = 4, + }, + cost = 4, + can_bulk_use = true, + loc_vars = function(self, info_queue, card) + return { } + end, + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + for i = 1, #G.hand.cards do + local CARD = G.hand.cards[i] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + return true + end, + })) + end + for i = 1, #G.jokers.cards do + local CARD = G.jokers.cards[i] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + return true + end, + })) + end + for i = 1, #G.consumeables.cards do + local CARD = G.consumeables.cards[i] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + return true + end, + })) + end + delay(0.2) + for i = 1, #G.hand.cards do + local CARD = G.hand.cards[i] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + if CARD.facing == "back" then + CARD:flip() + end + CARD.debuff = false + CARD.cry_debuff_immune = true + CARD.ability.perishable = nil + CARD.pinned = nil + CARD:set_rental(nil) + if not CARD.sob then + CARD:set_eternal(nil) + end + CARD.ability.banana = nil + if Cryptid.enabled["Spooky"] then + CARD.ability.cry_possessed = nil + SMODS.Stickers.cry_flickering:apply(CARD, nil) + end + play_sound("tarot2", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + for i = 1, #G.jokers.cards do + local CARD = G.jokers.cards[i] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + if CARD.facing == "back" then + CARD:flip() + end + CARD.debuff = false + CARD.ability.perishable = nil + CARD.pinned = nil + CARD:set_rental(nil) + if not CARD.sob then + CARD:set_eternal(nil) + end + CARD.ability.banana = nil + if Cryptid.enabled["Spooky"] then + CARD.ability.cry_possessed = nil + SMODS.Stickers.cry_flickering:apply(CARD, nil) + end + play_sound("card1", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + for i = 1, #G.consumeables.cards do + local CARD = G.consumeables.cards[i] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + if CARD.facing == "back" then + CARD:flip() + end + CARD.debuff = false + CARD.ability.perishable = nil + CARD.pinned = nil + CARD:set_rental(nil) + if not CARD.sob then + CARD:set_eternal(nil) + end + CARD.ability.banana = nil + if Cryptid.enabled["Spooky"] then + CARD.ability.cry_possessed = nil + SMODS.Stickers.cry_flickering:apply(CARD, nil) + end + play_sound("card1", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + end, +} + +local ctrl_v = { + object_type = "Consumable", + set = "Code", + key = "ctrl_v", + name = "cry-Ctrl-V", + atlas = "code", + order = 27, + config = { }, + pos = { + x = 2, + y = 4, + }, + cost = 4, + can_bulk_use = true, + loc_vars = function(self, info_queue, card) + return { } + end, + can_use = function(self, card) + return #G.hand.highlighted + + #G.consumeables.highlighted + == 2 + end, + use = function(self, card, area, copier) + if area then + area:remove_from_highlighted(card) + end + if G.hand.highlighted[1] then + G.E_MANAGER:add_event(Event({ + func = function() + local card = copy_card(G.hand.highlighted[1]) + card:add_to_deck() + table.insert(G.playing_cards, card) + G.hand:emplace(card) + return true + end, + })) + end + if G.consumeables.highlighted[1] then + G.E_MANAGER:add_event(Event({ + func = function() + local card = copy_card(G.consumeables.highlighted[1]) + card:add_to_deck() + if Incantation then + card:setQty(1) + end + G.consumeables:emplace(card) + return true + end, + })) + end + end, + bulk_use = function(self, card, area, copier, number) + for i = 1, number do + if area then + area:remove_from_highlighted(card) + end + if G.hand.highlighted[1] then + G.E_MANAGER:add_event(Event({ + func = function() + local card = copy_card(G.hand.highlighted[1]) + card:add_to_deck() + G.hand:emplace(card) + return true + end, + })) + end + if G.consumeables.highlighted[1] then + G.E_MANAGER:add_event(Event({ + func = function() + local card = copy_card(G.consumeables.highlighted[1]) + card:add_to_deck() + if Incantation then + card:setQty(1) + end + G.consumeables:emplace(card) + return true + end, + })) + end + end + end, +} + + + +local inst = { + object_type = "Consumable", + set = "Code", + key = "inst", + name = "cry-Inst", + atlas = "code", + order = 28, + config = { }, + pos = { + x = 3, + y = 4, + }, + cost = 4, + can_bulk_use = true, + loc_vars = function(self, info_queue, card) + return { } + end, + can_use = function(self, card) + local selected_cards = {} + for i = 1, #G.hand.highlighted do + if G.hand.highlighted[i] ~= card then selected_cards[#selected_cards+1] = G.hand.highlighted[i] end + end + return #selected_cards == 1 + end, + use = function(self, card, area, copier) + for i = 1, #G.deck.cards do + if G.deck.cards[i].base.value == G.hand.highlighted[1].base.value then + draw_card(G.deck,G.hand,nil,nil,false,G.deck.cards[i]) + break + end + end + for i = 1, #G.deck.cards do + if G.deck.cards[i].base.suit == G.hand.highlighted[1].base.suit then + draw_card(G.deck,G.hand,nil,nil,false,G.deck.cards[i]) + break + end + end + end, + bulk_use = function(self, card, area, copier, number) + for i = 1, number do + for i = 1, #G.deck.cards do + if G.deck.cards[i].base.value == G.hand.highlighted[1].base.value then + draw_card(G.deck,G.hand,nil,nil,false,G.deck.cards[i]) + break + end + end + for i = 1, #G.deck.cards do + if G.deck.cards[i].base.suit == G.hand.highlighted[1].base.suit then + draw_card(G.deck,G.hand,nil,nil,false,G.deck.cards[i]) + break + end + end + end + end, +} + +local automaton = { + object_type = "Consumable", + set = "Tarot", + name = "cry-Automaton", + key = "automaton", + pos = { x = 5, y = 1 }, + config = { create = 1 }, + order = 5, + atlas = "code", + loc_vars = function(self, info_queue, card) + return { vars = { self.config.create } } + end, + can_use = function(self, card) + return #G.consumeables.cards < G.consumeables.config.card_limit or card.area == G.consumeables + end, + use = function(self, card, area, copier) + for i = 1, math.min(card.ability.consumeable.create, G.consumeables.config.card_limit - #G.consumeables.cards) do + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + if G.consumeables.config.card_limit > #G.consumeables.cards then + play_sound("timpani") + local _card = create_card("Code", G.consumeables, nil, nil, nil, nil, nil, "cry_automaton") + _card:add_to_deck() + G.consumeables:emplace(_card) + card:juice_up(0.3, 0.5) + end + return true + end, + })) + end + delay(0.6) + end, +} + +local green_seal = { + object_type = "Seal", + name = "cry-Green-Seal", + key = "green", + badge_colour = HEX("12f254"), --same as code cards + atlas = "cry_misc", + pos = { x = 1, y = 2 }, + + calculate = function(self, card, context) + if context.unscoring then + G.E_MANAGER:add_event(Event({ + trigger = "after", + func = function() + if G.consumeables.config.card_limit > #G.consumeables.cards then + local c = create_card("Code", G.consumeables, nil, nil, nil, nil, nil, "cry_green_seal") + c:add_to_deck() + G.consumeables:emplace(c) + card:juice_up() + end + return true + end, + })) + return true + end + end, +} + +local source = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Source", + order = 9, + key = "source", + config = { + -- This will add a tooltip. + mod_conv = "cry_green_seal", + -- Tooltip args + max_highlighted = 1, + }, + loc_vars = function(self, info_queue, center) + -- Handle creating a tooltip with set args. + info_queue[#info_queue + 1] = { set = "Other", key = "cry_green_seal" } + return { vars = { center.ability.max_highlighted } } + end, + cost = 4, + atlas = "atlasnotjokers", + pos = { x = 2, y = 4 }, + use = function(self, card, area, copier) --Good enough + for i = 1, #G.hand.highlighted do + local highlighted = G.hand.highlighted[i] + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + highlighted:juice_up(0.3, 0.5) + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.1, + func = function() + if highlighted then + highlighted:set_seal("cry_green") + end + return true + end, + })) + delay(0.5) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + G.hand:unhighlight_all() + return true + end, + })) + end + end, +} +local pointer = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Pointer", + key = "pointer", + pos = { x = 4, y = 3 }, + hidden = true, + soul_set = "Code", + order = 41, + atlas = "code", + can_use = function(self, card) + return true + end, + loc_vars = function(self, info_queue, center) + return { vars = { (SMODS.Mods["jen"] or {}).can_load and "and OMEGA consumables " or "" } } + end, + use = function(self, card, area, copier) + G.GAME.USING_CODE = true + G.GAME.USING_POINTER = true + G.ENTERED_CARD = "" + G.CHOOSE_CARD = UIBox({ + definition = create_UIBox_pointer(card), + config = { + align = "cm", + offset = { x = 0, y = 10 }, + major = G.ROOM_ATTACH, + bond = "Weak", + instance_type = "POPUP", + }, + }) + G.CHOOSE_CARD.alignment.offset.y = 0 + G.ROOM.jiggle = G.ROOM.jiggle + 1 + G.CHOOSE_CARD:align_to_major() + end, +} + +local encoded = { + object_type = "Back", + name = "cry-Encoded", + key = "encoded", + order = 11, + config = { cry_encoded = true, cry_encoded_downside = true }, + pos = { x = 2, y = 5 }, + atlas = "atlasdeck", +} + +local source_deck = { + object_type = "Back", + name = "cry-Source Deck", + key = "source_deck", + order = 12, + config = { cry_force_seal = "cry_green" }, + pos = { x = 3, y = 5 }, + loc_txt = { + name = "Source Deck", --not localizing enhanced decks for now; they will be handled automatically later + text = { + "All cards have a {C:cry_code}Green Seal{}", + "Cards cannot change seals", + }, + }, + atlas = "atlasenhanced", +} + +local CodeJoker = { + object_type = "Joker", + name = "cry-CodeJoker", + key = "CodeJoker", + pos = { x = 2, y = 4 }, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { key = "e_negative_consumable", set = "Edition", config = { extra = 1 } } + end, + rarity = "cry_epic", + cost = 11, + order = 109, + blueprint_compat = true, + atlas = "atlasepic", + calculate = function(self, card, context) + if context.setting_blind and not (context.blueprint_card or self).getting_sliced then + play_sound("timpani") + local card = create_card("Code", G.consumables, nil, nil, nil, nil) + card:set_edition({ + negative = true, + }) + card:add_to_deck() + G.consumeables:emplace(card) + card:juice_up(0.3, 0.5) + return nil, true + end + end, + cry_credits = { + idea = { + "Kailen" + }, + art = { + "Kailen" + }, + code = { + "Kailen" + } + }, +} + +local copypaste = { + object_type = "Joker", + name = "cry-copypaste", + key = "copypaste", + pos = { x = 3, y = 4 }, + order = 110, + immune_to_chemach = true, + config = { extra = { odds = 2, ckt = 0 } }, + rarity = "cry_epic", + cost = 14, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { + vars = { "" .. (G.GAME and G.GAME.probabilities.normal or 1), (center and center.ability.extra.odds or 2) }, + } + end, + atlas = "atlasepic", + calculate = function(self, card, context) + if + context.using_consumeable + and context.consumeable.ability.set == "Code" + and not context.consumeable.beginning_end + then + if #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + if pseudorandom("cry_copypaste_joker") < G.GAME.probabilities.normal / card.ability.extra.odds then + G.E_MANAGER:add_event(Event({ + func = function() + local cards = copy_card(context.consumeable) + cards:add_to_deck() + G.consumeables:emplace(cards) + return true + end, + })) + card_eval_status_text( + context.blueprint_cards or card, + "extra", + nil, + nil, + nil, + { message = localize("k_copied_ex") } + ) + end + end + end + end, + cry_credits = { + idea = { + "Auto Watto" + }, + art = { + "Kailen" + }, + code = { + "Auto Watto" + } + }, +} +local cut = { + object_type = "Joker", + name = "cry-cut", + key = "cut", + config = { extra = { Xmult = 1, Xmult_mod = 0.5 } }, + pos = { x = 2, y = 2 }, + rarity = 2, + cost = 7, + order = 108, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasthree", + calculate = function(self, card, context) + if context.ending_shop then + local destructable_codecard = {} + for i = 1, #G.consumeables.cards do + if + G.consumeables.cards[i].ability.set == "Code" + and not G.consumeables.cards[i].getting_sliced + and not G.consumeables.cards[i].ability.eternal + then + destructable_codecard[#destructable_codecard + 1] = G.consumeables.cards[i] + end + end + local codecard_to_destroy = #destructable_codecard > 0 + and pseudorandom_element(destructable_codecard, pseudoseed("cut")) + or nil + + if codecard_to_destroy then + codecard_to_destroy.getting_sliced = true + card.ability.extra.Xmult = card.ability.extra.Xmult + card.ability.extra.Xmult_mod + G.E_MANAGER:add_event(Event({ + func = function() + (context.blueprint_card or card):juice_up(0.8, 0.8) + codecard_to_destroy:start_dissolve({ G.C.RED }, nil, 1.6) + return true + end, + })) + if not (context.blueprint_card or self).getting_sliced then + card_eval_status_text((context.blueprint_card or card), "extra", nil, nil, nil, { + message = localize{type='variable',key='a_xmult',vars={number_format(to_big(card.ability.extra.Xmult))}} + }) + end + return nil, true + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.Xmult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize{type='variable',key='a_xmult',vars={number_format(card.ability.extra.Xmult)}}, + Xmult_mod = card.ability.extra.Xmult, + colour = G.C.MULT, + } + end + end, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.Xmult_mod, center.ability.extra.Xmult } } + end, + cry_credits = { + idea = { + "Auto Watto" + }, + art = { + "Kailen" + }, + code = { + "Auto Watto" + } + }, +} +local blender = { + object_type = "Joker", + name = "cry-blender", + key = "blender", + pos = { x = 3, y = 2 }, + rarity = 1, + cost = 5, + blueprint_compat = true, + atlas = "atlasthree", + order = 111, + calculate = function(self, card, context) + if + context.using_consumeable + and context.consumeable.ability.set == "Code" + and not context.consumeable.beginning_end + then + if #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + local card = create_card("Consumeables", G.consumables, nil, nil, nil, nil, nil, "cry_blender") + card:add_to_deck() + G.consumeables:emplace(card) + end + end + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "Kailen" + }, + code = { + "Kailen" + } + }, +} +local python = { + object_type = "Joker", + name = "cry-python", + key = "python", + config = { extra = { Xmult = 1, Xmult_mod = 0.15 } }, + pos = { x = 4, y = 2 }, + rarity = 2, + cost = 7, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasthree", + order = 112, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.Xmult_mod, center.ability.extra.Xmult } } + end, + calculate = function(self, card, context) + if + context.using_consumeable + and context.consumeable.ability.set == "Code" + and not context.consumeable.beginning_end + then + card.ability.extra.Xmult = card.ability.extra.Xmult + card.ability.extra.Xmult_mod + G.E_MANAGER:add_event(Event({ + func = function() + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { card.ability.extra.Xmult }, + }), + } + ) + return true + end, + })) + return + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.Xmult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.Xmult } }), + Xmult_mod = card.ability.extra.Xmult, + } + end + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "Kailen" + }, + code = { + "Kailen" + } + }, +} + +function create_UIBox_variable(card) + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.REFRESH_ALERTS = true + return true + end, + })) + local t = create_UIBox_generic_options({ + no_back = true, + colour = HEX("04200c"), + outline_colour = G.C.SECONDARY_SET.Code, + contents = { + { + n = G.UIT.R, + nodes = { + create_text_input({ + colour = G.C.SET.Code, + hooked_colour = darken(copy_table(G.C.SET.Code), 0.3), + w = 4.5, + h = 1, + max_length = 16, + extended_corpus = true, + prompt_text = localize("cry_code_rank"), + ref_table = G, + ref_value = "ENTERED_RANK", + keyboard_offset = 1, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.SET.Code, + button = "variable_apply", + label = { localize("cry_code_apply") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.RED, + button = "variable_apply_previous", + label = { localize("cry_code_apply_previous") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.RED, + button = "variable_cancel", + label = { localize("cry_code_cancel") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + }, + }) + return t +end + +function create_UIBox_class(card) + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.REFRESH_ALERTS = true + return true + end, + })) + local t = create_UIBox_generic_options({ + no_back = true, + colour = HEX("04200c"), + outline_colour = G.C.SECONDARY_SET.Code, + contents = { + { + n = G.UIT.R, + nodes = { + create_text_input({ + colour = G.C.SET.Code, + hooked_colour = darken(copy_table(G.C.SET.Code), 0.3), + w = 4.5, + h = 1, + max_length = 16, + prompt_text = localize("cry_code_enh"), + ref_table = G, + ref_value = "ENTERED_ENH", + keyboard_offset = 1, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.SET.Code, + button = "class_apply", + label = { localize("cry_code_apply") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.RED, + button = "class_apply_previous", + label = { localize("cry_code_apply_previous") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.RED, + button = "class_cancel", + label = { localize("cry_code_cancel") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + }, + }) + return t +end + +function create_UIBox_exploit(card) + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.REFRESH_ALERTS = true + return true + end, + })) + local t = create_UIBox_generic_options({ + no_back = true, + colour = HEX("04200c"), + outline_colour = G.C.SECONDARY_SET.Code, + contents = { + { + n = G.UIT.R, + nodes = { + create_text_input({ + colour = G.C.SET.Code, + hooked_colour = darken(copy_table(G.C.SET.Code), 0.3), + w = 4.5, + h = 1, + max_length = 24, + extended_corpus = true, + prompt_text = localize("cry_code_hand"), + ref_table = G, + ref_value = "ENTERED_HAND", + keyboard_offset = 1, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.SET.Code, + button = "exploit_apply", + label = { localize("cry_code_exploit") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.RED, + button = "exploit_apply_previous", + label = { localize("cry_code_exploit_previous") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + nodes = { + UIBox_button({ + colour = G.C.RED, + button = "exploit_cancel", + label = { localize("cry_code_cancel") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + }, + }) + return t +end + +function create_UIBox_crash(card) + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.REFRESH_ALERTS = true + return true + end, + })) + local t = create_UIBox_generic_options({ + no_back = true, + colour = HEX("04200c"), + outline_colour = G.C.SECONDARY_SET.Code, + contents = { + { + n = G.UIT.R, + nodes = { + create_text_input({ + colour = G.C.SET.Code, + hooked_colour = darken(copy_table(G.C.SET.Code), 0.3), + w = 4.5, + h = 1, + max_length = 2500, + extended_corpus = true, + prompt_text = "???", + ref_table = G, + ref_value = "ENTERED_ACE", + keyboard_offset = 1, + }), + }, + }, + { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + UIBox_button({ + colour = G.C.SET.Code, + button = "ca", + label = { localize("cry_code_execute") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + }, + }) + return t +end + +function create_UIBox_pointer(card) + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.REFRESH_ALERTS = true + return true + end, + })) + local t = create_UIBox_generic_options({ + no_back = true, + colour = HEX("04200c"), + outline_colour = G.C.SECONDARY_SET.Code, + contents = { + { + n = G.UIT.R, + nodes = { + create_text_input({ + colour = G.C.SET.Code, + hooked_colour = darken(copy_table(G.C.SET.Code), 0.3), + w = 4.5, + h = 1, + max_length = 100, + extended_corpus = true, + prompt_text = localize("cry_code_enter_card"), + ref_table = G, + ref_value = "ENTERED_CARD", + keyboard_offset = 1, + }), + }, + }, + { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + UIBox_button({ + colour = G.C.SET.Code, + button = "pointer_apply", + label = { localize("cry_code_create") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + UIBox_button({ + colour = G.C.SET.Code, + button = "your_collection", + label = { localize("b_collection_cap") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + UIBox_button({ + colour = G.C.RED, + button = "pointer_apply_previous", + label = { localize("cry_code_create_previous") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + UIBox_button({ + colour = G.C.RED, + button = "pointer_cancel", + label = { localize("cry_code_cancel") }, + minw = 4.5, + focus_args = { snap_to = true }, + }), + }, + }, + }, + }) + return t +end + +G.FUNCS.pointer_cancel = function() + G.CHOOSE_CARD:remove() + G.GAME.USING_CODE = false + G.GAME.USING_POINTER = false +end + +G.FUNCS.variable_apply_previous = function() + if G.PREVIOUS_ENTERED_RANK then + G.ENTERED_RANK = G.PREVIOUS_ENTERED_RANK or "" + end + G.FUNCS.variable_apply() +end + +G.FUNCS.variable_apply = function() + local rank_table = { + {}, + { "2", "Two", "II" }, + { "3", "Three", "III" }, + { "4", "Four", "IV" }, + { "5", "Five", "V" }, + { "6", "Six", "VI" }, + { "7", "Seven", "VII" }, + { "8", "Eight", "VIII" }, + { "9", "Nine", "IX" }, + { "10", "1O", "Ten", "X", "T" }, + { "J", "Jack" }, + { "Q", "Queen" }, + { "K", "King" }, + { "A", "Ace", "One" }, + { "M" }, + { "nil" }, + } + + local rank_suffix = nil + + for i, v in pairs(rank_table) do + for j, k in pairs(v) do + if string.lower(G.ENTERED_RANK) == string.lower(k) then + rank_suffix = i + end + end + end + + if rank_suffix then + G.PREVIOUS_ENTERED_RANK = G.ENTERED_RANK + G.GAME.USING_CODE = false + if rank_suffix == 15 then + check_for_unlock({ type = "cheat_used" }) + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") + card:add_to_deck() + G.jokers:emplace(card) + elseif rank_suffix == 16 then + check_for_unlock({ type = "cheat_used" }) + local card = create_card("Code", G.consumeables, nil, nil, nil, nil, "c_cry_crash") + card:add_to_deck() + G.consumeables:emplace(card) + elseif rank_suffix == 17 then + check_for_unlock({ type = "cheat_used" }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + return true + end, + })) + for i = 1, #G.hand.highlighted do + local percent = 1.15 - (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.highlighted[i]:flip() + play_sound("card1", percent) + G.hand.highlighted[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.hand.highlighted do + local CARD = G.hand.highlighted[i] + local percent = 0.85 + (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + CARD:set_ability( + G.P_CENTERS[pseudorandom_element(G.P_CENTER_POOLS.Consumeables, pseudoseed("cry_variable")).key], + true, + nil + ) + play_sound("tarot2", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + else + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + return true + end, + })) + for i = 1, #G.hand.highlighted do + local percent = 1.15 - (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.highlighted[i]:flip() + play_sound("card1", percent) + G.hand.highlighted[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.hand.highlighted do + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.1, + func = function() + local card = G.hand.highlighted[i] + local suit_prefix = string.sub(card.base.suit, 1, 1) .. "_" + local r2suffix = nil + if rank_suffix < 10 then + r2suffix = tostring(rank_suffix) + elseif rank_suffix == 10 then + r2suffix = "T" + elseif rank_suffix == 11 then + r2suffix = "J" + elseif rank_suffix == 12 then + r2suffix = "Q" + elseif rank_suffix == 13 then + r2suffix = "K" + elseif rank_suffix == 14 then + r2suffix = "A" + end + card:set_base(G.P_CARDS[suit_prefix .. r2suffix]) + return true + end, + })) + end + for i = 1, #G.hand.highlighted do + local percent = 0.85 + (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.highlighted[i]:flip() + play_sound("tarot2", percent, 0.6) + G.hand.highlighted[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + G.hand:unhighlight_all() + return true + end, + })) + delay(0.5) + end + G.CHOOSE_RANK:remove() + end +end + +G.FUNCS.variable_cancel = function() + G.CHOOSE_RANK:remove() + G.GAME.USING_CODE = false +end + +G.FUNCS.exploit_apply_previous = function() + if G.PREVIOUS_ENTERED_HAND then + G.ENTERED_HAND = G.PREVIOUS_ENTERED_HAND or "" + end + G.FUNCS.exploit_apply() +end +G.FUNCS.exploit_apply = function() + local hand_table = { + ["High Card"] = { "high card", "high", "1oak", "1 of a kind", "haha one" }, + ["Pair"] = { "pair", "2oak", "2 of a kind", "m" }, + ["Two Pair"] = { "two pair", "2 pair", "mm" }, + ["Three of a Kind"] = { "three of a kind", "3 of a kind", "3oak", "trips", "triangle" }, + ["Straight"] = { "straight", "lesbian", "gay", "bisexual", "asexual" }, + ["Flush"] = { "flush", "skibidi", "toilet", "floosh" }, + ["Full House"] = { "full house", "full", "that 70s show", "modern family", "family matters", "the middle" }, + ["Four of a Kind"] = { "four of a kind", "4 of a kind", "4oak", "22oakoak", "quads", "four to the floor" }, + ["Straight Flush"] = { "straight flush", "strush", "slush", "slushie", "slushy" }, + ["Five of a Kind"] = { "five of a kind", "5 of a kind", "5oak", "quints" }, + ["Flush House"] = { "flush house", "flouse", "outhouse" }, + ["Flush Five"] = { "flush five", "fish", "you know what that means", "five of a flush" }, + ["cry_Bulwark"] = { "bulwark", "flush rock", "stoned", "stone flush", "flush stone" }, + ["cry_Clusterfuck"] = { "clusterfuck", "fuck", "wtf" }, + ["cry_UltPair"] = { "ultimate pair", "ultpair", "ult pair", "pairpairpair" }, + ["cry_WholeDeck"] = { "the entire fucking deck", "deck", "tefd", "fifty-two", "you are fuck deck" }, + } + local current_hand = nil + for k, v in pairs(SMODS.PokerHands) do + local index = v.key + local current_name = G.localization.misc.poker_hands[index] + if not hand_table[v.key] then + hand_table[v.key] = { current_name } + end + end + for i, v in pairs(hand_table) do + for j, k in pairs(v) do + if string.lower(G.ENTERED_HAND) == string.lower(k) then + current_hand = i + end + end + end + if current_hand and G.GAME.hands[current_hand].visible then + G.PREVIOUS_ENTERED_HAND = G.ENTERED_HAND + G.GAME.cry_exploit_override = current_hand + G.FUNCS.exploit_cancel() + return + end +end + +G.FUNCS.exploit_cancel = function() + G.CHOOSE_HAND:remove() + G.GAME.USING_CODE = false +end + +G.FUNCS.class_apply_previous = function() + if G.PREVIOUS_ENTERED_ENH then + G.ENTERED_ENH = G.PREVIOUS_ENTERED_ENH or "" + end + G.FUNCS.class_apply() +end +--todo: mod support +G.FUNCS.class_apply = function() + local enh_table = { + m_bonus = { "bonus" }, + m_mult = { "mult", "red" }, + m_wild = { "wild", "suit" }, + m_glass = { "glass", "xmult" }, + m_steel = { "steel", "metal", "grey" }, + m_stone = { "stone", "chip", "chips" }, + m_gold = { "gold", "money", "yellow" }, + m_lucky = { "lucky", "rng" }, + m_cry_echo = { "echo", "retrigger", "retriggers" }, + ccd = { "ccd" }, + null = { "nil" }, + } + + local enh_suffix = nil + + for i, v in pairs(enh_table) do + for j, k in pairs(v) do + if string.lower(G.ENTERED_ENH) == string.lower(k) then + enh_suffix = i + end + end + end + + if enh_suffix then + G.PREVIOUS_ENTERED_ENH = G.ENTERED_ENH + G.GAME.USING_CODE = false + if enh_suffix == "ccd" then + check_for_unlock({ type = "cheat_used" }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + return true + end, + })) + for i = 1, #G.hand.highlighted do + local percent = 1.15 - (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.highlighted[i]:flip() + play_sound("card1", percent) + G.hand.highlighted[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.hand.highlighted do + local CARD = G.hand.highlighted[i] + local percent = 0.85 + (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + CARD:set_ability(get_random_consumable("cry_class"), true, nil) + play_sound("tarot2", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + elseif enh_suffix == "null" then + local destroyed_cards = {} + check_for_unlock({ type = "cheat_used" }) + for i = #G.hand.highlighted, 1, -1 do + local card = G.hand.highlighted[i] + if not card.ability.eternal then + destroyed_cards[#destroyed_cards + 1] = G.hand.highlighted[i] + if card.ability.name == "Glass Card" then + card:shatter() + else + card:start_dissolve(nil, i == #G.hand.highlighted) + end + end + end + if destroyed_cards[1] then + for j=1, #G.jokers.cards do + eval_card(G.jokers.cards[j], {cardarea = G.jokers, remove_playing_cards = true, removed = destroyed_cards}) + end + end + G.CHOOSE_ENH:remove() + return + else + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + return true + end, + })) + for i = 1, #G.hand.highlighted do + local percent = 1.15 - (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.highlighted[i]:flip() + play_sound("card1", percent) + G.hand.highlighted[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.hand.highlighted do + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.1, + func = function() + G.hand.highlighted[i]:set_ability(G.P_CENTERS[enh_suffix]) + return true + end, + })) + end + for i = 1, #G.hand.highlighted do + local percent = 0.85 + (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.highlighted[i]:flip() + play_sound("tarot2", percent, 0.6) + G.hand.highlighted[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + G.hand:unhighlight_all() + return true + end, + })) + delay(0.5) + G.CHOOSE_ENH:remove() + end +end + +G.FUNCS.class_cancel = function() + G.GAME.USING_CODE = false + G.CHOOSE_ENH:remove() +end + +G.FUNCS.ca = function() + G.GAME.USING_CODE = false + loadstring(G.ENTERED_ACE)() --Scary! + glitched_intensity = 0 + G.SETTINGS.GRAPHICS.crt = 0 + check_for_unlock({ type = "ach_cry_used_crash" }) + G.CHOOSE_ACE:remove() + G.ENTERED_ACE = nil +end +G.FUNCS.pointer_apply_previous = function() + if G.PREVIOUS_ENTERED_CARD then + G.ENTERED_CARD = G.PREVIOUS_ENTERED_CARD or "" + end + G.FUNCS.pointer_apply() +end + +local aliases = { + jimbo = "joker", + ["gary mccready"] = "joker", + greedy = "greedy joker", + lusty = "lusty joker", + wrathful = "wrathful joker", + gluttonous = "gluttonous joker", + jolly = "jolly joker", + zany = "zany joker", + mad = "mad joker", + crazy = "crazy joker", + droll = "droll joker", + sly = "sly joker", + wily = "wily joker", + clever = "clever joker", + devious = "devious joker", + crafty = "crafty joker", + half = "half joker", + stencil = "joker stencil", + dagger = "ceremonial dagger", + chaos = "chaos the clown", + fib = "fibonacci", + scary = "scary face", + abstract = "abstract joker", + delayedgrat = "delayed gratification", + banana = "gros michel", + steven = "even steven", + todd = "odd todd", + bus = "ride the bus", + faceless = "faceless joker", + todo = "to do list", + ["to-do"] = "to do list", + square = "square joker", + seance = "séance", + riffraff = "riff-raff", + cloudnine = "cloud 9", + trousers = "spare trousers", + ancient = "ancient joker", + mrbones = "mr. bones", + smeared = "smeared joker", + wee = "wee joker", + oopsall6s = "oops! all 6s", + all6s = "oops! all 6s", + oa6 = "oops! all 6s", + idol = "the idol", + duo = "the duo", + trio = "the trio", + family = "the family", + order = "the order", + tribe = "the tribe", + invisible = "invisible joker", + driverslicense = "driver's license", + burnt = "burnt joker", + caino = "canio", + house = "happy house", + queensgambit = "queen's gambit", + weefib = "weebonacci", + interest = "compound interest", + whip = "the whip", + triplet = "triplet rhythm", + pepper = "chili pepper", + krusty = "krusty the clown", + blurred = "blurred joker", + gofp = "garden of forking paths", + lutn = "light up the night", + nsnm = "no sound, no memory", + nosoundnomemory = "no sound, no memory", + lath = "...like antennas to heaven", + likeantennastoheaven = "...like antennas to heaven", + consumeable = "consume-able", + error = "j_cry_error", + ap = "ap joker", + rng = "rnjoker", + filler = "the filler", + duos = "the duos", + home = "the home", + nuts = "the nuts", + quintet = "the quintet", + unity = "the unity", + swarm = "the swarm", + crypto = "crypto coin", + googol = "googol play card", + googolplay = "googol play card", + google = "googol play card", + googleplay = "googol play card", + googleplaycard = "googol play card", + nostalgicgoogol = "nostalgic googol play card", + nostalgicgoogolplay = "nostalgic googol play card", + nostalgicgoogle = "nostalgic googol play card", + nostalgicgoogleplay = "nostalgic googol play card", + nostalgicgoogleplaycard = "nostalgic googol play card", + oldgoogol = "nostalgic googol play card", + oldgoogolplay = "nostalgic googol play card", + oldgoogle = "nostalgic googol play card", + oldgoogleplay = "nostalgic googol play card", + oldgoogleplaycard = "nostalgic googol play card", + localthunk = "supercell", + ["1fa"] = "one for all", + crust = "crustulum", + deathstar = "stella mortis", + ["jolly?"] = "jolly joker?", + scrabble = "scrabble tile", + ["13"] = "tredecim", + ["overstock+"] = "overstock plus", + directorscut = "director's cut", + ["3rs"] = "the 3 rs", + fool = "the fool", + magician = "the magician", + priestess = "the high priestess", + highpriestess = "the high priestess", + empress = "the empress", + emperor = "the emperor", + hierophant = "the hierophant", + lovers = "the lovers", + chariot = "the chariot", + hermit = "the hermit", + wheeloffortune = "the wheel of fortune", + hangedman = "the hanged man", + devil = "the devil", + tower = "the tower", + star = "the star", + moon = "the moon", + sun = "the sun", + world = "the world", + automaton = "the automaton", + eclipse = "c_cry_eclipse", + x = "planet x", + X = "planet x", + pointer = "pointer://", + payload = "://payload", + reboot = "://reboot", + revert = "://revert", + crash = "://crash", + semicolon = ";//", + [";"] = ";//", + malware = "://malware", + seed = "://seed", + variable = "://variable", + class = "://class", + commit = "://commit", + merge = "://merge", + multiply = "://multiply", + divide = "://divide", + delete = "://delete", + machinecode = "://machinecode", + run = "://run", + exploit = "://exploit", + offbyone = "://offbyone", + rework = "://rework", + patch = "://patch", + ctrlv = "://ctrl+v", + ["ctrl+v"] = "://ctrl+v", + ["ctrl v"] = "://ctrl+v", + instantiate = "://INSTANTIATE", + inst = "://INSTANTIATE", + spaghetti = "://spaghetti", + topuptag = "top-up tag", + gamblerstag = "gambler's tag", + hook = "hook://", + ox = "the ox", + wall = "the wall", + wheel = "the wheel", + arm = "the arm", + club = "the club", + fish = "the fish", + psychic = "the psychic", + goad = "the goad", + water = "the water", + window = "the window", + manacle = "the manacle", + eye = "the eye", + mouth = "the mouth", + plant = "the plant", + serpent = "the serpent", + pillar = "the pillar", + needle = "the needle", + head = "the head", + tooth = "the tooth", + flint = "the flint", + mark = "the mark", + oldox = "nostalgic ox", + oldhouse = "nostalgic house", + oldarm = "nostalgic arm", + oldfish = "nostalgic fish", + oldmanacle = "nostalgic manacle", + oldserpent = "nostalgic serpent", + oldpillar = "nostalgic pillar", + oldflint = "nostalgic flint", + oldmark = "nostalgic mark", + tax = "the tax", + trick = "the trick", + joke = "the joke", + hammer = "the hammer", + box = "the box", + windmill = "the windmill", + clock = "the clock", + code = "code joker", + copypaste = "copy/paste", + translucent = "translucent joker", + circulus = "circulus pistoris", + macabre = "macabre joker", + -- Jen's Almanac aliases + freddy = "freddy snowshoe", + paupovlin = "paupovlin revere", + poppin = "paupovlin revere", + jen = "jen walter", + --should I add "reverse ___" prefixes for the reverse tarots? + survivor = "the survivor", + monk = "the monk", + hunter = "the hunter", + gourmand = "the gourmand", + saint = "the saint", + genius = "the genius", + scientist = "the scientist", + peasant = "the peasant", + adversary = "the adversary", + rivals = "the rivals", + hitchhiker = "the hitchhiker", + angel = "the angel", + collapse = "the collapse", + lowlaywoman = "the low laywoman", + laywoman = "the low laywoman", + servant = "the servant", + extrovert = "the extrovert", + discofpenury = "the disc of penury", + flash = "the flash", + eclipsespectral = "c_jen_reverse_moon", + eclipsetorat = "c_jen_reverse_moon", + darkness = "the darkness", + void = "the void", + topuptoken = "top-up token", + sagittarius = "sagittarius a*", + ["sagitarius a*"] = "sagittarius a*", --minor spelling mistakes are forgiven + sagitarius = "sagittarius a*", --minor spelling mistakes are forgiven +} +for k, v in pairs(aliases) do + Cryptid.aliases[k] = v +end +G.FUNCS.pointer_apply = function() + local function apply_lower(str) + -- Remove content within {} and any remaining spaces + str = str:gsub("%b{}", ""):gsub("%s+", "") + --this weirdness allows you to get m and M separately + if string.len(str) == 1 then + return str + end + return string.lower(str) + end + local current_card + local entered_card = G.ENTERED_CARD + G.PREVIOUS_ENTERED_CARD = G.ENTERED_CARD + local aliases = Cryptid.aliases + if aliases[apply_lower(entered_card)] then + entered_card = aliases[apply_lower(entered_card)] + end + for i, v in pairs(G.P_CENTERS) do + if v.name and apply_lower(entered_card) == apply_lower(v.name) then + current_card = i + end + if apply_lower(entered_card) == apply_lower(i) then + current_card = i + end + if apply_lower(entered_card) == apply_lower(localize({ type = "name_text", set = v.set, key = i })) then + current_card = i + end + end + if current_card then + local created = false + if + G.P_CENTERS[current_card].set == "Joker" + and G.P_CENTERS[current_card].unlocked + and not G.GAME.banned_keys[current_card] + and (G.P_CENTERS[current_card].rarity ~= "cry_exotic" or #SMODS.find_card("j_jen_p03") > 0) + and not (Jen and Jen.overpowered(G.P_CENTERS[current_card].rarity)) + then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, current_card) + card:add_to_deck() + G.jokers:emplace(card) + created = true + end + if + G.P_CENTERS[current_card].consumeable + and G.P_CENTERS[current_card].set ~= "jen_omegaconsumable" + and not G.GAME.banned_keys[current_card] + then + local card = create_card("Consumeable", G.consumeables, nil, nil, nil, nil, current_card) + card:add_to_deck() + G.consumeables:emplace(card) + created = true + end + if + G.P_CENTERS[current_card].set == "Voucher" + and G.P_CENTERS[current_card].unlocked + and not G.GAME.banned_keys[current_card] + then + local area + if G.STATE == G.STATES.HAND_PLAYED then + if not G.redeemed_vouchers_during_hand then + G.redeemed_vouchers_during_hand = + CardArea(G.play.T.x, G.play.T.y, G.play.T.w, G.play.T.h, { type = "play", card_limit = 5 }) + end + area = G.redeemed_vouchers_during_hand + else + area = G.play + end + local card = create_card("Voucher", area, nil, nil, nil, nil, current_card) + card:start_materialize() + area:emplace(card) + card.cost = 0 + card.shop_voucher = false + local current_round_voucher = G.GAME.current_round.voucher + card:redeem() + G.GAME.current_round.voucher = current_round_voucher + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + func = function() + card:start_dissolve() + return true + end, + })) + created = true + end + if + G.P_CENTERS[current_card].set == "Booster" + and not G.GAME.banned_keys[current_card] + and (G.P_CENTERS[current_card].name ~= "Exotic Buffoon Pack" or #SMODS.find_card("j_jen_p03") ~= 0) + and G.STATE ~= G.STATES.TAROT_PACK + and G.STATE ~= G.STATES.SPECTRAL_PACK + and G.STATE ~= G.STATES.STANDARD_PACK + and G.STATE ~= G.STATES.BUFFOON_PACK + and G.STATE ~= G.STATES.PLANET_PACK + and G.STATE ~= G.STATES.SMODS_BOOSTER_OPENED + then + local card = create_card("Booster", G.hand, nil, nil, nil, nil, current_card) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({ config = { ref_table = card } }) + card:start_materialize() + created = true + end + if created then + G.CHOOSE_CARD:remove() + G.GAME.USING_CODE = false + G.GAME.USING_POINTER = false + return + end + end + for i, v in pairs(G.P_TAGS) do + if v.name and apply_lower(entered_card) == apply_lower(v.name) then + current_card = i + end + if apply_lower(entered_card) == apply_lower(i) then + current_card = i + end + if apply_lower(entered_card) == apply_lower(localize({ type = "name_text", set = v.set, key = i })) then + current_card = i + end + end + if + current_card + and not G.P_CENTERS[current_card] + and not G.GAME.banned_keys[current_card] + then + local created = false + local t = Tag(current_card, nil, "Big") + add_tag(t) + if current_card == "tag_orbital" then + local _poker_hands = {} + for k, v in pairs(G.GAME.hands) do + if v.visible then + _poker_hands[#_poker_hands + 1] = k + end + end + t.ability.orbital_hand = pseudorandom_element(_poker_hands, pseudoseed("cry_pointer_orbital")) + end + if current_card == "tag_cry_rework" then + --tbh this is the most unbalanced part of the card + t.ability.rework_edition = + pseudorandom_element(G.P_CENTER_POOLS.Edition, pseudoseed("cry_pointer_edition")).key + t.ability.rework_key = pseudorandom_element(G.P_CENTER_POOLS.Joker, pseudoseed("cry_pointer_joker")).key + end + G.CHOOSE_CARD:remove() + G.GAME.USING_CODE = false + G.GAME.USING_POINTER = false + return + end + for i, v in pairs(G.P_BLINDS) do + if v.name and apply_lower(entered_card) == apply_lower(v.name) then + current_card = i + end + if apply_lower(entered_card) == apply_lower(i) then + current_card = i + end + if apply_lower(entered_card) == apply_lower(localize({ type = "name_text", set = "Blind", key = i })) then + current_card = i + end + end + if current_card and not G.P_CENTERS[current_card] and not G.P_TAGS[current_card] and not G.GAME.banned_keys[current_card] then + local created = false + if not G.GAME.blind or (G.GAME.blind.name == "" or not G.GAME.blind.blind_set) then + --from debugplus + local par = G.blind_select_opts.boss.parent + G.GAME.round_resets.blind_choices.Boss = current_card + + G.blind_select_opts.boss:remove() + G.blind_select_opts.boss = UIBox({ + T = { par.T.x, 0, 0, 0 }, + definition = { + n = G.UIT.ROOT, + config = { + align = "cm", + colour = G.C.CLEAR, + }, + nodes = { + UIBox_dyn_container( + { create_UIBox_blind_choice("Boss") }, + false, + get_blind_main_colour("Boss"), + mix_colours(G.C.BLACK, get_blind_main_colour("Boss"), 0.8) + ), + }, + }, + config = { + align = "bmi", + offset = { + x = 0, + y = G.ROOM.T.y + 9, + }, + major = par, + xy_bond = "Weak", + }, + }) + par.config.object = G.blind_select_opts.boss + par.config.object:recalculate() + G.blind_select_opts.boss.parent = par + G.blind_select_opts.boss.alignment.offset.y = 0 + + for i = 1, #G.GAME.tags do + if G.GAME.tags[i]:apply_to_run({ + type = "new_blind_choice", + }) then + break + end + end + created = true + else + G.GAME.blind:set_blind(G.P_BLINDS[current_card]) + ease_background_colour_blind(G.STATE) + created = true + end + if created then + G.CHOOSE_CARD:remove() + G.GAME.USING_CODE = false + G.GAME.USING_POINTER = false + end + end +end +crashes = { + function() + G:save_settings() + G:save_progress() + --instantly quit the game, no error log + function love.errorhandler() end + print(crash.crash.crash) + end, + function() + G:save_settings() + G:save_progress() + --removes draw loop, you're frozen and can still weirdly interact with the game a bit + function love.draw() end + end, + function() + G:save_settings() + G:save_progress() + --by WilsonTheWolf and MathIsFun_, funky error screen with random funny message + messages = { + "Oops.", + "Your cards have been TOASTED, extra crispy for your pleasure.", + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA", + "What we have here is a certified whoopsidaisy", + "Why don't you buy more jonkers? Are you stupid?", + "lmao", + "How about a game of YOU MUST DIE?", + "Sorry, I was in the bathroom. What'd I mi'Where'd... Where is everyone?", + "Peter? What are you doing? Cards. WHAT THE FUCK?", + "what if it was called freaklatro", + "4", + "I SAWED THIS GAME IN HALF!", + "is this rush hour 4", + "You missed a semicolon on line 19742, you buffoon", + "you are an idiot", + "You do not recognise the cards in the deck.", + ":( Your P", + "Assertion failed", + "Play ULTRAKILL", + "Play Nova Drift", + "Play Balatro- wait", + "what if instead of rush hour it was called kush hour and you just smoked a massive blunt", + "death.fell.accident.water", + "Balatro's innards were made outards", + "i am going to club yrou", + "But the biggest joker of them all, it was you all along!", + "fission mailed successfully", + "table index is nil", + "index is nil table", + "nil is index table", + "nildex is in table", + "I AM THE TABLE", + "I'm never going back this casino agai-", + "what did you think would happen?", + "DO THE EARTHQUAKE! [screams]", + "fuck you", + "Screaming in the casino prank! AAAAAAAAAAAAAAAAAA", + "https://www.youtube.com/watch?v=dQw4w9WgXcQ", + "You musn't tear or crease it.", + "Sure, but the point is to do it without making a hole.", + "The end of all things! As was foretold in the prophecy!", + "Do it again. It'd be funny", + "", + ":3", + "Looks like a skill issue to me.", + "it turns out that card was ligma", + "YouJustLostTheCasinoGame", + "Nah fuck off", + "attempt to call global your_mom (value too large)", + "Killed by intentional game design", + "attempt to index field 'attempt to call global to_big (too big)' (a nil value)", + "what.avi", + "The C", + "Shoulda kept Chicot", + "Maybe next time don't do that?", + "[recursion]", + "://SHART", + "It's converging time.", + "This is the last error message.", + } + function corruptString(str) + -- replace each character with a random valid ascii character + local newStr = "" + local len + if type(str) == "string" then + len = #str + else + len = str + end + for i = 1, len do + -- newStr = newStr .. string.char(math.random(33, 122)) + local c = math.random(33, 122) + newStr = newStr .. string.char(c) + if c == 92 then -- backslash + newStr = newStr .. string.char(c) + end + end + return newStr + end + + function getDebugInfoForCrash() + local info = "Additional Context:\nBalatro Version: " .. VERSION .. "\nModded Version: " .. MODDED_VERSION + local major, minor, revision, codename = love.getVersion() + info = info .. "\nLove2D Version: " .. corruptString(string.format("%d.%d.%d", major, minor, revision)) + + local lovely_success, lovely = pcall(require, "lovely") + if lovely_success then + info = info .. "\nLovely Version: " .. corruptString(lovely.version) + end + if SMODS.mod_list then + info = info .. "\nSteamodded Mods:" + local enabled_mods = {} + for _, v in ipairs(SMODS.mod_list) do + if v.can_load then + table.insert(enabled_mods, v) + end + end + for k, v in ipairs(enabled_mods) do + info = info + .. "\n " + .. k + .. ": " + .. v.name + .. " by " + .. table.concat(v.author, ", ") + .. " [ID: " + .. v.id + .. (v.priority ~= 0 and (", Priority: " .. v.priority) or "") + .. (v.version and v.version ~= "0.0.0" and (", Version: " .. v.version) or "") + .. "]" + local debugInfo = v.debug_info + if debugInfo then + if type(debugInfo) == "string" then + if #debugInfo ~= 0 then + info = info .. "\n " .. debugInfo + end + elseif type(debugInfo) == "table" then + for kk, vv in pairs(debugInfo) do + if type(vv) ~= "nil" then + vv = tostring(vv) + end + if #vv ~= 0 then + info = info .. "\n " .. kk .. ": " .. vv + end + end + end + end + end + end + return info + end + + VERSION = corruptString(VERSION) + MODDED_VERSION = corruptString(MODDED_VERSION) + + if SMODS.mod_list then + for i, mod in ipairs(SMODS.mod_list) do + mod.can_load = true + mod.name = corruptString(mod.name) + mod.id = corruptString(mod.id) + mod.author = { corruptString(20) } + mod.version = corruptString(mod.version) + mod.debug_info = corruptString(math.random(5, 100)) + end + end + + do + local utf8 = require("utf8") + local linesize = 100 + + -- Modifed from https://love2d.org/wiki/love.errorhandler + function love.errorhandler(msg) + msg = tostring(msg) + + if not love.window or not love.graphics or not love.event then + return + end + + if not love.graphics.isCreated() or not love.window.isOpen() then + local success, status = pcall(love.window.setMode, 800, 600) + if not success or not status then + return + end + end + + -- Reset state. + if love.mouse then + love.mouse.setVisible(true) + love.mouse.setGrabbed(false) + love.mouse.setRelativeMode(false) + if love.mouse.isCursorSupported() then + love.mouse.setCursor() + end + end + if love.joystick then + -- Stop all joystick vibrations. + for i, v in ipairs(love.joystick.getJoysticks()) do + v:setVibration() + end + end + if love.audio then + love.audio.stop() + end + + love.graphics.reset() + local font = love.graphics.setNewFont("resources/fonts/m6x11plus.ttf", 20) + + local background = { math.random() * 0.3, math.random() * 0.3, math.random() * 0.3 } + love.graphics.clear(background) + love.graphics.origin() + + local sanitizedmsg = {} + for char in msg:gmatch(utf8.charpattern) do + table.insert(sanitizedmsg, char) + end + sanitizedmsg = table.concat(sanitizedmsg) + + local err = {} + + table.insert(err, "Oops! The game crashed:") + table.insert(err, sanitizedmsg) + + if #sanitizedmsg ~= #msg then + table.insert(err, "Invalid UTF-8 string in error message.") + end + + local success, msg = pcall(getDebugInfoForCrash) + if success and msg then + table.insert(err, "\n" .. msg) + else + table.insert(err, "\n" .. "Failed to get additional context :/") + end + + local p = table.concat(err, "\n") + + p = p:gsub("\t", "") + p = p:gsub('%[string "(.-)"%]', "%1") + + local scrollOffset = 0 + local endHeight = 0 + love.keyboard.setKeyRepeat(true) + + local function scrollDown(amt) + if amt == nil then + amt = 18 + end + scrollOffset = scrollOffset + amt + if scrollOffset > endHeight then + scrollOffset = endHeight + end + end + + local function scrollUp(amt) + if amt == nil then + amt = 18 + end + scrollOffset = scrollOffset - amt + if scrollOffset < 0 then + scrollOffset = 0 + end + end + + local pos = 70 + local arrowSize = 20 + + local function calcEndHeight() + local font = love.graphics.getFont() + local rw, lines = font:getWrap(p, love.graphics.getWidth() - pos * 2) + local lineHeight = font:getHeight() + local atBottom = scrollOffset == endHeight and scrollOffset ~= 0 + endHeight = #lines * lineHeight - love.graphics.getHeight() + pos * 2 + if endHeight < 0 then + endHeight = 0 + end + if scrollOffset > endHeight or atBottom then + scrollOffset = endHeight + end + end + + local function draw() + if not love.graphics.isActive() then + return + end + love.graphics.clear(background) + calcEndHeight() + local time = love.timer.getTime() + if not G.SETTINGS.reduced_motion then + background = { math.random() * 0.3, math.random() * 0.3, math.random() * 0.3 } + p = p .. "\n" .. corruptString(math.random(linesize - linesize / 2, linesize + linesize * 2)) + linesize = linesize + linesize / 25 + end + love.graphics.printf(p, pos, pos - scrollOffset, love.graphics.getWidth() - pos * 2) + if scrollOffset ~= endHeight then + love.graphics.polygon( + "fill", + love.graphics.getWidth() - (pos / 2), + love.graphics.getHeight() - arrowSize, + love.graphics.getWidth() - (pos / 2) + arrowSize, + love.graphics.getHeight() - (arrowSize * 2), + love.graphics.getWidth() - (pos / 2) - arrowSize, + love.graphics.getHeight() - (arrowSize * 2) + ) + end + if scrollOffset ~= 0 then + love.graphics.polygon( + "fill", + love.graphics.getWidth() - (pos / 2), + arrowSize, + love.graphics.getWidth() - (pos / 2) + arrowSize, + arrowSize * 2, + love.graphics.getWidth() - (pos / 2) - arrowSize, + arrowSize * 2 + ) + end + love.graphics.present() + end + + local fullErrorText = p + local function copyToClipboard() + if not love.system then + return + end + love.system.setClipboardText(fullErrorText) + p = p .. "\nCopied to clipboard!" + end + + if G then + -- Kill threads (makes restarting possible) + if G.SOUND_MANAGER and G.SOUND_MANAGER.channel then + G.SOUND_MANAGER.channel:push({ + type = "kill", + }) + end + if G.SAVE_MANAGER and G.SAVE_MANAGER.channel then + G.SAVE_MANAGER.channel:push({ + type = "kill", + }) + end + if G.HTTP_MANAGER and G.HTTP_MANAGER.channel then + G.HTTP_MANAGER.channel:push({ + type = "kill", + }) + end + end + + return function() + love.event.pump() + + for e, a, b, c in love.event.poll() do + if e == "quit" then + return 1 + elseif e == "keypressed" and a == "escape" then + return 1 + elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then + copyToClipboard() + elseif e == "keypressed" and a == "r" then + return "restart" + elseif e == "keypressed" and a == "down" then + scrollDown() + elseif e == "keypressed" and a == "up" then + scrollUp() + elseif e == "keypressed" and a == "pagedown" then + scrollDown(love.graphics.getHeight()) + elseif e == "keypressed" and a == "pageup" then + scrollUp(love.graphics.getHeight()) + elseif e == "keypressed" and a == "home" then + scrollOffset = 0 + elseif e == "keypressed" and a == "end" then + scrollOffset = endHeight + elseif e == "wheelmoved" then + scrollUp(b * 20) + elseif e == "gamepadpressed" and b == "dpdown" then + scrollDown() + elseif e == "gamepadpressed" and b == "dpup" then + scrollUp() + elseif e == "gamepadpressed" and b == "a" then + return "restart" + elseif e == "gamepadpressed" and b == "x" then + copyToClipboard() + elseif e == "gamepadpressed" and (b == "b" or b == "back" or b == "start") then + return 1 + elseif e == "touchpressed" then + local name = love.window.getTitle() + if #name == 0 or name == "Untitled" then + name = "Game" + end + local buttons = { "OK", localize("cry_code_cancel"), "Restart" } + if love.system then + buttons[4] = "Copy to clipboard" + end + local pressed = love.window.showMessageBox("Quit " .. name .. "?", "", buttons) + if pressed == 1 then + return 1 + elseif pressed == 3 then + return "restart" + elseif pressed == 4 then + copyToClipboard() + end + end + end + + draw() + + if love.timer then + love.timer.sleep(0.1) + end + end + end + end + + load("error(messages[math.random(1, #messages)])", corruptString(30), "t")() + end, + function() + check_for_unlock({ type = "ach_cry_used_crash" }) + --fills screen with Crash cards + glitched_intensity = 100 + G.SETTINGS.GRAPHICS.crt = 101 + G.E_MANAGER:add_event( + Event({ + trigger = "immediate", + blockable = false, + no_delete = true, + func = function() + local c = create_card("Code", nil, nil, nil, nil, nil, "c_cry_crash") + c.T.x = math.random(-G.CARD_W, G.TILE_W) + c.T.y = math.random(-G.CARD_H, G.TILE_H) + return false + end, + }), + "other" + ) + end, + function() + G:save_settings() + G:save_progress() + -- Fake lovely panic + love.window.showMessageBox( + "lovely-injector", + 'lovely-injector has crashed:\npanicked at crates/lovely-core/src/lib.rs:420:69:\nFailed to parse patch at "C:\\\\users\\\\jimbo\\\\AppData\\\\Roaming\\\\Balatro\\\\Mods\\\\Cryptid\\\\lovely.toml":\nError { inner: TomlError { message: "expected `.`, `=`", original: Some("' + .. "\"According to all known laws of aviation, there is no way a bee should be able to fly.\\nIts wings are too small to get its fat little body off the ground.\\nThe bee, of course, flies anyway because bees don't care what humans think is impossible.\\nYellow, black. Yellow, black. Yellow, black. Yellow, black.\\nOoh, black and yellow!\\nLet's shake it up a little.\\nBarry! Breakfast is ready!\\nComing!\\nHang on a second.\\nHello?\\nBarry?\\nAdam?\\nCan you believe this is happening?\\nI can't.\\nI'll pick you up.\\nLooking sharp.\\nUse the stairs, Your father paid good money for those.\\nSorry. I'm excited.\\nHere's the graduate.\\nWe're very proud of you, son.\\nA perfect report card, all B's.\\nVery proud.\\nMa! I got a thing going here.\\nYou got lint on your fuzz.\\nOw! That's me!\\nWave to us! We'll be in row 118,000.\\nBye!\\nBarry, I told you, stop flying in the house!\\nHey, Adam.\\nHey, Barry.\\nIs that fuzz gel?\\nA little. Special day, graduation.\\nNever thought I'd make it.\\nThree days grade school, three days high school.\\nThose were awkward.\\nThree days college. I'm glad I took a day and hitchhiked around The Hive.\\nYou did come back different.\\nHi, Barry. Artie, growing a mustache? Looks good.\\nHear about Frankie?\\nYeah.\\nYou going to the funeral?\\nNo, I'm not going.\\nEverybody knows, sting someone, you die.\\nDon't waste it on a squirrel.\\nSuch a hothead.\\nI guess he could have just gotten out of the way.\\nI love this incorporating an amusement park into our day.\\nThat's why we don't need vacations.\\nBoy, quite a bit of pomp under the circumstances.\\nWell, Adam, today we are men.\\nWe are!\\nBee-men.\\nAmen!\\nHallelujah!\\nStudents, faculty, distinguished bees,\\nplease welcome Dean Buzzwell.\\nWelcome, New Hive City graduating class of 9:15.\\nThat concludes our ceremonies And begins your career at Honex Industries!\\nWill we pick our job today?\\nI heard it's just orientation.\\nHeads up! Here we go.\\nKeep your hands and antennas inside the tram at all times.\\nWonder what it'll be like?\\nA little scary.\\nWelcome to Honex, a division of Honesco and a part of the Hexagon Group.\\nThis is it!\\nWow.\\nWow.\\nWe know that you, as a bee, have worked your whole life to get to the point where you can work for your whole life.\\nHoney begins when our valiant Pollen Jocks bring the nectar to The Hive.\\nOur top-secret formula is automatically color-corrected, scent-adjusted and bubble-contoured into this soothing sweet syrup with its distinctive golden glow you know as... Honey!\\nThat girl was hot.\\nShe's my cousin!\\nShe is?\\nYes, we're all cousins.\\nRight. You're right.\\nAt Honex, we constantly strive to improve every aspect of bee existence.\\nThese bees are stress-testing a new helmet technology.\\nWhat do you think he makes?\\nNot enough.\\nHere we have our latest advancement, the Krelman.\\nWhat does that do?\\nCatches that little strand of honey that hangs after you pour it.\\nSaves us millions.\\nCan anyone work on the Krelman?\\nOf course. Most bee jobs are small ones.\\nBut bees know that every small job, if it's done well, means a lot.\\nBut choose carefully because you'll stay in the job you pick for the rest of your life.\\nThe same job the rest of your life? I didn't know that.\\nWhat's the difference?\\nYou'll be happy to know that bees, as a species, haven't had one day off in 27 million years.\\nSo you'll just work us to death?\\nWe'll sure try.\\nWow! That blew my mind!\\n\\\"What's the difference?\\\"\\nHow can you say that?\\nOne job forever?\\nThat's an insane choice to have to make.\\nI'm relieved. Now we only have to make one decision in life.\\nBut, Adam, how could they never have told us that?\\nWhy would you question anything? We're bees.\\nWe're the most perfectly functioning society on Earth.\\nYou ever think maybe things work a little too well here?\\nLike what? Give me one example.\\nI don't know. But you know what I'm talking about.\\nPlease clear the gate. Royal Nectar Force on approach.\\nWait a second. Check it out.\\nHey, those are Pollen Jocks!\\nWow.\\nI've never seen them this close.\\nThey know what it's like outside The Hive.\\nYeah, but some don't come back.\\nHey, Jocks!\\nHi, Jocks!\\nYou guys did great!\\nYou're monsters!\\nYou're sky freaks! I love it! I love it!\\nI wonder where they were.\\nI don't know.\\nTheir day's not planned.\\nOutside The Hive, flying who knows where, doing who knows what.\\nYou can't just decide to be a Pollen Jock. You have to be bred for that.\\nRight.\\nLook. That's more pollen than you and I will see in a lifetime.\\nIt's just a status symbol.\\nBees make too much of it.\\nPerhaps. Unless you're wearing it and the ladies see you wearing it.\\nThose ladies?\\nAren't they our cousins too?\\nDistant. Distant.\\nLook at these two.\\nCouple of Hive Harrys.\\nLet's have fun with them.\\nIt must be dangerous being a Pollen Jock.\\nYeah. Once a bear pinned me against a mushroom!\\nHe had a paw on my throat, and with the other, he was slapping me!\\nOh, my!\\nI never thought I'd knock him out.\\nWhat were you doing during this?\\nTrying to alert the authorities.\\nI can autograph that.\\nA little gusty out there today, wasn't it, comrades?\\nYeah. Gusty.\\nWe're hitting a sunflower patch six miles from here tomorrow.\\nSix miles, huh?\\nBarry!\\nA puddle jump for us, but maybe you're not up for it.\\nMaybe I am.\\nYou are not!\\nWe're going 0900 at J-Gate.\\nWhat do you think, buzzy-boy?\\nAre you bee enough?\\nI might be. It all depends on what 0900 means.\\nHey, Honex!\\nDad, you surprised me.\\nYou decide what you're interested in?\\nWell, there's a lot of choices.\\nBut you only get one.\\nDo you ever get bored doing the same job every day?\\nSon, let me tell you about stirring.\\nYou grab that stick, and you just move it around, and you stir it around.\\nYou get yourself into a rhythm.\\nIt's a beautiful thing.\\nYou know, Dad, the more I think about it,\\nmaybe the honey field just isn't right for me.\\nYou were thinking of what, making balloon animals?\\nThat's a bad job for a guy with a stinger.\\nJanet, your son's not sure he wants to go into honey!\\nBarry, you are so funny sometimes.\\nI'm not trying to be funny.\\nYou're not funny! You're going into honey. Our son, the stirrer!\\nYou're gonna be a stirrer?\\nNo one's listening to me!\\nWait till you see the sticks I have.\\nI could say anything right now.\\nI'm gonna get an ant tattoo!\\nLet's open some honey and celebrate!\\nMaybe I'll pierce my thorax. Shave my antennae. Shack up with a grasshopper. Get a gold tooth and call everybody \\\"dawg\\\"!\\nI'm so proud.\\nWe're starting work today!\\nToday's the day.\\nCome on! All the good jobs will be gone.\\nYeah, right.\\nPollen counting, stunt bee, pouring, stirrer, front desk, hair removal...\\nIs it still available?\\nHang on. Two left!\\nOne of them's yours! Congratulations!\\nStep to the side.\\nWhat'd you get?\\nPicking crud out. Stellar!\\nWow!\\nCouple of newbies?\\nYes, sir! Our first day! We are ready!\\nMake your choice.\\nYou want to go first?\\nNo, you go.\\nOh, my. What's available?\\nRestroom attendant's open, not for the reason you think.\\nAny chance of getting the Krelman?\\nSure, you're on.\\nI'm sorry, the Krelman just closed out.\\nWax monkey's always open.\\nThe Krelman opened up again.\\nWhat happened?\\nA bee died. Makes an opening. See? He's dead. Another dead one.\\nDeady. Deadified. Two more dead.\\nDead from the neck up. Dead from the neck down. That's life!\\nOh, this is so hard!\\nHeating, cooling, stunt bee, pourer, stirrer, humming, inspector number seven, lint coordinator, stripe supervisor, mite wrangler.\\nBarry, what do you think I should... Barry?\\nBarry!\\nAll right, we've got the sunflower patch in quadrant nine...\\nWhat happened to you?\\nWhere are you?\\nI'm going out.\\nOut? Out where?\\nOut there.\\nOh, no!\\nI have to, before I go to work for the rest of my life.\\nYou're gonna die! You're crazy! Hello?\\nAnother call coming in.\\nIf anyone's feeling brave, there's a Korean deli on 83rd that gets their roses today.\\nHey, guys.\\nLook at that.\\nIsn't that the kid we saw yesterday?\\nHold it, son, flight deck's restricted.\\nIt's OK, Lou. We're gonna take him up.\\nReally? Feeling lucky, are you?\\nSign here, here. Just initial that.\\nThank you.\\nOK.\\nYou got a rain advisory today, and as you all know, bees cannot fly in rain.\\nSo be careful. As always, watch your brooms, hockey sticks, dogs, birds, bears and bats.\\nAlso, I got a couple of reports of root beer being poured on us.\\nMurphy's in a home because of it, babbling like a cicada!\\nThat's awful.\\nAnd a reminder for you rookies, bee law number one, absolutely no talking to humans!\\n All right, launch positions!\\nBuzz, buzz, buzz, buzz! Buzz, buzz, buzz, buzz! Buzz, buzz, buzz, buzz!\\nBlack and yellow!\\nHello!\\nYou ready for this, hot shot?\\nYeah. Yeah, bring it on.\\nWind, check.\\nAntennae, check.\\nNectar pack, check.\\nWings, check.\\nStinger, check.\\nScared out of my shorts, check.\\nOK, ladies,\\nlet's move it out!\\nPound those petunias, you striped stem-suckers!\\nAll of you, drain those flowers!\\nWow! I'm out!\\nI can't believe I'm out!\\nSo blue.\\nI feel so fast and free!\\nBox kite!\\nWow!\\nFlowers!\\nThis is Blue Leader, We have roses visual.\\nBring it around 30 degrees and hold.\\nRoses!\\n30 degrees, roger. Bringing it around.\\nStand to the side, kid.\\nIt's got a bit of a kick.\\nThat is one nectar collector!\\nEver see pollination up close?\\nNo, sir.\\nI pick up some pollen here, sprinkle it over here. Maybe a dash over there, a pinch on that one.\\nSee that? It's a little bit of magic.\\nThat's amazing. Why do we do that?\\nThat's pollen power. More pollen, more flowers, more nectar, more honey for us.\\nCool.\\nI'm picking up a lot of bright yellow, Could be daisies, Don't we need those?\\nCopy that visual.\\nWait. One of these flowers seems to be on the move.\\nSay again? You're reporting a moving flower?\\nAffirmative.\\nThat was on the line!\\nThis is the coolest. What is it?\\nI don't know, but I'm loving this color.\\nIt smells good.\\nNot like a flower, but I like it.\\nYeah, fuzzy.\\nChemical-y.\\nCareful, guys. It's a little grabby.\\nMy sweet lord of bees!\\nCandy-brain, get off there!\\nProblem!\\nGuys!\\nThis could be bad.\\nAffirmative.\\nVery close.\\nGonna hurt.\\nMama's little boy.\\nYou are way out of position, rookie!\\nComing in at you like a missile!\\nHelp me!\\nI don't think these are flowers.\\nShould we tell him?\\nI think he knows.\\nWhat is this?!\\nMatch point!\\nYou can start packing up, honey, because you're about to eat it!\\nYowser!\\nGross.\\nThere's a bee in the car!\\nDo something!\\nI'm driving!\\nHi, bee.\\nHe's back here!\\nHe's going to sting me!\\nNobody move. If you don't move, he won't sting you. Freeze!\\nHe blinked!\\nSpray him, Granny!\\nWhat are you doing?!\\nWow... the tension level out here is unbelievable.\\nI gotta get home.\\nCan't fly in rain. Can't fly in rain. Can't fly in rain.\\nMayday! Mayday! Bee going down!\\nKen, could you close the window please?\\nKen, could you close the window please?\\nCheck out my new resume. I made it into a fold-out brochure. You see? Folds out.\\nOh, no. More humans. I don't need this.\\nWhat was that?\\nMaybe this time. This time. This time. This time! This time! This... Drapes!\\nThat is diabolical.\\nIt's fantastic. It's got all my special skills, even my top-ten favorite movies.\\nWhat's number one? Star Wars?\\nNah, I don't go for that... kind of stuff.\\nNo wonder we shouldn't talk to them. They're out of their minds.\\nWhen I leave a job interview, they're flabbergasted, can't believe what I say.\\nThere's the sun. Maybe that's a way out.\\nI don't remember the sun having a big 75 on it.\\nI predicted global warming. I could feel it getting hotter. At first I thought it was just me.\\nWait! Stop! Bee!\\nStand back. These are winter boots.\\nWait!\\nDon't kill him!\\nYou know I'm allergic to them! This thing could kill me!\\nWhy does his life have less value than yours?\\nWhy does his life have any less value than mine? Is that your statement?\\nI'm just saying all life has value. You don't know what he's capable of feeling.\\nMy brochure!\\nThere you go, little guy.\\nI'm not scared of him.It's an allergic thing.\\n Put that on your resume brochure.\\nMy whole face could puff up.\\nMake it one of your special skills.\\nKnocking someone out is also a special skill.\\nRight. Bye, Vanessa. Thanks.\\nVanessa, next week? Yogurt night?\\nSure, Ken. You know, whatever.\\nYou could put carob chips on there.\\nBye.\\nSupposed to be less calories.\\nBye.\\nI gotta say something. She saved my life. I gotta say something.\\nAll right, here it goes.\\nNah.\\nWhat would I say?\\nI could really get in trouble. It's a bee law. You're not supposed to talk to a human.\\nI can't believe I'm doing this. I've got to.\\nOh, I can't do it. Come on!\\nNo. Yes. No. Do it. I can't.\\nHow should I start it? \\\"You like jazz?\\\" No, that's no good.\\nHere she comes! Speak, you fool!\\nHi!\\nI'm sorry. You're talking.\\nYes, I know.\\nYou're talking!\\nI'm so sorry.\\nNo, it's OK. It's fine.\\nI know I'm dreaming. But I don't recall going to bed.\\nWell, I'm sure this is very disconcerting.\\nThis is a bit of a surprise to me. I mean, you're a bee!\\nI am. And I'm not supposed to be doing this, but they were all trying to kill me.\\nAnd if it wasn't for you... I had to thank you. It's just how I was raised.\\nThat was a little weird. I'm talking with a bee.\\nYeah.\\nI'm talking to a bee. And the bee is talking to me!\\nI just want to say I'm grateful.\\nI'll leave now.\\nWait! How did you learn to do that?\\nWhat?\\nThe talking thing.\\nSame way you did, I guess. \\\"Mama, Dada, honey.\\\" You pick it up.\\nThat's very funny.\\nYeah.\\nBees are funny. If we didn't laugh, we'd cry with what we have to deal with.\\nAnyway... Can I... get you something?\\nLike what?\\nI don't know. I mean... I don't know. Coffee?\\nI don't want to put you out.\\nIt's no trouble. It takes two minutes.\\nIt's just coffee.\\nI hate to impose.\\nDon't be ridiculous!\\nActually, I would love a cup.\\nHey, you want rum cake?\\nI shouldn't.\\nHave some.\\nNo, I can't.\\nCome on!\\nI'm trying to lose a couple micrograms.\\nWhere?\\nThese stripes don't help.\\nYou look great!\\nI don't know if you know anything about fashion.\\nAre you all right?\\nNo.\\nHe's making the tie in the cab as they're flying up Madison.\\nHe finally gets there.\\nHe runs up the steps into the church.\\nThe wedding is on.\\nAnd he says, \\\"Watermelon?\\nI thought you said Guatemalan.\\nWhy would I marry a watermelon?\\\"\\nIs that a bee joke?\\nThat's the kind of stuff we do.\\nYeah, different.\\nSo, what are you gonna do, Barry?\\nAbout work? I don't know.\\nI want to do my part for The Hive, but I can't do it the way they want.\\nI know how you feel.\\nYou do?\\nSure.\\nMy parents wanted me to be a lawyer or a doctor, but I wanted to be a florist.\\nReally?\\nMy only interest is flowers.\\nOur new queen was just elected with that same campaign slogan.\\nAnyway, if you look... There's my hive right there. See it?\\nYou're in Sheep Meadow!\\nYes! I'm right off the Turtle Pond!\\nNo way! I know that area. I lost a toe ring there once.\\nWhy do girls put rings on their toes?\\nWhy not?\\nIt's like putting a hat on your knee.\\nMaybe I'll try that.\\nYou all right, ma'am?\\nOh, yeah. Fine.\\nJust having two cups of coffee!\\nAnyway, this has been great.\\nThanks for the coffee.\\nYeah, it's no trouble.\\nSorry I couldn't finish it. If I did, I'd be up the rest of my life.\\nAre you...?\\nCan I take a piece of this with me?\\nSure! Here, have a crumb.\\nThanks!\\nYeah.\\nAll right. Well, then... I guess I'll see you around. Or not.\\nOK, Barry.\\nAnd thank you so much again... for before.\\nOh, that? That was nothing.\\nWell, not nothing, but... Anyway...\\nThis can't possibly work.\\nHe's all set to go.\\nWe may as well try it.\\nOK, Dave, pull the chute.\\nSounds amazing.\\nIt was amazing!\\nIt was the scariest, happiest moment of my life.\\nHumans! I can't believe you were with humans!\\nGiant, scary humans!\\nWhat were they like?\\nHuge and crazy. They talk crazy.\\nThey eat crazy giant things.\\nThey drive crazy.\\nDo they try and kill you, like on TV?\\nSome of them. But some of them don't.\\nHow'd you get back?\\nPoodle.\\nYou did it, and I'm glad. You saw whatever you wanted to see.\\nYou had your \\\"experience.\\\" Now you can pick out yourjob and be normal.\\nWell...\\nWell?\\nWell, I met someone.\\nYou did? Was she Bee-ish?\\nA wasp?! Your parents will kill you!\\nNo, no, no, not a wasp.\\nSpider?\\nI'm not attracted to spiders.\\nI know it's the hottest thing, with the eight legs and all. I can't get by that face.\\nSo who is she?\\nShe's... human.\\nNo, no. That's a bee law. You wouldn't break a bee law.\\nHer name's Vanessa.\\nOh, boy.\\nShe's so nice. And she's a florist!\\nOh, no! You're dating a human florist!\\nWe're not dating.\\nYou're flying outside The Hive, talking to humans that attack our homes with power washers and M-80s! One-eighth a stick of dynamite!\\nShe saved my life! And she understands me.\\nThis is over!\\nEat this.\\nThis is not over! What was that?\\nThey call it a crumb.\\nIt was so stingin' stripey!\\nAnd that's not what they eat.\\nThat's what falls off what they eat!\\nYou know what a Cinnabon is?\\nNo.\\nIt's bread and cinnamon and frosting. They heat it up...\\nSit down!\\n...really hot!\\nListen to me!\\nWe are not them! We're us.\\nThere's us and there's them!\\n\"), keys: [], span: Some(10..11)}}}", + "error" + ) + love.window.showMessageBox( + "lovely-injector", + "lovely-injector has crashed:\npanicked at library/cors/src/panicking.rs:221:5:\npanic in a function that cannot unwind", + "error" + ) + + function love.errorhandler() end + print(crash.crash.crash) + end, + function() + --Arbitrary Code Execution + glitched_intensity = 100 + G.SETTINGS.GRAPHICS.crt = 100 + G.GAME.USING_CODE = true + G.ENTERED_ACE = "" + G.CHOOSE_ACE = UIBox({ + definition = create_UIBox_crash(card), + config = { + align = "bmi", + offset = { x = 0, y = G.ROOM.T.y + 29 }, + major = G.jokers, + bond = "Weak", + instance_type = "POPUP", + }, + }) + end, +} + +--for testing +-- crashes = {crashes[#crashes]} +-- crashes[1]() + +local code_cards = { + code, + code_atlas, + pack_atlas, + pack1, + pack2, + packJ, + packM, + console, + automaton, + green_seal, + source, + pointer, + cut, + blender, + python, + payload, + reboot, + revert, + crash, + semicolon, + malware, + seed, + rigged, + hook, + hooked, + variable, + class, + commit, + merge, + multiply, + divide, + delete, + machinecode, + run, + exploit, + oboe, + rework, + rework_tag, + --patch, + ctrl_v, + inst, + encoded, +} +if Cryptid.enabled["Misc."] then + code_cards[#code_cards + 1] = spaghetti +end +if Cryptid.enabled["Enhanced Decks"] then + code_cards[#code_cards + 1] = source_deck +end +if Cryptid.enabled["Epic Jokers"] then + code_cards[#code_cards + 1] = CodeJoker + code_cards[#code_cards + 1] = copypaste +end +return { + name = "Code Cards", + init = function() + --allow Program Packs to let you keep the cards + local G_UIDEF_use_and_sell_buttons_ref = G.UIDEF.use_and_sell_buttons + function G.UIDEF.use_and_sell_buttons(card) + if (card.area == G.pack_cards and G.pack_cards) and card.ability.consumeable then --Add a use button + if card.ability.set == "Code" then + return { + n = G.UIT.ROOT, + config = { padding = -0.1, colour = G.C.CLEAR }, + nodes = { + { + n = G.UIT.R, + config = { + ref_table = card, + r = 0.08, + padding = 0.1, + align = "bm", + minw = 0.5 * card.T.w - 0.15, + minh = 0.7 * card.T.h, + maxw = 0.7 * card.T.w - 0.15, + hover = true, + shadow = true, + colour = G.C.UI.BACKGROUND_INACTIVE, + one_press = true, + button = "use_card", + func = "can_reserve_card", + }, + nodes = { + { + n = G.UIT.T, + config = { + text = localize("b_pull"), + colour = G.C.UI.TEXT_LIGHT, + scale = 0.55, + shadow = true, + }, + }, + }, + }, + { + n = G.UIT.R, + config = { + ref_table = card, + r = 0.08, + padding = 0.1, + align = "bm", + minw = 0.5 * card.T.w - 0.15, + maxw = 0.9 * card.T.w - 0.15, + minh = 0.1 * card.T.h, + hover = true, + shadow = true, + colour = G.C.UI.BACKGROUND_INACTIVE, + one_press = true, + button = "Do you know that this parameter does nothing?", + func = "can_use_consumeable", + }, + nodes = { + { + n = G.UIT.T, + config = { + text = localize("b_use"), + colour = G.C.UI.TEXT_LIGHT, + scale = 0.45, + shadow = true, + }, + }, + }, + }, + { n = G.UIT.R, config = { align = "bm", w = 7.7 * card.T.w } }, + { n = G.UIT.R, config = { align = "bm", w = 7.7 * card.T.w } }, + { n = G.UIT.R, config = { align = "bm", w = 7.7 * card.T.w } }, + { n = G.UIT.R, config = { align = "bm", w = 7.7 * card.T.w } }, + -- Betmma can't explain it, neither can I + }, + } + end + end + return G_UIDEF_use_and_sell_buttons_ref(card) + end + --Code from Betmma's Vouchers + G.FUNCS.can_reserve_card = function(e) + if #G.consumeables.cards < G.consumeables.config.card_limit then + e.config.colour = G.C.GREEN + e.config.button = "reserve_card" + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + end + end + G.FUNCS.reserve_card = function(e) + local c1 = e.config.ref_table + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.1, + func = function() + c1.area:remove_card(c1) + c1:add_to_deck() + if c1.children.price then + c1.children.price:remove() + end + c1.children.price = nil + if c1.children.buy_button then + c1.children.buy_button:remove() + end + c1.children.buy_button = nil + remove_nils(c1.children) + G.consumeables:emplace(c1) + G.GAME.pack_choices = G.GAME.pack_choices - 1 + if G.GAME.pack_choices <= 0 then + G.FUNCS.end_consumeable(nil, delay_fac) + end + return true + end, + })) + end + --Revert + local sr = save_run + function save_run() + if G.GAME.round_resets.ante ~= G.GAME.cry_revert_ante then + G.GAME.cry_revert_ante = G.GAME.round_resets.ante + G.GAME.cry_revert = nil + sr() + G.GAME.cry_revert = STR_PACK(G.culled_table) + sr() + end + sr() + end + --Semicolon - don't evaluate round + local gfer = G.FUNCS.evaluate_round + function G.FUNCS.evaluate_round() + if G.GAME.current_round.semicolon then + add_round_eval_row({ dollars = 0, name = "blind1", pitch = 0.95, saved = true }) + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 1.3 * math.min(G.GAME.blind.dollars + 2, 7) / 2 * 0.15 + 0.5, + func = function() + G.GAME.blind:defeat() + return true + end, + })) + delay(0.2) + add_round_eval_row({ name = "bottom", dollars = 0 }) + else + return gfer() + end + end + --Multiply - reset Jokers at end of round + local er = end_round + function end_round() + er() + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].cry_multiply then + m = G.jokers.cards[i].cry_multiply + cry_with_deck_effects(G.jokers.cards[i], function(card) + cry_misprintize(card, { min = 1 / m, max = 1 / m }, nil, true) + end) + G.jokers.cards[i].cry_multiply = nil + end + end + end + --some code to make typing more characters better + G.FUNCS.text_input_key = function(args) + args = args or {} + local hook = G.CONTROLLER.text_input_hook + if not hook.config.ref_table.extended_corpus then + if args.key == "[" or args.key == "]" then + return + end + if args.key == "0" then + args.key = "o" + end + else + if string.byte(args.key, 1) >= 128 then + print(string.byte(args.key, 1)) + args.key = "?" --fix for lovely bugging out + end + end + + --shortcut to hook config + local hook_config = G.CONTROLLER.text_input_hook.config.ref_table + hook_config.orig_colour = hook_config.orig_colour or copy_table(hook_config.colour) + + args.key = args.key or "%" + args.caps = args.caps or G.CONTROLLER.capslock or hook_config.all_caps --capitalize if caps lock or hook requires + + --Some special keys need to be mapped accordingly before passing through the corpus + local keymap = { + space = " ", + backspace = "BACKSPACE", + delete = "DELETE", + ["return"] = "RETURN", + right = "RIGHT", + left = "LEFT", + } + local corpus = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + .. (hook.config.ref_table.extended_corpus and " 0!$&()<>?:{}+-=,.[]_" or "") + + if hook.config.ref_table.extended_corpus then + local lower_ext = "1234567890-=;',./" + local upper_ext = '!@#$%^&*()_+:"<>?' + if args.caps then + if args.key == "." then + args.key = ">" + end + if args.key == "[" then + args.key = "{" + end + if args.key == "]" then + args.key = "}" + end + if args.key == "\\" then + args.key = "|" + end + end + + pcall(function() + if string.find(lower_ext, args.key) and args.caps then + args.key = string.sub(string.sub(upper_ext, string.find(lower_ext, args.key)), 0, 1) + end + end) + end + local text = hook_config.text + + --set key to mapped key or upper if caps is true + args.key = keymap[args.key] or (args.caps and string.upper(args.key) or args.key) + + --Start by setting the cursor position to the correct location + TRANSPOSE_TEXT_INPUT(0) + + if string.len(text.ref_table[text.ref_value]) > 0 and args.key == "BACKSPACE" then --If not at start, remove preceding letter + MODIFY_TEXT_INPUT({ + letter = "", + text_table = text, + pos = text.current_position, + delete = true, + }) + TRANSPOSE_TEXT_INPUT(-1) + elseif string.len(text.ref_table[text.ref_value]) > 0 and args.key == "DELETE" then --if not at end, remove following letter + MODIFY_TEXT_INPUT({ + letter = "", + text_table = text, + pos = text.current_position + 1, + delete = true, + }) + TRANSPOSE_TEXT_INPUT(0) + elseif args.key == "RETURN" then --Release the hook + if hook.config.ref_table.callback then + hook.config.ref_table.callback() + end + hook.parent.parent.config.colour = hook_config.colour + local temp_colour = copy_table(hook_config.orig_colour) + hook_config.colour[1] = G.C.WHITE[1] + hook_config.colour[2] = G.C.WHITE[2] + hook_config.colour[3] = G.C.WHITE[3] + ease_colour(hook_config.colour, temp_colour) + G.CONTROLLER.text_input_hook = nil + elseif args.key == "LEFT" then --Move cursor position to the left + TRANSPOSE_TEXT_INPUT(-1) + elseif args.key == "RIGHT" then --Move cursor position to the right + TRANSPOSE_TEXT_INPUT(1) + elseif + hook_config.max_length > string.len(text.ref_table[text.ref_value]) + and (string.len(args.key) == 1) + and (string.find(corpus, args.key, 1, true) or hook.config.ref_table.extended_corpus) + then --check to make sure the key is in the valid corpus, add it to the string + MODIFY_TEXT_INPUT({ + letter = args.key, + text_table = text, + pos = text.current_position + 1, + }) + TRANSPOSE_TEXT_INPUT(1) + end + end + + --Machine Code rendering + codechars2 = { "!", "'", ",", ".", ":", ";", "i", "l", "|", "¡", "¦", "ì", "í", "ı" } + codechars4 = { " ", "(", ")", "[", "]", "j", "î", "ī", "ĭ" } + codechars5 = { '"', "*", "<", ">", "{", "}", "¨", "°", "º", "×" } + codechars6 = { + "$", + "%", + "+", + "-", + "/", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "=", + "?", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "N", + "O", + "P", + "R", + "S", + "T", + "U", + "V", + "Y", + "Z", + "\\", + "^", + "_", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "k", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "y", + "z", + "~", + "¢", + "¥", + "§", + "¬", + "±", + "¿", + "À", + "Á", + "Â", + "Ã", + "Ä", + "Å", + "Ç", + "È", + "É", + "Ê", + "Ë", + "Ì", + "Í", + "Î", + "Ï", + "Ñ", + "Ò", + "Ó", + "Ô", + "Õ", + "Ö", + "Ù", + "Ú", + "Û", + "Ü", + "Ý", + "Þ", + "à", + "á", + "â", + "ã", + "ä", + "å", + "ç", + "è", + "é", + "ê", + "ë", + "ï", + "ñ", + "ò", + "ó", + "ô", + "õ", + "ö", + "÷", + "ù", + "ú", + "û", + "ü", + "ý", + "þ", + "ÿ", + "Ā", + "ā", + "Ă", + "ă", + "Ć", + "ć", + "Ē", + "ē", + "Ĕ", + "ĕ", + "Ğ", + "ğ", + "Ī", + "Ĭ", + "İ", + "ł", + "Ń", + "ń", + "Ō", + "ō", + "Ŏ", + "ŏ", + "Ś", + "ś", + "Ş", + "ş", + "Ū", + "ū", + "Ŭ", + "ŭ", + "Ÿ", + "Ź", + "ź", + "Ż", + "ż", + "Ǔ", + "ǔ", + "μ", + } + codechars7 = { "#", "Q", "X", "x", "£", "ß", "Ą", "ą", "Đ", "đ", "Ę", "ę" } + codechars8 = { "M", "W", "m", "w", "¤", "¶", "Ø", "ø", "Ł" } + codechars9 = { "&", "@", "©", "«", "®", "»" } + codechars10 = { "Æ", "æ", "Œ", "œ" } + function randomchar(arr) + return { + n = G.UIT.O, + config = { + object = DynaText({ + string = arr, + colours = { G.C.BLACK }, + pop_in_rate = 9999999, + silent = true, + random_element = true, + pop_delay = 0.1, + scale = 0.4, + min_cycle_time = 0, + }), + }, + } + end + + --Run - don't open packs in shop + local gfco = G.FUNCS.can_open + G.FUNCS.can_open = function(e) + if G.GAME.USING_RUN then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + gfco(e) + end + end + local gfts = G.FUNCS.toggle_shop + G.FUNCS.toggle_shop = function(e) + gfts(e) + if G.GAME.USING_RUN then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.5, + func = function() + G.GAME.USING_RUN = false + G.GAME.USING_CODE = false + return true + end, + })) + local hand_count = #G.cry_runarea.cards + for i = 1, hand_count do + draw_card(G.cry_runarea, G.hand, i * 100 / hand_count, "up", true) + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.5, + func = function() + G.cry_runarea:remove() + G.cry_runarea = nil + G.STATE = G.STATES.SELECTING_HAND + return true + end, + })) + end + end + local gus = Game.update_shop + function Game:update_shop(dt) + gus(self, dt) + if G.GAME.USING_RUN and G.STATE_COMPLETE and G.GAME.RUN_STATE_COMPLETE < 60 then + G.shop.alignment.offset.y = -5.3 + G.GAME.RUN_STATE_COMPLETE = G.GAME.RUN_STATE_COMPLETE + 1 + end + end + local guis = G.UIDEF.shop + function G.UIDEF.shop() + local ret = guis() + if G.GAME.USING_RUN then + G.SHOP_SIGN:remove() + G.SHOP_SIGN = { + remove = function() + return true + end, + alignment = { offset = { y = 0 } }, + } + end + return ret + end + --Pointer Patches + local upd = Game.update + cry_pointer_dt = 0 + function Game:update(dt) + upd(self, dt) + cry_pointer_dt = cry_pointer_dt + dt + if G.P_CENTERS and G.P_CENTERS.c_cry_pointer and cry_pointer_dt > 0.5 then + cry_pointer_dt = 0 + local obj = G.P_CENTERS.c_cry_pointer + obj.pos.x = (obj.pos.x == 4) and 5 or 4 + end + if not G.OVERLAY_MENU and not G.CHOOSE_CARD and G.GAME.USING_POINTER then + G.CHOOSE_CARD = UIBox({ + definition = create_UIBox_pointer(card), + config = { + align = "cm", + offset = { x = 0, y = 10 }, + major = G.ROOM_ATTACH, + bond = "Weak", + instance_type = "POPUP", + }, + }) + G.CHOOSE_CARD.alignment.offset.y = 0 + G.ROOM.jiggle = G.ROOM.jiggle + 1 + G.CHOOSE_CARD:align_to_major() + end + end + local yc = G.FUNCS.your_collection + G.FUNCS.your_collection = function(e) + if G.CHOOSE_CARD then + G.CHOOSE_CARD:remove() + G.CHOOSE_CARD = nil + end + yc(e) + end + --HOOK:// patches + local cj = Card.calculate_joker + function Card:calculate_joker(context) + local ret, trig = cj(self, context) + if + (ret or trig) + and self.ability.cry_hooked + and not context.post_trigger + and not context.cry_hook + and not context.retrigger_joker_check + and not context.megatrigger_check + then + context.cry_hook = true + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].sort_id == self.hook_id then + card_eval_status_text( + G.jokers.cards[i], + "extra", + nil, + nil, + nil, + { message = localize("cry_hooked_ex"), colour = G.C.SET.Code } + ) + cj(G.jokers.cards[i], context) + --I tried a few things to get the color of messages to be green from the other joker, but they haven't worked :( + end + end + context.cry_hook = nil + end + return ret, trig + end + local evaluate_poker_hand_ref = evaluate_poker_hand + function evaluate_poker_hand(hand) + local results = evaluate_poker_hand_ref(hand) + if G.GAME.cry_exploit_override then + if not results[G.GAME.cry_exploit_override][1] then + results[G.GAME.cry_exploit_override] = results["High Card"] -- i would do results.top here but it just doesn't work, if someone could get that working that would be great + end + end + return results + end + --Encoded Deck patches + local Backapply_to_runRef = Back.apply_to_run + function Back.apply_to_run(self) + Backapply_to_runRef(self) + if self.effect.config.cry_encoded then + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + -- Adding a before spawning becuase jen banned copy_paste + if G.P_CENTERS["j_cry_CodeJoker"] and (G.GAME.banned_keys and not G.GAME.banned_keys["j_cry_CodeJoker"]) then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_CodeJoker") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + end + if G.P_CENTERS["j_cry_copypaste"] and (G.GAME.banned_keys and not G.GAME.banned_keys["j_cry_copypaste"]) then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_copypaste") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + end + return true + end + end, + })) + end + if self.effect.config.cry_encoded_downside then + G.GAME.joker_rate = 0 + G.GAME.planet_rate = 0 + G.GAME.tarot_rate = 0 + G.GAME.code_rate = 1e100 + end + end + local Cardstart_dissolveRef = Card.start_dissolve + function Card:start_dissolve(dissolve_colours, silent, dissolve_time_fac, no_juice) + Cardstart_dissolveRef(self,dissolve_colours, silent, dissolve_time_fac, no_juice) + if G.jokers then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].hook_id == self.sort_id then + G.jokers.cards[i].ability.cry_hooked = false + G.jokers.cards[i].hook_id = nil + end + end + end + end + end, + items = code_cards, +} diff --git a/Cryptid/Items/CryptidJokerDisplay.lua b/Cryptid/Items/CryptidJokerDisplay.lua new file mode 100644 index 0000000..d015f12 --- /dev/null +++ b/Cryptid/Items/CryptidJokerDisplay.lua @@ -0,0 +1,1995 @@ +--=============== Page 1 ===============-- +--SuperCell +--Dropshot +--Kidnapping +--Iterum +--Happy House +--Bubble M +--Fast Food M +--M Stack +--Exponentia +--Neon M +--Virgo +--Notebook +--Pot Of Jokes +--Bonk +--Loopy +--=============== Page 2 ===============-- +--Old Membership Card +--Redeo +--Scrabble Tile +--Sacrifice +--Googol Play Card +--Weebonacci +--Reverse card +--Tiny +--Tenebris +--Huge +--Tredecim +--The WHIP +--Lucky Joker +--Canvas +--Megg +--=============== Page 3 ===============-- +--Jolly Joker? +--Cursor +--Pickle +--ERROR +--Cube +--Triplet Rhythm +--Chili Pepper +--Compound Interest +--Crustulum +--Big Cube +--m +--Boredom +--Eternal Flame +--Nice +--Chad +--=============== Page 4 ===============-- +--Jimball +--Fidget Spinner +--Number Blocks +--Luigi +--Waluigi +--Mario +--Nostalgic candy +--Wario +--Krusty the Clown +--Primus +--Garden of Forking Paths +--Light Up the Night +--No sound, No Memory +--...Like Antennas To heavem +--Caramel +--=============== Page 5 ===============-- +--Sob +--Consume-able +--2D +--Red Bloon +--AP Joker +--Fridge Magnet +--Stella Mortis +--Unjust Dagger +--Monkey Dagger +--Bonus Joker +--Pirate Dagger +--Mondrian +--Sapling +--Celestial Globe +--:D +--=============== Page 6 ===============-- +--Meteor Shower +--Exoplanet +--Stardust +--Mult Joker +--Gold Joker +--Nostalgic Googol play Card +--Circulus Pistoris +--The Duos +--The Home +--The Nuts +--The Quintet +--The Unity +--The Swarm +--The Filler +--Crypto Coin +--=============== Page 7 ===============-- +--Wheel Of Hope +--Old Blueprint +--Night +--Bus Driver +--Morse Code +--Membership Card +--M Chain +--Copy/Paste +--Cut +--Python +--Monster +--Non-Verisimile +--Gemini +--Nostalgic Invisible Joker +--Facile +--=============== Page 8 ===============-- +--Giggly Joker +--Nutty Joker +--Manic Joker +--Silly Joker +--Delirious Joker +--Wacky Joker +--Kooky Joker +--Dubious Joker +--Shrewd Joker +--Tricksy Joker +--Foxy Joker +--Savvy Joker +--Subtle Joker +--Discreet Joker +--Fractal +--=============== Page 9 ===============-- +--Ace Aequilibrium +--Duplicare +--Queen's Gambit +--Formidiulosus +--The Stronghold +--The Fuck!? +--The Clash +--Bonkers Joker +--Fucked Up Joker +--Foolhardy Joker +--Adroit Joker +--Penetrating Joker +--Treacherous Joker + +if JokerDisplay then + + --Side note: I Don't think retrigger type exp gives a correct value with Emult jokers, but ehhhhh ig I can live with that (It's good enough) + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page1 = {} + + JokerDisplay.Definitions["j_cry_supercell"] = { + text = { + { text = "+", colour = G.C.CHIPS }, + { ref_table = "card.ability.extra", ref_value = "stat1", colour = G.C.CHIPS, retrigger_type = "mult" }, + { text = " +", colour = G.C.MULT }, + { ref_table = "card.ability.extra", ref_value = "stat1", colour = G.C.MULT, retrigger_type = "mult" }, + }, + extra = { + { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "stat2", retrigger_type = "exp" }, + }, + border_colour = G.C.CHIPS, + }, + { text = " " }, + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "stat2", retrigger_type = "exp" }, + }, + }, + }, + { + { text = "+$", colour = G.C.GOLD }, + { ref_table = "card.ability.extra", ref_value = "money", colour = G.C.GOLD }, + { + ref_table = "card.joker_display_values", + ref_value = "localized_text", + colour = G.C.UI.TEXT_INACTIVE, + scale = 0.3, + }, + }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_dropshot"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "dropshot_card_suit" }, + { text = ")" }, + }, + calc_function = function(card) + local dropshot_suit = G.GAME + and G.GAME.current_round.cry_dropshot_card + and G.GAME.current_round.cry_dropshot_card.suit + card.joker_display_values.dropshot_card_suit = dropshot_suit + and localize(G.GAME.current_round.cry_dropshot_card.suit, "suits_plural") + or "-" + end, + style_function = function(card, text, reminder_text, extra) + if reminder_text and reminder_text.children[2] then + local dropshot_suit = G.GAME + and G.GAME.current_round.cry_dropshot_card + and G.GAME.current_round.cry_dropshot_card.suit + reminder_text.children[2].config.colour = dropshot_suit + and lighten(G.C.SUITS[G.GAME.current_round.cry_dropshot_card.suit], 0.35) + or G.C.ORANGE + end + return false + end, + } + JokerDisplay.Definitions["j_cry_kidnap"] = { + text = { + { text = "+$" }, + { ref_table = "card.ability.extra", ref_value = "money" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_iterum"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + calc_function = function(card) + local count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + card.joker_display_values.x_mult = card.ability.extra.x_mult ^ count + end, + retrigger_function = function(playing_card, scoring_hand, held_in_hand, joker_card) + if held_in_hand then + return 0 + end + return (joker_card.ability.extra.repetitions * JokerDisplay.calculate_joker_triggers(joker_card)) or 0 + end, + } + JokerDisplay.Definitions["j_cry_happyhouse"] = { + text = { + { + border_nodes = { + { text = "^" }, + { + ref_table = "card.joker_display_values", + ref_value = "e_mult", + retrigger_type = function(number, triggers) + local num = number + for i = 1, triggers - 1 do + num = num ^ number + end + return num + end, + }, + }, + border_colour = G.C.DARK_EDITION, + }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.ability.extra", ref_value = "check" }, + { text = "/114)" }, + }, + calc_function = function(card) + card.joker_display_values.e_mult = card.ability.extra.check >= 114 and card.ability.extra.mult or 1 + end, + } + JokerDisplay.Definitions["j_cry_bubblem"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + reminder_text_config = { scale = 0.35 }, + calc_function = function(card) + card.joker_display_values.localized_text = localize(card.ability.extra.type, "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_foodm"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "mult", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.MULT }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "rounds_remaining" }, + { text = ")" }, + }, + calc_function = function(card) + card.joker_display_values.rounds_remaining = localize({ + type = "variable", + key = "loyalty_inactive", + vars = { card.ability.extra.rounds_remaining or 2 }, + }) + end, + } + JokerDisplay.Definitions["j_cry_mstack"] = { + reminder_text = { + { text = "(" }, + { + ref_table = "card.ability.extra", + ref_value = "retriggers", + colour = G.C.ORANGE, + retrigger_type = "mult", + }, + { text = ")" }, + }, + retrigger_function = function(playing_card, scoring_hand, held_in_hand, joker_card) + if held_in_hand then + return 0 + end + return (joker_card.ability.extra.retriggers * JokerDisplay.calculate_joker_triggers(joker_card)) or 0 + end, + } + JokerDisplay.Definitions["j_cry_exponentia"] = { + text = { + { + border_nodes = { + { text = "^" }, + { + ref_table = "card.ability.extra", + ref_value = "Emult", + retrigger_type = function(number, triggers) + local num = number + for i = 1, triggers - 1 do + num = num ^ number + end + return num + end, + }, + }, + border_colour = G.C.DARK_EDITION, + }, + }, + } + JokerDisplay.Definitions["j_cry_mneon"] = { + text = { + { text = "+$" }, + { ref_table = "card.ability.extra", ref_value = "money" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_virgo"] = { + reminder_text = { + { text = "(" }, + { text = "$", colour = G.C.GOLD }, + { ref_table = "card", ref_value = "sell_cost", colour = G.C.GOLD }, + { text = ") (" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + reminder_text_config = { scale = 0.35 }, + calc_function = function(card) + card.joker_display_values.localized_text = localize(card.ability.extra.type, "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_notebook"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.ability.extra", ref_value = "slot" }, + { text = ") " }, + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + extra = { + { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "odds" }, + { text = " in " }, + { ref_table = "card.ability.extra", ref_value = "odds" }, + { text = ")" }, + }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" + .. (card.ability.extra.check and localize("k_active_ex") or "Inactive") + .. ")" + card.joker_display_values.odds = G.GAME and G.GAME.probabilities.normal or 1 + end, + } + JokerDisplay.Definitions["j_cry_pot_of_jokes"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "h_size", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + card.joker_display_values.h_size = (card.ability.extra.h_size >= 0 and "+" or "") + .. card.ability.extra.h_size + end, + } + JokerDisplay.Definitions["j_cry_bonk"] = { + mod_function = function(card, mod_joker) + local chips_mod = mod_joker.ability.extra.chips + if card.ability.name == "Jolly Joker" or (card.edition and card.edition.key == "e_cry_m") then + chips_mod = chips_mod * mod_joker.ability.extra.xchips + end + return { chips = chips_mod * JokerDisplay.calculate_joker_triggers(mod_joker) or nil } + end, + } + JokerDisplay.Definitions["j_cry_loopy"] = { + text = { + { text = "x" }, + { ref_table = "card.ability.extra", ref_value = "retrigger" }, + }, + retrigger_joker_function = function(card, retrigger_joker) + return retrigger_joker.ability.extra.retrigger or 0 + end, + } + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page2 = {} + + JokerDisplay.Definitions["j_cry_membershipcardtwo"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "stat", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + calc_function = function(card) + card.joker_display_values.stat = card.ability.extra.chips * (GLOBAL_cry_member_count or 1) + end, + } + JokerDisplay.Definitions["j_cry_redeo"] = { + reminder_text = { + { text = "($" }, + { ref_table = "card.ability.extra", ref_value = "money_remaining" }, + { text = "/$" }, + { ref_table = "card.ability.extra", ref_value = "money_req" }, + { text = ")" }, + }, + } + JokerDisplay.Definitions["j_cry_scrabble"] = { + extra = { + { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "odds" }, + { text = " in " }, + { ref_table = "card.ability.extra", ref_value = "odds" }, + { text = ")" }, + }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + card.joker_display_values.odds = G.GAME and G.GAME.probabilities.normal or 1 + end, + } + JokerDisplay.Definitions["j_cry_sacrifice"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.ability.extra", ref_value = "text" }, + { text = ")" }, + }, + } + JokerDisplay.Definitions["j_cry_googol_play"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "Xmult", retrigger_type = "exp" }, + }, + }, + }, + extra = { + { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "odds" }, + { text = " in " }, + { ref_table = "card.ability.extra", ref_value = "odds" }, + { text = ")" }, + }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + card.joker_display_values.odds = G.GAME and G.GAME.probabilities.normal or 1 + end, + } + JokerDisplay.Definitions["j_cry_wee_fib"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "mult", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.MULT }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("Ace", "ranks") .. ",2,3,5,8)" + end, + } + JokerDisplay.Definitions["j_cry_reverse"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + reminder_text_config = { scale = 0.35 }, + calc_function = function(card) + card.joker_display_values.localized_text = localize(card.ability.extra.type, "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_smallestm"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = localize(card.ability.extra.type, "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_tenebris"] = { + text = { + { text = "+$" }, + { ref_table = "card.ability.extra", ref_value = "money" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_biggestm"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + card.joker_display_values.x_mult = card.ability.extra.check and card.ability.extra.x_mult or 1 + card.joker_display_values.localized_text = localize(card.ability.extra.type, "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_mprime"] = { + mod_function = function(card, mod_joker) + return { e_mult = ( + card.ability.name == "Jolly Joker" + or card.edition and card.edition.key == "e_cry_m" + or card.ability.effect == "M Joker" + ) + and mod_joker.ability.extra.mult * JokerDisplay.calculate_joker_triggers(mod_joker) or nil } + end, + } + JokerDisplay.Definitions["j_cry_whip"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + reminder_text = { + { text = "(2,7)" }, + }, + } + JokerDisplay.Definitions["j_cry_lucky_joker"] = { + text = { + { ref_table = "card.joker_display_values", ref_value = "count", retrigger_type = "mult" }, + { text = "x", scale = 0.35 }, + { text = "$", colour = G.C.GOLD }, + { ref_table = "card.ability.extra", ref_value = "dollars", colour = G.C.GOLD }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + local count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card.ability.effect and scoring_card.ability.effect == "Lucky Card" then + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.count = count + card.joker_display_values.localized_text = + localize({ type = "name_text", set = "Enhanced", key = "m_lucky" }) + end, + } + JokerDisplay.Definitions["j_cry_canvas"] = { + text = { + { text = "x" }, + { ref_table = "card.joker_display_values", ref_value = "num_retriggers" }, + }, + calc_function = function(card) + local num_retriggers = 0 + if G.jokers then + for i = 1, #G.jokers.cards do + if + card.T.x + card.T.w / 2 < G.jokers.cards[i].T.x + G.jokers.cards[i].T.w / 2 + and G.jokers.cards[i].config.center.rarity ~= 1 + then + num_retriggers = num_retriggers + 1 + end + end + end + card.joker_display_values.num_retriggers = num_retriggers + end, + retrigger_joker_function = function(card, retrigger_joker) + return card.T.x + card.T.w / 2 < retrigger_joker.T.x + retrigger_joker.T.w / 2 + and retrigger_joker.joker_display_values.num_retriggers + or 0 + end, + } + JokerDisplay.Definitions["j_cry_Megg"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "amount" }, + }, + text_config = { colour = G.C.ORANGE }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page3 = {} + + JokerDisplay.Definitions["j_cry_jollysus"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.ability.extra", ref_value = "active" }, + { text = ")" }, + }, + } + JokerDisplay.Definitions["j_cry_cursor"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "chips", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + } + JokerDisplay.Definitions["j_cry_pickle"] = { + reminder_text = { + { text = "(" }, + { text = "+", colour = G.C.ORANGE }, + { ref_table = "card.ability.extra", ref_value = "tags", colour = G.C.ORANGE, retrigger_type = "mult" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = " " .. localize("b_tags") + end, + } + JokerDisplay.Definitions["j_cry_error"] = { + text = { + { + dynatext = { + -- Maybe this can be defined before so we don't have to hard code the value + string = { "+", "-", "X", "/", "^", "=", ">", "<", "m" }, + colours = { G.C.DARK_EDITION }, + pop_in_rate = 9999999, + silent = true, + random_element = true, + pop_delay = 0.30, + scale = 0.4, + min_cycle_time = 0, + }, + }, + { + dynatext = { + string = { + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "69", + "404", + "420", + "-1", + "0.5", + "m", + "nan", + "inf", + "nil", + "pi", + "1e9", + "???", + }, + colours = { G.C.DARK_EDITION }, + pop_in_rate = 9999999, + silent = true, + random_element = true, + pop_delay = 0.33, + scale = 0.4, + min_cycle_time = 0, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_cube"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "chips", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + } + JokerDisplay.Definitions["j_cry_triplet_rhythm"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + calc_function = function(card) + local count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" and #scoring_hand == 3 then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:get_id() and scoring_card:get_id() == 3 then + count = count + 1 + end + end + end + card.joker_display_values.x_mult = count == 3 and card.ability.extra.Xmult or 1 + end, + } + JokerDisplay.Definitions["j_cry_chili_pepper"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "Xmult", retrigger_type = "exp" }, + }, + }, + }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "rounds_remaining" }, + }, + calc_function = function(card) + card.joker_display_values.start_round = card.joker_display_values.start_round + or card.ability.extra.rounds_remaining + card.joker_display_values.rounds_remaining = "(" + .. card.ability.extra.rounds_remaining + .. "/" + .. card.joker_display_values.start_round + .. ")" + end, + } + JokerDisplay.Definitions["j_cry_compound_interest"] = { + text = { + { text = "+$" }, + { ref_table = "card.joker_display_values", ref_value = "dollars" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + local bonus = math.max(0, math.floor(0.01 * card.ability.extra.percent * (G.GAME.dollars or 1))) + card.joker_display_values.dollars = bonus and bonus > 0 and bonus or 0 + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_crustulum"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "chips", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + } + JokerDisplay.Definitions["j_cry_big_cube"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_chips", retrigger_type = "exp" }, + }, + border_colour = G.C.CHIPS, + }, + }, + } + JokerDisplay.Definitions["j_cry_m"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_boredom"] = { + extra = { + { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "odds" }, + { text = " in " }, + { ref_table = "card.ability.extra", ref_value = "odds" }, + { text = ")" }, + }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + card.joker_display_values.odds = G.GAME and G.GAME.probabilities.normal or 1 + end, + } + JokerDisplay.Definitions["j_cry_eternalflame"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_nice"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "chips", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + reminder_text = { + { text = "(6+9)" }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + local six_count = 0 + local nine_count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + for _, played_card in pairs(hand) do + if played_card:get_id() then + if played_card:get_id() == 6 then + six_count = six_count + 1 + elseif played_card:get_id() == 9 then + nine_count = nine_count + 1 + end + end + end + card.joker_display_values.chips = six_count > 0 and nine_count > 0 and card.ability.extra.chips or 0 + end, + } + JokerDisplay.Definitions["j_cry_chad"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + local leftmost_joker_key = G.jokers + and G.jokers.cards[1] + and G.jokers.cards[1] ~= card + and G.jokers.cards[1].config.center.key + card.joker_display_values.localized_text = leftmost_joker_key + and localize({ type = "name_text", key = leftmost_joker_key, set = "Joker" }) + or "-" + end, + retrigger_joker_function = function(card, retrigger_joker) + return card ~= retrigger_joker and G.jokers.cards[1] == card and retrigger_joker.ability.extra.retriggers + or 0 + end, + } + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page4 = {} + + JokerDisplay.Definitions["j_cry_jimball"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + calc_function = function(card) + local hand = G.hand.highlighted + local text, _, _ = JokerDisplay.evaluate_hand(hand) + local play_more_than = 0 + local hand_exists = text ~= "Unknown" and G.GAME and G.GAME.hands and G.GAME.hands[text] + if hand_exists then + for k, v in pairs(G.GAME.hands) do + if text ~= k and v.played and v.played >= play_more_than and v.visible then + play_more_than = v.played + end + end + end + card.joker_display_values.x_mult = ( + hand_exists + and (G.GAME.hands[text].played < play_more_than and 1 or card.ability.x_mult + card.ability.extra) + or card.ability.x_mult + ) + end, + } + JokerDisplay.Definitions["j_cry_fspinner"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "chips", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + calc_function = function(card) + local hand = G.hand.highlighted + local text, _, _ = JokerDisplay.evaluate_hand(hand) + local play_more_than = 0 + local hand_exists = text ~= "Unknown" and G.GAME and G.GAME.hands and G.GAME.hands[text] + if hand_exists then + for k, v in pairs(G.GAME.hands) do + if v.played and v.played >= play_more_than and v.visible then + play_more_than = v.played + end + end + end + card.joker_display_values.chips = ( + hand_exists + and (G.GAME.hands[text].played < play_more_than and card.ability.extra.chips + card.ability.extra.chip_mod) + or card.ability.extra.chips + ) + end, + } + JokerDisplay.Definitions["j_cry_number_blocks"] = { + text = { + { text = "+$" }, + { ref_table = "card.ability.extra", ref_value = "money" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text_rank" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text_round" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text_rank = "(" + .. localize( + G.GAME.current_round.cry_nb_card and G.GAME.current_round.cry_nb_card.rank or "Ace", + "ranks" + ) + .. ") " + card.joker_display_values.localized_text_round = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_luigi"] = { + mod_function = function(card, mod_joker) + return { x_chips = mod_joker.ability.extra.x_chips ^ JokerDisplay.calculate_joker_triggers(mod_joker) } + end, + } + JokerDisplay.Definitions["j_cry_waluigi"] = { + mod_function = function(card, mod_joker) + return { x_mult = mod_joker.ability.extra.Xmult ^ JokerDisplay.calculate_joker_triggers(mod_joker) } + end, + } + JokerDisplay.Definitions["j_cry_mario"] = { + text = { + { text = "x" }, + { ref_table = "card.ability.extra", ref_value = "retriggers" }, + }, + retrigger_joker_function = function(card, retrigger_joker) + return retrigger_joker.ability.extra.retriggers or 0 + end, + } + JokerDisplay.Definitions["j_cry_oldcandy"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "hand_size" }, + }, + text_config = { colour = G.C.ORANGE }, + } + JokerDisplay.Definitions["j_cry_wario"] = { + mod_function = function(card, mod_joker) + return { dollars = mod_joker.ability.extra.money } + end, + } + JokerDisplay.Definitions["j_cry_krustytheclown"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_primus"] = { + text = { + { + border_nodes = { + { text = "^" }, + { + ref_table = "card.ability.extra", + ref_value = "Emult", + retrigger_type = function(number, triggers) + local num = number + for i = 1, triggers - 1 do + num = num ^ number + end + return num + end, + }, + }, + border_colour = G.C.DARK_EDITION, + }, + }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("Ace", "ranks") .. ",2,3,5,7)" + end, + } + JokerDisplay.Definitions["j_cry_gardenfork"] = { + text = { + { text = "+$" }, + { ref_table = "card.joker_display_values", ref_value = "dollars", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + local ace_count = 0 + local seven_count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:get_id() then + if scoring_card:get_id() == 14 then + ace_count = ace_count + 1 + elseif scoring_card:get_id() == 7 then + seven_count = seven_count + 1 + end + end + end + end + card.joker_display_values.dollars = ace_count > 0 and seven_count > 0 and card.ability.extra.money or 0 + card.joker_display_values.localized_text = "(" .. localize("Ace", "ranks") .. "+7)" + end, + } + JokerDisplay.Definitions["j_cry_lightupthenight"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + reminder_text = { + { text = "(7,2)" }, + }, + calc_function = function(card) + local count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card:get_id() and (scoring_card:get_id() == 2 or scoring_card:get_id() == 7) then + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.x_mult = card.ability.extra.xmult ^ count + end, + } + JokerDisplay.Definitions["j_cry_nosound"] = { + retrigger_function = function(playing_card, scoring_hand, held_in_hand, joker_card) + if held_in_hand then + return 0 + end + return playing_card:get_id() + and playing_card:get_id() == 7 + and joker_card.ability.extra.retriggers * JokerDisplay.calculate_joker_triggers(joker_card) + or 0 + end, + } + JokerDisplay.Definitions["j_cry_antennastoheaven"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_chips", retrigger_type = "exp" }, + }, + border_colour = G.C.CHIPS, + }, + }, + reminder_text = { + { text = "(7,4)" }, + }, + } + JokerDisplay.Definitions["j_cry_caramel"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "rounds_remaining" }, + }, + calc_function = function(card) + local count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + card.joker_display_values.x_mult = card.ability.extra.x_mult ^ count + + card.joker_display_values.start_round = card.joker_display_values.start_round + or card.ability.extra.rounds_remaining + card.joker_display_values.rounds_remaining = "(" + .. card.ability.extra.rounds_remaining + .. "/" + .. card.joker_display_values.start_round + .. ")" + end, + } + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page5 = {} + + JokerDisplay.Definitions["j_cry_curse_sob"] = { + text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.DARK_EDITION }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = localize("cry_sobbing") + end, + } + JokerDisplay.Definitions["j_cry_hunger"] = { + text = { + { text = "+$" }, + { ref_table = "card.ability.extra", ref_value = "money" }, + }, + text_config = { colour = G.C.GOLD }, + } + JokerDisplay.Definitions["j_cry_weegaming"] = { + retrigger_function = function(playing_card, scoring_hand, held_in_hand, joker_card) + if held_in_hand then + return 0 + end + return playing_card:get_id() + and playing_card:get_id() == 2 + and joker_card.ability.extra.retriggers * JokerDisplay.calculate_joker_triggers(joker_card) + or 0 + end, + } + JokerDisplay.Definitions["j_cry_redbloon"] = { + text = { + { text = "+$" }, + { ref_table = "card.joker_display_values", ref_value = "dollars" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + { ref_table = "card.joker_display_values", ref_value = "rounds_remaining" }, + }, + calc_function = function(card) + card.joker_display_values.dollars = card.ability.extra.rounds_remaining + and card.ability.extra.rounds_remaining <= 1 + and card.ability.extra.money + or 0 + + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + card.joker_display_values.start_round = card.joker_display_values.start_round + or card.ability.extra.rounds_remaining + card.joker_display_values.rounds_remaining = " (" + .. card.ability.extra.rounds_remaining + .. "/" + .. card.joker_display_values.start_round + .. ")" + end, + } + JokerDisplay.Definitions["j_cry_apjoker"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + calc_function = function(card) + local is_boss = G.GAME and G.GAME.blind and G.GAME.blind.get_type and G.GAME.blind:get_type() == "Boss" + card.joker_display_values.x_mult = is_boss and card.ability.extra.x_mult or 1 + end, + } + JokerDisplay.Definitions["j_cry_magnet"] = { + text = { + { text = "+$" }, + { ref_table = "card.joker_display_values", ref_value = "dollars" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.dollars = G.jokers + and #G.jokers.cards <= card.ability.extra.slots + and card.ability.extra.money * card.ability.extra.Xmoney + or card.ability.extra.money + + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_stella_mortis"] = { + text = { + { + border_nodes = { + { text = "^" }, + { + ref_table = "card.ability.extra", + ref_value = "Emult", + retrigger_type = function(number, triggers) + local num = number + for i = 1, triggers - 1 do + num = num ^ number + end + return num + end, + }, + }, + border_colour = G.C.DARK_EDITION, + }, + }, + } + JokerDisplay.Definitions["j_cry_unjust_dagger"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_monkey_dagger"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "chips", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + } + JokerDisplay.Definitions["j_cry_bonusjoker"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "count" }, + }, + text_config = { colour = G.C.DARK_EDITION }, + extra = { + { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "odds" }, + { text = " in " }, + { ref_table = "card.ability.extra", ref_value = "odds" }, + { text = ")" }, + }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + local count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card.ability.effect and scoring_card.ability.effect == "Bonus Card" then + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.count = math.min(count, 2 - card.ability.extra.check) + card.joker_display_values.odds = G.GAME and G.GAME.probabilities.normal or 1 + end, + } + JokerDisplay.Definitions["j_cry_pirate_dagger"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_chips", retrigger_type = "exp" }, + }, + border_colour = G.C.CHIPS, + }, + }, + } + JokerDisplay.Definitions["j_cry_mondrian"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_sapling"] = { + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + local is_active = card.ability.extra.score >= card.ability.extra.req + card.joker_display_values.localized_text = "(" + .. (is_active and localize("k_active_ex") or (card.ability.extra.score .. "/" .. card.ability.extra.req)) + .. ")" + end, + } + JokerDisplay.Definitions["j_cry_spaceglobe"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_chips", retrigger_type = "exp" }, + }, + border_colour = G.C.CHIPS, + }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = localize(card.ability.extra.type, "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_happy"] = { + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page6 = {} + + JokerDisplay.Definitions["j_cry_meteor"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "chips", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.CHIPS }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.DARK_EDITION }, + { text = ")" }, + }, + calc_function = function(card) + local chips = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do --Foil cards scored (Note: Meteor shower does not support foil cards held in hand) + if scoring_card.edition and scoring_card.edition.foil == true then + chips = chips + + card.ability.extra.chips + * JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.chips = chips + card.joker_display_values.localized_text = localize({ type = "name_text", set = "Edition", key = "e_foil" }) + end, + mod_function = function(card, mod_joker) --Foil Jokers + return { chips = (card ~= mod_joker and card.edition and card.edition.foil == true) and + mod_joker.ability.extra.chips * JokerDisplay.calculate_joker_triggers(mod_joker) or nil } + end, + } + JokerDisplay.Definitions["j_cry_exoplanet"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "mult", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.MULT }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.DARK_EDITION }, + { text = ")" }, + }, + calc_function = function(card) + local count = 0 + local playing_hand = next(G.play.cards) + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do --Holographic cards scored + if scoring_card.edition and scoring_card.edition.holo == true then + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + for _, playing_card in ipairs(G.hand.cards) do --Holographic cards held in hand + if playing_hand or not playing_card.highlighted then + if not (playing_card.facing == 'back') and not playing_card.debuff + and playing_card.edition and playing_card.edition.holo == true then + count = count + JokerDisplay.calculate_card_triggers(playing_card, nil, true) + end + end + end + card.joker_display_values.mult = card.ability.extra.mult * count + card.joker_display_values.localized_text = localize({ type = "name_text", set = "Edition", key = "e_holo" }) + end, + mod_function = function(card, mod_joker)--Holographic Jokers + return { mult = (card ~= mod_joker and card.edition and card.edition.holo == true) and + mod_joker.ability.extra.mult * JokerDisplay.calculate_joker_triggers(mod_joker) or nil } + end, + } + JokerDisplay.Definitions["j_cry_stardust"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.DARK_EDITION }, + { text = ")" }, + }, + calc_function = function(card) + local count = 0 + local playing_hand = next(G.play.cards) + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do --Polychrome cards scored + if scoring_card.edition and scoring_card.edition.polychrome == true then + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + for _, playing_card in ipairs(G.hand.cards) do --Polychrome cards held in hand + if playing_hand or not playing_card.highlighted then + if not (playing_card.facing == 'back') and not playing_card.debuff + and playing_card.edition and playing_card.edition.polychrome == true then + count = count + JokerDisplay.calculate_card_triggers(playing_card, nil, true) + end + end + end + card.joker_display_values.x_mult = card.ability.extra.xmult ^ count + card.joker_display_values.localized_text = + localize({ type = "name_text", set = "Edition", key = "e_polychrome" }) + end, + mod_function = function(card, mod_joker) --Polychrome Jokers + return { x_mult = (card ~= mod_joker and card.edition and card.edition.polychrome == true) and + mod_joker.ability.extra.xmult ^ JokerDisplay.calculate_joker_triggers(mod_joker) or nil } + end, + } + JokerDisplay.Definitions["j_cry_multjoker"] = { + text = { + { text = "+" }, + { ref_table = "card.joker_display_values", ref_value = "count", retrigger_type = "mult" }, + }, + text_config = { colour = G.C.SECONDARY_SET.Spectral }, + extra = { + { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "odds" }, + { text = " in " }, + { ref_table = "card.ability.extra", ref_value = "odds" }, + { text = ")" }, + }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + local count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do + if scoring_card.ability.effect and scoring_card.ability.effect == "Mult Card" then + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + end + card.joker_display_values.count = count + card.joker_display_values.odds = G.GAME and G.GAME.probabilities.normal or 1 + end, + } + JokerDisplay.Definitions["j_cry_goldjoker"] = { + text = { + { text = "+$" }, + { ref_table = "card.joker_display_values", ref_value = "dollars" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + local bonus = math.max(0, math.floor(0.01 * card.ability.extra.percent * (G.GAME.dollars or 0))) + card.joker_display_values.dollars = bonus and bonus > 0 and bonus or 0 + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_altgoogol"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + local leftmost_joker_key = G.jokers + and G.jokers.cards[1] + and G.jokers.cards[1] ~= card + and G.jokers.cards[1].config.center.key + card.joker_display_values.localized_text = leftmost_joker_key + and localize({ type = "name_text", key = leftmost_joker_key, set = "Joker" }) + or "-" + end, + } + JokerDisplay.Definitions["j_cry_circulus_pistoris"] = { + text = { + { text = "^", colour = G.C.CHIPS }, + { ref_table = "card.ability.extra", ref_value = "Echips", colour = G.C.CHIPS }, + { text = "^", colour = G.C.MULT }, + { ref_table = "card.ability.extra", ref_value = "Emult", colour = G.C.MULT }, + }, + extra = { + + { + ref_table = "card.joker_display_values", + ref_value = "localized_text", + colour = G.C.UI.TEXT_INACTIVE, + scale = 0.3, + }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + local hand_xmult_jd = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + local x_mult = 1 + local text, poker_hands, _ = JokerDisplay.evaluate_hand() + if text ~= "Unknown" and poker_hands[card.ability.type] and next(poker_hands[card.ability.type]) then + x_mult = card.ability.x_mult + end + card.joker_display_values.x_mult = x_mult + card.joker_display_values.localized_text = localize(card.ability.type, "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_duos"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_home"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_nuts"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_quintet"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_unity"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_swarm"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_filler"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_coin"] = { + text = { + { text = "+$" }, + { ref_table = "card.ability.extra", ref_value = "money" }, + { text = "-" }, + { ref_table = "card.joker_display_values", ref_value = "money" }, + }, + text_config = { colour = G.C.GOLD }, + calc_function = function(card) + card.joker_display_values.money = (card.ability.extra.money * 10) + end, + } + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page7 = {} + + JokerDisplay.Definitions["j_cry_wheelhope"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "x_mult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_oldblueprint"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "blueprint_compat", colour = G.C.RED }, + { text = ")" }, + }, + extra = { + { + { text = "(" }, + { ref_table = "G.GAME.probabilities", ref_value = "normal" }, --the usual thing doesn't work for some reason + { text = " in " }, + { ref_table = "card.ability.extra", ref_value = "odds" }, + { text = ")" }, + }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + local copied_joker, copied_debuff = JokerDisplay.calculate_blueprint_copy(card) + card.joker_display_values.blueprint_compat = localize("k_incompatible") + JokerDisplay.copy_display(card, copied_joker, copied_debuff) + end, + get_blueprint_joker = function(card) + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + return G.jokers.cards[i + 1] + end + end + return nil + end, + } + JokerDisplay.Definitions["j_cry_night"] = { + text = { + { + border_nodes = { + { text = "^" }, + { + ref_table = "card.joker_display_values", + ref_value = "e_mult", + retrigger_type = function(number, triggers) + local num = number + for i = 1, triggers - 1 do + num = num ^ number + end + return num + end, + }, + }, + border_colour = G.C.DARK_EDITION, + }, + }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.e_mult = (G.GAME and G.GAME.current_round.hands_left <= 1) + and card.ability.extra.mult + or 1 + card.joker_display_values.localized_text = "(" + .. ((G.GAME and G.GAME.current_round.hands_left <= 1) and localize("k_active_ex") or "Inactive") + .. ")" + end, + } + JokerDisplay.Definitions["j_cry_busdriver"] = { + text = { + { text = "+", colour = G.C.MULT }, + { ref_table = "card.ability.extra", ref_value = "mult", colour = G.C.MULT }, + { text = " or -", colour = G.C.MULT }, + { ref_table = "card.ability.extra", ref_value = "mult", colour = G.C.MULT }, + }, + } + JokerDisplay.Definitions["j_cry_morse"] = { + text = { + { text = "+$" }, + { ref_table = "card.ability.extra", ref_value = "money" }, + }, + text_config = { colour = G.C.GOLD }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_membershipcard"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.joker_display_values", ref_value = "stat", retrigger_type = "exp" }, + }, + }, + }, + calc_function = function(card) + card.joker_display_values.stat = + math.max(1, (card.ability.extra.Xmult_mod * (GLOBAL_cry_member_count or 1))) + end, + } + JokerDisplay.Definitions["j_cry_cryptidmoment"] = { + text = { + { text = "+" }, + { ref_table = "card.ability.extra", ref_value = "money" }, + }, + text_config = { colour = G.C.ORANGE }, + } + JokerDisplay.Definitions["j_cry_copypaste"] = { + extra = { + { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "odds" }, + { text = " in " }, + { ref_table = "card.ability.extra", ref_value = "odds" }, + { text = ")" }, + }, + }, + extra_config = { colour = G.C.GREEN, scale = 0.3 }, + calc_function = function(card) + card.joker_display_values.odds = G.GAME and G.GAME.probabilities.normal or 1 + end, + } + JokerDisplay.Definitions["j_cry_cut"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "Xmult", retrigger_type = "exp" }, + }, + border_colour = G.C.MULT, + }, + }, + } + JokerDisplay.Definitions["j_cry_python"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "Xmult", retrigger_type = "exp" }, + }, + border_colour = G.C.MULT, + }, + }, + } + JokerDisplay.Definitions["j_cry_longboi"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "mult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_verisimile"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "xmult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_gemino"] = { + text = { + { text = "X2" }, + }, + text_config = { colour = G.C.GREEN }, + reminder_text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text" }, + }, + calc_function = function(card) + card.joker_display_values.localized_text = "(" .. localize("k_round") .. ")" + end, + } + JokerDisplay.Definitions["j_cry_oldinvisible"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.ability", ref_value = "extra" }, + { text = "/4)" }, + } + } + JokerDisplay.Definitions["j_cry_facile"] = { + text = { + { + border_nodes = { + { text = "^" }, + { ref_table = "card.joker_display_values", ref_value = "Emult", retrigger_type = "exp" }, + }, + border_colour = G.C.DARK_EDITION, + }, + }, + calc_function = function(card) + local count = 0 + local hand = next(G.play.cards) and G.play.cards or G.hand.highlighted + local text, _, scoring_hand = JokerDisplay.evaluate_hand(hand) + if text ~= "Unknown" then + for _, scoring_card in pairs(scoring_hand) do + count = count + JokerDisplay.calculate_card_triggers(scoring_card, scoring_hand) + end + end + card.joker_display_values.Emult = (count <= card.ability.extra.check and card.ability.extra.Emult or 1) + end, + } + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page8 = {} + + local hand_tmult_jd = { + text = { + { text = "+", colour = G.C.MULT }, + { ref_table = "card.joker_display_values", ref_value = "t_mult", colour = G.C.MULT, retrigger_type = "mult" }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + local t_mult = 0 + local text, poker_hands, _ = JokerDisplay.evaluate_hand() + if text ~= "Unknown" and poker_hands[card.ability.type] and next(poker_hands[card.ability.type]) then + t_mult = card.ability.t_mult + end + card.joker_display_values.t_mult = t_mult + card.joker_display_values.localized_text = localize(card.ability.type, "poker_hands") + end, + } + local hand_tchips_jd = { + text = { + { text = "+", colour = G.C.CHIPS }, + { ref_table = "card.joker_display_values", ref_value = "t_chips", colour = G.C.CHIPS, retrigger_type = "mult" }, + }, + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + calc_function = function(card) + local t_chips = 0 + local text, poker_hands, _ = JokerDisplay.evaluate_hand() + if text ~= "Unknown" and poker_hands[card.ability.type] and next(poker_hands[card.ability.type]) then + t_chips = card.ability.t_chips + end + card.joker_display_values.t_chips = t_chips + card.joker_display_values.localized_text = localize(card.ability.type, "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_giggly"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_nutty"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_manic"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_silly"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_delirious"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_wacky"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_kooky"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_dubious"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_shrewd"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_tricksy"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_foxy"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_savvy"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_subtle"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_discreet"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_fractal"] = { + text = { + { text = "+" }, + { ref_table = "card.ability", ref_value = "extra" }, + }, + text_config = { colour = G.C.ORANGE }, + } + + --This is here so it shows up on the github symbol panel (easy to scroll to) + local page9 = {} + + JokerDisplay.Definitions["j_cry_duplicare"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "Xmult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_equilib"] = { + text = { + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + }, + calc_function = function(card) + local joker_generated = "???" + if G.GAME.aequilibriumkey and G.GAME.aequilibriumkey > 1 then + joker_generated = localize({ + type = "name_text", + set = "Joker", + key = G.P_CENTER_POOLS["Joker"][math.floor(G.GAME.aequilibriumkey or 1) - 1].key, + }) + end + card.joker_display_values.localized_text = joker_generated + end, + } + JokerDisplay.Definitions["j_cry_queens_gambit"] = { + reminder_text = { + { text = "(" }, + { ref_table = "card.joker_display_values", ref_value = "localized_text", colour = G.C.ORANGE }, + { text = ")" }, + }, + reminder_text_config = { scale = 0.35 }, + calc_function = function(card) + card.joker_display_values.localized_text = localize("Royal Flush", "poker_hands") + end, + } + JokerDisplay.Definitions["j_cry_duplicare"] = { + text = { + { + border_nodes = { + { text = "X" }, + { ref_table = "card.ability.extra", ref_value = "Xmult", retrigger_type = "exp" }, + }, + }, + }, + } + JokerDisplay.Definitions["j_cry_formidiulosus"] = { + text = { + { + border_nodes = { + { text = "^" }, + { + ref_table = "card.ability.extra", + ref_value = "Emult", + retrigger_type = function(number, triggers) + local num = number + for i = 1, triggers - 1 do + num = num ^ number + end + return num + end, + }, + }, + border_colour = G.C.DARK_EDITION, + }, + }, + } + JokerDisplay.Definitions["j_cry_stronghold"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_wtf"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_clash"] = hand_xmult_jd + JokerDisplay.Definitions["j_cry_bonkers"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_fuckedup"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_foolhardy"] = hand_tmult_jd + JokerDisplay.Definitions["j_cry_adroit"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_penetrating"] = hand_tchips_jd + JokerDisplay.Definitions["j_cry_treacherous"] = hand_tchips_jd + + --end of Jokerdisplays +end +return { name = "JokerDisplay" } diff --git a/Cryptid/Items/Decks.lua b/Cryptid/Items/Decks.lua new file mode 100644 index 0000000..1bc5cbb --- /dev/null +++ b/Cryptid/Items/Decks.lua @@ -0,0 +1,468 @@ +local atlasdeck = { + object_type = "Atlas", + key = "atlasdeck", + path = "atlasdeck.png", + px = 71, + py = 95, +} +local atlasglowing = { + object_type = "Atlas", + key = "glowing", + path = "b_cry_glowing.png", + px = 71, + py = 95, +} +local very_fair = { + object_type = "Back", + name = "Very Fair Deck", + key = "very_fair", + config = { hands = -2, discards = -2, cry_no_vouchers = true }, + pos = { x = 4, y = 0 }, + order = 1, + --[[loc_vars = function(self, info_queue, center) + return {vars = {center.effect.config.hands, center.effect.config.discards}} + end,--]] + --this doesn't work, will fix later + atlas = "atlasdeck", +} + +very_fair_quip = {} + +local equilibrium = { + object_type = "Back", + name = "cry-Equilibrium", + key = "equilibrium", + order = 3, + config = { vouchers = { "v_overstock_norm", "v_overstock_plus" }, cry_equilibrium = true }, + pos = { x = 0, y = 1 }, + atlas = "atlasdeck", +} +local misprint = { + object_type = "Back", + name = "cry-Misprint", + key = "misprint", + order = 4, + config = { cry_misprint_min = 0.1, cry_misprint_max = 10 }, + pos = { x = 4, y = 2 }, + atlas = "atlasdeck", +} +local infinite = { + object_type = "Back", + name = "cry-Infinite", + key = "infinite", + order = 2, + config = { cry_highlight_limit = 1e20, hand_size = 1 }, + pos = { x = 3, y = 0 }, + atlas = "atlasdeck", +} +local conveyor = { + object_type = "Back", + name = "cry-Conveyor", + key = "conveyor", + order = 7, + config = { cry_conveyor = true }, + pos = { x = 1, y = 1 }, + atlas = "atlasdeck", +} +local CCD = { + object_type = "Back", + name = "cry-CCD", + key = "CCD", + order = 5, + config = { cry_ccd = true }, + pos = { x = 0, y = 0 }, + atlas = "atlasdeck", +} +local wormhole = { + object_type = "Back", + name = "cry-Wormhole", + key = "wormhole", + order = 6, + config = { cry_wormhole = true, cry_negative_rate = 20, joker_slot = -2 }, + pos = { x = 3, y = 4 }, + atlas = "atlasdeck", +} +local redeemed = { + object_type = "Back", + name = "cry-Redeemed", + key = "redeemed", + order = 8, + config = { cry_redeemed = true }, + pos = { x = 4, y = 4 }, + atlas = "atlasdeck", +} +local legendary = { + object_type = "Back", + name = "cry-Legendary", + key = "legendary", + config = { cry_legendary = true, cry_legendary_rate = 0.2 }, + pos = { x = 0, y = 6 }, + atlas = "atlasdeck", + order = 15, + trigger_effect = function(self, args) + if args.context == "eval" and G.GAME.last_blind and G.GAME.last_blind.boss then + if G.jokers then + if #G.jokers.cards < G.jokers.config.card_limit then + local legendary_poll = pseudorandom(pseudoseed("cry_legendary")) + legendary_poll = legendary_poll / (G.GAME.probabilities.normal or 1) + if legendary_poll < self.config.cry_legendary_rate then + local card = create_card("Joker", G.jokers, true, 4, nil, nil, nil, "") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + return true + else + card_eval_status_text( + G.jokers, + "jokers", + nil, + nil, + nil, + { message = localize("k_nope_ex"), colour = G.C.RARITY[4] } + ) + end + else + card_eval_status_text( + G.jokers, + "jokers", + nil, + nil, + nil, + { message = localize("k_no_room_ex"), colour = G.C.RARITY[4] } + ) + end + end + end + end, +} +local critical = { + object_type = "Back", + name = "cry-Critical", + key = "critical", + order = 10, + config = { cry_crit_rate = 0.25, cry_crit_miss_rate = 0.125 }, + pos = { x = 4, y = 5 }, + atlas = "atlasdeck", + loc_vars = function(self, info_queue, center) + return { vars = { G.GAME.probabilities.normal or 1 } } + end, + trigger_effect = function(self, args) + if args.context == "final_scoring_step" then + local crit_poll = pseudorandom(pseudoseed("cry_critical")) + crit_poll = crit_poll / (G.GAME.probabilities.normal or 1) + if crit_poll < self.config.cry_crit_rate then + args.mult = args.mult ^ 2 + update_hand_text({ delay = 0 }, { mult = args.mult, chips = args.chips }) + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("talisman_emult", 1) + attention_text({ + scale = 1.4, + text = localize("cry_critical_hit_ex"), + hold = 2, + align = "cm", + offset = { x = 0, y = -2.7 }, + major = G.play, + }) + return true + end, + })) + elseif crit_poll < self.config.cry_crit_rate + self.config.cry_crit_miss_rate then + args.mult = args.mult ^ 0.5 + update_hand_text({ delay = 0 }, { mult = args.mult, chips = args.chips }) + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("timpani", 1) + attention_text({ + scale = 1.4, + text = localize("cry_critical_miss_ex"), + hold = 2, + align = "cm", + offset = { x = 0, y = -2.7 }, + major = G.play, + }) + return true + end, + })) + end + delay(0.6) + return args.chips, args.mult + end + end, +} +local glowing = { + object_type = "Back", + name = "cry-Glowing", + key = "glowing", + config = { cry_glowing = true }, + pos = { x = 4, y = 2 }, + order = 9, + loc_vars = function(self, info_queue, center) + return { vars = { " " } } + end, + atlas = "glowing", + trigger_effect = function(self, args) + if args.context == "eval" and G.GAME.last_blind and G.GAME.last_blind.boss then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.name ~= "Ace Aequilibrium" then --Same Reason as Gemini/Multiply + cry_with_deck_effects(G.jokers.cards[i], function(card) + cry_misprintize(card, { min = 1.25, max = 1.25 }, nil, true) + end) + end + end + end + end, +} +local beta = { + object_type = "Back", + name = "cry-Beta", + key = "beta", + config = { cry_beta = true }, + pos = { x = 5, y = 5 }, + order = 13, + atlas = "atlasdeck", +} +local bountiful = { + object_type = "Back", + name = "cry-Bountiful", + key = "bountiful", + config = { cry_forced_draw_amount = 5 }, + pos = { x = 4, y = 2 }, + order = 14, + atlas = "placeholders", +} +return { + name = "Misc. Decks", + init = function() + local Backapply_to_runRef = Back.apply_to_run + function Back.apply_to_run(self) + Backapply_to_runRef(self) + if self.effect.config.cry_no_vouchers then + G.GAME.modifiers.cry_no_vouchers = true + end + if self.effect.config.cry_equilibrium then + G.GAME.modifiers.cry_equilibrium = true + end + if self.effect.config.cry_conveyor then + G.GAME.modifiers.cry_conveyor = true + end + if self.effect.config.cry_misprint_min then + G.GAME.modifiers.cry_misprint_min = self.effect.config.cry_misprint_min + G.GAME.modifiers.cry_misprint_max = self.effect.config.cry_misprint_max + end + if self.effect.config.cry_highlight_limit then + G.GAME.modifiers.cry_highlight_limit = self.effect.config.cry_highlight_limit + end + if self.effect.config.cry_ccd then + G.GAME.modifiers.cry_ccd = true + end + if self.effect.config.cry_beta then + G.GAME.modifiers.cry_beta = true + end + if self.effect.config.cry_legendary then + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + local card = create_card("Joker", G.jokers, true, 4, nil, nil, nil, "") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + return true + end + end, + })) + end + if self.effect.config.cry_wormhole then + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + local card = + create_card("Joker", G.jokers, nil, "cry_exotic", nil, nil, nil, "cry_wormhole") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + return true + end + end, + })) + end + if self.effect.config.cry_negative_rate then + G.GAME.modifiers.cry_negative_rate = self.effect.config.cry_negative_rate + end + if self.effect.config.cry_redeemed then + G.GAME.modifiers.cry_redeemed = true + end + if self.effect.config.cry_forced_draw_amount then + G.GAME.modifiers.cry_forced_draw_amount = self.effect.config.cry_forced_draw_amount + end + end + --equilibrium deck patches + local gcp = get_current_pool + function get_current_pool(t, r, l, a, override_equilibrium_effect) + if + G.GAME.modifiers.cry_equilibrium + and not override_equilibrium_effect + and (a == "sho" or t == "Voucher" or t == "Booster") + then + if + t ~= "Enhanced" + and t ~= "Edition" + and t ~= "Back" + and t ~= "Tag" + and t ~= "Seal" + and t ~= "Stake" + then + if not P_CRY_ITEMS then + P_CRY_ITEMS = {} + local valid_pools = { "Joker", "Consumeables", "Voucher", "Booster" } + for _, id in ipairs(valid_pools) do + for k, v in pairs(G.P_CENTER_POOLS[id]) do + if v.unlocked == true and not center_no(v, "doe", k) then + P_CRY_ITEMS[#P_CRY_ITEMS + 1] = v.key + end + end + end + for k, v in pairs(G.P_CARDS) do + if v.unlocked == true and not center_no(v, "doe", k) then + P_CRY_ITEMS[#P_CRY_ITEMS + 1] = v.key + end + end + end + return P_CRY_ITEMS, "cry_equilibrium" .. G.GAME.round_resets.ante + end + end + return gcp(t, r, l, a) + end + local gp = get_pack + function get_pack(k, t) + if G.GAME.modifiers.cry_equilibrium then + if not P_CRY_ITEMS then + P_CRY_ITEMS = {} + local valid_pools = { "Joker", "Consumeables", "Voucher", "Booster" } + for _, id in ipairs(valid_pools) do + for k, v in pairs(G.P_CENTER_POOLS[id]) do + if not center_no(v, "doe", k) then + P_CRY_ITEMS[#P_CRY_ITEMS + 1] = v.key + end + end + end + for k, v in pairs(G.P_CARDS) do + if not center_no(v, "doe", k) then + P_CRY_ITEMS[#P_CRY_ITEMS + 1] = v.key + end + end + end + return G.P_CENTERS[pseudorandom_element( + P_CRY_ITEMS, + pseudoseed("cry_equipackbrium" .. G.GAME.round_resets.ante) + )] + end + return gp(k, t) + end + --wormhole deck patches + SMODS.Edition:take_ownership("negative", { + get_weight = function(self) + return self.weight * (G.GAME.modifiers.cry_negative_rate or 1) + end, + }, true) + --redeemed deck patches + local cr = Card.redeem + function Card:redeem() + cr(self) + if G.GAME.modifiers.cry_redeemed then + if + #G.play.cards == 0 + and (not G.redeemed_vouchers_during_hand or #G.redeemed_vouchers_during_hand.cards == 0) + then + G.cry_redeemed_buffer = {} + end + for k, v in pairs(G.P_CENTER_POOLS["Voucher"]) do + if v.requires and not G.GAME.used_vouchers[v] then + for _, vv in pairs(v.requires) do + if vv == self.config.center.key then + --redeem extra voucher code based on Betmma's Vouchers + local area + if G.STATE == G.STATES.HAND_PLAYED then + if not G.redeemed_vouchers_during_hand then + G.redeemed_vouchers_during_hand = CardArea( + G.play.T.x, + G.play.T.y, + G.play.T.w, + G.play.T.h, + { type = "play", card_limit = 5 } + ) + end + area = G.redeemed_vouchers_during_hand + else + area = G.play + end + if not G.cry_redeemed_buffer[v.key] + and v.unlocked then + local card = create_card("Voucher", area, nil, nil, nil, nil, v.key) + G.cry_redeemed_buffer[v.key] = true + card:start_materialize() + area:emplace(card) + card.cost = 0 + card.shop_voucher = false + local current_round_voucher = G.GAME.current_round.voucher + card:redeem() + G.GAME.current_round.voucher = current_round_voucher + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + func = function() + card:start_dissolve() + return true + end, + })) + end + end + end + end + end + end + end + --glowing deck patches + local upd = Game.update + cry_glowing_dt = 0 + function Game:update(dt) + upd(self, dt) + cry_glowing_dt = cry_glowing_dt + dt + if G.P_CENTERS and G.P_CENTERS.b_cry_glowing and cry_glowing_dt > 0.1 then + cry_glowing_dt = 0 + local obj = G.P_CENTERS.b_cry_glowing + if obj.pos.x == 1 and obj.pos.y == 4 then + obj.pos.x = 0 + obj.pos.y = 0 + elseif obj.pos.x < 4 then + obj.pos.x = obj.pos.x + 1 + elseif obj.pos.y < 6 then + obj.pos.x = 0 + obj.pos.y = obj.pos.y + 1 + end + end + for k, v in pairs(G.I.CARD) do + if v.children.back and v.children.back.atlas.name == "cry_glowing" then + v.children.back:set_sprite_pos(G.P_CENTERS.b_cry_glowing.pos or G.P_CENTERS["b_red"].pos) + end + end + end + end, + items = { + atlasdeck, + very_fair, + equilibrium, + misprint, + infinite, + conveyor, + CCD, + wormhole, + redeemed, + legendary, + critical, + atlasglowing, + glowing, + beta, + bountiful, + }, +} diff --git a/Cryptid/Items/Enhanced.lua b/Cryptid/Items/Enhanced.lua new file mode 100644 index 0000000..73c4c50 --- /dev/null +++ b/Cryptid/Items/Enhanced.lua @@ -0,0 +1,484 @@ +-- Enhanced has to be loaded near-last because of Jolly edition +-- Not localized for now - will be rewritten later + +local atlasenhanced = { + object_type = "Atlas", + key = "atlasenhanced", + path = "atlasdeck.png", + px = 71, + py = 95, +} + +packs_to_add = { atlasenhanced } + +local typed_decks = { + -- {'mod_prefix', 'Type', 'Name of Deck', 'Name of Object', 'Object Key', 'Shader Name', 'Atlas', 'posX', 'posY', 'Flavour Text', 'Add Price Increase'}, + -- eg. 'cry_' for Edition, Leave nil to construct Usually matches Leave nil to use All three of these are used Small subtext underneath If true, editions + -- Cryptid cards Enhancement, automatically from name object key as name for custom deck backs main text affect the price of + -- Leave empty Seal, object name Used instead for Should be nil for Leave nil to use default cards in shop + -- for vanilla Sticker, banned boss blind non-shader objects fallback + -- Suit on Suit decks + -- For stickers ONLY, + -- prefix must be included + -- if you use one + -- + -- Vanilla + { "", "Enhancement", "The Hierophant's Deck", "Bonus", "bonus", nil, "atlasenhanced", 3, 3, "" }, + { "", "Enhancement", "The Empress' Deck", "Mult", "mult", nil, "atlasenhanced", 2, 3, "" }, + { "", "Enhancement", "The Lovers' Deck", "Wild", "wild", nil, "atlasenhanced", 5, 3, "" }, + { "", "Enhancement", "Deck of Justice", "Glass", "glass", nil, "atlasenhanced", 4, 3, "" }, + { "", "Enhancement", "The Chariot's Deck", "Steel", "steel", nil, nil, 6, 1, "" }, + { "", "Enhancement", "Stoner's Deck", "Stone", "stone", nil, nil, 5, 0, "" }, + { "", "Enhancement", "The Devil's Deck", "Gold", "gold", nil, nil, 6, 0, "" }, + { "", "Enhancement", "The Magician's Deck", "Lucky", "lucky", nil, nil, 4, 1, "" }, + + { "", "Edition", "Deck of Chips", "Foil", "foil", nil, nil, 0, 2, "" }, + { "", "Edition", "Deck of Mult", "Holographic", "holo", nil, nil, 0, 0, "" }, + { "", "Edition", "Deck of XMult", "Polychrome", "polychrome", nil, nil, 5, 2, "" }, + { "", "Edition", nil, "Negative", "negative", nil, nil, 5, 2, "" }, + + { "", "Seal", "Talisman Deck", "Gold", "Gold", nil, nil, 1, 2, "" }, + { "", "Seal", "Déja Vu Deck", "Red", "Red", nil, nil, 0, 0, "" }, + { "", "Seal", "Trance Deck", "Blue", "Blue", nil, "atlasenhanced", 2, 2, "" }, + { "", "Seal", "Medium Deck", "Purple", "Purple", nil, "atlasenhanced", 1, 2, "" }, + + { "", "Sticker", nil, "Eternal", "eternal", nil, "atlasenhanced", 5, 2, "" }, + { "", "Sticker", nil, "Perishable", "perishable", nil, "atlasenhanced", 0, 3, "" }, + { "", "Sticker", nil, "Rental", "rental", nil, "atlasenhanced", 1, 3, "" }, + { "", "Sticker", nil, "Pinned", "pinned", nil, "atlasenhanced", 0, 5, "" }, + + { "", "Suit", "Deck of the Stars", "Diamonds", "window", nil, "atlasenhanced", 2, 1, "" }, + { "", "Suit", "Deck of the Sun", "Hearts", "head", nil, "atlasenhanced", 3, 1, "" }, + { "", "Suit", "Deck of the World", "Spades", "goad", nil, "atlasenhanced", 4, 1, "" }, + { "", "Suit", "Deck of the Moon", "Clubs", "club", nil, "atlasenhanced", 5, 1, "" }, + + -- Cryptid + -- todo: work with mod config better here + + { "cry", "Enhancement", "The Eclipse's Deck", "Echo", "echo", nil, "atlasenhanced", 1, 5, "" }, + + { "cry", "Edition", nil, "Fragile", "glass", nil, nil, 5, 2, "" }, + { "cry", "Edition", nil, "Golden", "gold", nil, nil, 5, 2, "" }, + { "cry", "Edition", nil, "Noisy", "noisy", nil, nil, 5, 2, "" }, + { "cry", "Edition", nil, "Astral", "astral", nil, nil, 5, 2, "" }, + { "cry", "Edition", nil, "Blurred", "blur", nil, nil, 0, 0, "" }, + { "cry", "Edition", nil, "Mosaic", "mosaic", nil, nil, 5, 2, "" }, + { "cry", "Edition", nil, "Oversaturated", "oversat", nil, nil, 5, 2, "" }, + { + "cry", + "Edition", + nil, + "Glitched", + "glitched", + nil, + nil, + 5, + 2, + "Wait, isn't this just Misprint Deck?", + }, + + { "cry", "Seal", "Typhoon Deck", "Azure", "azure", nil, nil, 0, 2, "" }, + + { "cry", "Sticker", nil, "Banana", "banana", nil, "atlasenhanced", 5, 4, "" }, +} + +if Cryptid.enabled["M Jokers"] then -- Crashes the game if M jokers are disabled if we don't add this separately + table.insert(typed_decks, 31, { "cry", "Edition", "Meck", "Jolly", "m", nil, nil, 5, 2, "" }) +end + +for i = 1, #typed_decks do + local deck = typed_decks[i] + + local shader = nil + if deck[6] then + shader = deck[1] .. "_" .. deck[6] + if deck.no_prefix then + shader = deck[6] + end + end + + local deck_name = deck[3] + if not deck_name then + deck_name = deck[4] .. " Deck" + end + + local deck_internal_name = "" + if deck[1] == "cry" then -- don't register eg. 'cry-cry-Typhoon Deck' + deck_internal_name = "cry-" .. deck_name + else -- eg. 'cry-jen-Blood Deck' + deck_internal_name = "cry-" .. deck[1] .. "-" .. deck_name + end + + local deck_key = "" + if deck[1] == "cry" then + deck_key = "cry" .. (deck[5] or deck[4]) .. "_deck" + else + deck_key = "cry" .. deck[1] .. "-" .. (deck[5] or deck[4]) .. "_deck" + end + + local object_key = "" + if deck[1] == "" or deck.no_prefix then -- vanilla doesn't have a prefix, don't add the _ + object_key = deck[5] or deck[4] + else + object_key = deck[1] .. "_" .. (deck[5] or deck[4]) + end + + local suit_key = "" + if deck[1] == "" or deck.no_prefix then + suit_key = deck[4] + else + suit_key = deck[1] .. "_" .. deck[4] + end + + if deck[2] == "Edition" then + local obj = { + object_type = "Back", + name = deck_internal_name, + key = deck_key, + config = { cry_force_edition = object_key, cry_force_edition_shader = shader }, + pos = { x = deck[8], y = deck[9] }, + loc_txt = { + name = deck_name, + text = { + "All cards are {C:dark_edition,T:" .. object_key .. "}" .. deck[4] .. " Cards{}", + "Cards cannot change editions", + "{s:0.8,C:inactive}" .. deck[10], + }, + }, + } + if deck[7] then + obj.atlas = deck[7] + if string.find(deck[7], "_") then + obj.prefix_config = { atlas = false } + end + end + if not deck[11] then + obj.config.cry_no_edition_price = true + end + packs_to_add[#packs_to_add + 1] = obj + elseif deck[2] == "Enhancement" then + local obj = { + object_type = "Back", + name = deck_internal_name, + key = deck_key, + config = { cry_force_enhancement = "m_" .. object_key }, + pos = { x = deck[8], y = deck[9] }, + loc_txt = { + name = deck_name, + text = { + "All {C:attention}playing cards{}", + "are {C:attention,T:m_" .. object_key .. "}" .. deck[4] .. " Cards{}", + "Cards cannot change enhancements", + "{s:0.8,C:inactive}" .. deck[10], + }, + }, + } + + if deck[7] then + obj.atlas = deck[7] + if string.find(deck[7], "_") then + obj.prefix_config = { atlas = false } + end + end + packs_to_add[#packs_to_add + 1] = obj + elseif deck[2] == "Seal" then + local obj = { + object_type = "Back", + name = deck_internal_name, + key = deck_key, + config = { cry_force_seal = object_key }, + pos = { x = deck[8], y = deck[9] }, + loc_txt = { + name = deck_name, + text = { + "All cards have a {C:dark_edition}" .. deck[4] .. " Seal{}", + "Cards cannot change seals", + "{s:0.8,C:inactive}" .. deck[10], + }, + }, + } + + if deck[7] then + obj.atlas = deck[7] + if string.find(deck[7], "_") then + obj.prefix_config = { atlas = false } + end + end + packs_to_add[#packs_to_add + 1] = obj + elseif deck[2] == "Sticker" then + local obj = { + object_type = "Back", + name = deck_internal_name, + key = deck_key, + config = { cry_force_sticker = deck[5] }, -- stickers DON'T use object_key for SOME reason + pos = { x = deck[8], y = deck[9] }, + loc_txt = { + name = deck_name, + text = { + "All cards are {C:attention}" .. deck[4] .. "{}", + "{s:0.8,C:inactive}" .. deck[10], + }, + }, + } + + if deck[7] then + obj.atlas = deck[7] + if string.find(deck[7], "_") then + obj.prefix_config = { atlas = false } + end + end + packs_to_add[#packs_to_add + 1] = obj + elseif deck[2] == "Suit" then + local obj = { + object_type = "Back", + name = deck_internal_name, + key = deck_key, + config = { cry_force_suit = suit_key, cry_boss_blocked = deck[5] and { "bl_" .. object_key } }, + pos = { x = deck[8], y = deck[9] }, + loc_txt = { + name = deck_name, + text = { + "All playing cards are {C:dark_edition}" .. deck[4] .. "{}", + "and cannot change suits", + deck[10] or "{s:0}", + deck[5] and "{C:attention}The " .. string.upper(string.sub(deck[5], 1, 1)) .. string.sub( + deck[5], + 2 + ) .. "{} cannot appear", -- UGLY hack + }, + }, + } + + if deck[7] then + obj.atlas = deck[7] + if string.find(deck[7], "_") then + obj.prefix_config = { atlas = false } + end + end + packs_to_add[#packs_to_add + 1] = obj + end +end + +return { + name = "Enhanced Decks", + init = function() + local Backapply_to_runRef = Back.apply_to_run + function Back.apply_to_run(self) + Backapply_to_runRef(self) + + if self.effect.config.cry_force_enhancement then + if self.effect.config.cry_force_enhancement ~= "random" then + G.GAME.modifiers.cry_force_enhancement = self.effect.config.cry_force_enhancement + end + G.E_MANAGER:add_event(Event({ + func = function() + for c = #G.playing_cards, 1, -1 do + if self.effect.config.cry_force_enhancement == "random" then + local enh = {} + for i = 1, #G.P_CENTER_POOLS.Enhanced do + enh[#enh + 1] = G.P_CENTER_POOLS.Enhanced[i] + end + enh[#enh + 1] = "CCD" + local random_enhancement = pseudorandom_element(enh, pseudoseed("cry_ant_enhancement")) + if random_enhancement.key and G.P_CENTERS[random_enhancement.key] then + G.playing_cards[c]:set_ability(G.P_CENTERS[random_enhancement.key]) + else + G.playing_cards[c]:set_ability(get_random_consumable("cry_ant_ccd", nil, true)) + end + else + G.playing_cards[c]:set_ability(G.P_CENTERS[self.effect.config.cry_force_enhancement]) + end + end + + return true + end, + })) + end + if self.effect.config.cry_force_edition then + if self.effect.config.cry_force_edition ~= "random" then + G.GAME.modifiers.cry_force_edition = self.effect.config.cry_force_edition + else + G.GAME.modifiers.cry_force_random_edition = true + end + for k, v in pairs(G.P_TAGS) do + if v.config and v.config.edition then + G.GAME.banned_keys[k] = true + end + end + G.E_MANAGER:add_event(Event({ + func = function() + for c = #G.playing_cards, 1, -1 do + local ed_table = {} + if self.effect.config.cry_force_edition == "random" then + local random_edition = + pseudorandom_element(G.P_CENTER_POOLS.Edition, pseudoseed("cry_ant_edition")) + while random_edition.key == "e_base" do + random_edition = + pseudorandom_element(G.P_CENTER_POOLS.Edition, pseudoseed("cry_ant_edition")) + end + ed_table[random_edition.key:sub(3)] = true + G.playing_cards[c]:set_edition(ed_table, true, true) + else + ed_table[self.effect.config.cry_force_edition] = true + G.playing_cards[c]:set_edition(ed_table, true, true) + end + end + + return true + end, + })) + end + if self.effect.config.cry_force_seal then + if self.effect.config.cry_force_seal ~= "random" then + G.GAME.modifiers.cry_force_seal = self.effect.config.cry_force_seal + end + G.E_MANAGER:add_event(Event({ + func = function() + for c = #G.playing_cards, 1, -1 do + if self.effect.config.cry_force_seal == "random" then + local random_seal = + pseudorandom_element(G.P_CENTER_POOLS.Seal, pseudoseed("cry_ant_seal")) + G.playing_cards[c]:set_seal(random_seal.key, true) + else + G.playing_cards[c]:set_seal(self.effect.config.cry_force_seal, true) + end + end + return true + end, + })) + end + if self.effect.config.cry_force_sticker then + G.GAME.modifiers.cry_force_sticker = self.effect.config.cry_force_sticker + G.E_MANAGER:add_event(Event({ + func = function() + for c = #G.playing_cards, 1, -1 do + G.playing_cards[c].config.center.eternal_compat = true + G.playing_cards[c].config.center.perishable_compat = true + if + SMODS.Stickers[self.effect.config.cry_force_sticker] + and SMODS.Stickers[self.effect.config.cry_force_sticker].apply + then + SMODS.Stickers[self.effect.config.cry_force_sticker]:apply(G.playing_cards[c], true) + else + G.playing_cards[c]["set_" .. self.effect.config.cry_force_sticker]( + G.playing_cards[c], + true + ) + end + end + return true + end, + })) + end + if self.effect.config.cry_force_suit then + G.GAME.modifiers.cry_force_suit = self.effect.config.cry_force_suit + G.E_MANAGER:add_event(Event({ + func = function() + for c = #G.playing_cards, 1, -1 do + G.playing_cards[c]:change_suit(self.effect.config.cry_force_suit) + end + return true + end, + })) + end + if self.effect.config.cry_boss_blocked then + for _, v in pairs(self.effect.config.cry_boss_blocked) do + G.GAME.bosses_used[v] = 1e308 + end + end + if self.effect.config.cry_no_edition_price then + G.GAME.modifiers.cry_no_edition_price = true + end + end + local sa = Card.set_ability + function Card:set_ability(center, y, z) + --adding immutable to cards because + -- A they are hardcoded and unaffected by misprintize but still have a description that changes because of it + -- B so they ignore misprintize in order to keep vanilla descripton accurate (ex hack shouldn't be able to trigger more than once) + -- C so Gemini doesn't say they are compatible when they are not + -- D Invisible Joker + + if center.name == "Fortune Teller" + or center.name == "Shoot the Moon" + or center.name == "Riff-raff" + or center.name == "Business Card" + or center.name == "Chaos the Clown" + or center.name == "Dusk" + or center.name == "Mime" + or center.name == "Hack" + or center.name == "Sock and Buskin" + or center.name == "Invisible Joker" + or center.name == "Swashbuckler" + or center.name == "Smeared Joker" + or center.name == "Certificate" + or center.name == "Mr. Bones" + or center.name == "Diet Cola" + or center.name == "Luchador" + or center.name == "Midas Mask" + or center.name == "Shortcut" + or center.name == "Seance" + or center.name == "Superposition" + or center.name == "Sixth Sense" + or center.name == "DNA" + or center.name == "Splash" + or center.name == "Supernova" + or center.name == "Pareidolia" + or center.name == "Raised Fist" + or center.name == "Marble Joker" + or center.name == "Four Fingers" + or center.name == "Joker Stencil" + or center.name == "Showman" + or center.name == "Blueprint" + or center.name == "Oops! All 6s" + or center.name == "Brainstorm" + or center.name == "Cartomancer" + or center.name == "Astronomer" + or center.name == "Burnt Joker" + or center.name == "Chicot" + or center.name == "Perkeo" + then + self.config.center.immutable = true + end + + if center and center.set == "Enhanced" then + return sa( + self, + (not self.no_forced_enhancement and G.GAME.modifiers.cry_force_enhancement) and G.P_CENTERS[G.GAME.modifiers.cry_force_enhancement] + or center, + y, + z + ) + else + return sa(self, center, y, z) + end + end + local se = Card.set_edition + function Card:set_edition(edition, y, z, force) + if not force then + return se(self, (not self.no_forced_edition and G.GAME.modifiers.cry_force_edition) and { [G.GAME.modifiers.cry_force_edition] = true } or edition, y, z) + end + return se(self, edition, y, z) + end + local ss = Card.set_seal + function Card:set_seal(seal, y, z) + return ss(self, not self.no_forced_seal and G.GAME.modifiers.cry_force_seal or seal, y, z) + end + local cs = Card.change_suit + function Card:change_suit(new_suit) + return cs(self, not self.no_forced_suit and G.GAME.modifiers.cry_force_suit or new_suit) + end + local sc = Card.set_cost + function Card:set_cost() + if self.edition and G.GAME.modifiers.cry_no_edition_price then + local m = cry_deep_copy(self.edition) + self.edition = nil + sc(self) + self.edition = m + else + sc(self) + end + end + end, + order = 1000000, + items = packs_to_add, +} diff --git a/Cryptid/Items/EpicJokers.lua b/Cryptid/Items/EpicJokers.lua new file mode 100644 index 0000000..fee15bc --- /dev/null +++ b/Cryptid/Items/EpicJokers.lua @@ -0,0 +1,1528 @@ +local supercell = { + object_type = "Joker", + name = "cry-supercell", + key = "supercell", + config = { extra = { stat1 = 15, stat2 = 2, money = 3 } }, + pos = { x = 5, y = 1 }, + rarity = "cry_epic", + cost = 14, + order = 64, + blueprint_compat = true, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.stat1, center.ability.extra.stat2, center.ability.extra.money } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after then + if card.ability.extra.stat2 > 1 then --misprint deck moment + return { + message = localize("cry_gaming_ex"), + chip_mod = card.ability.extra.stat1, + mult_mod = card.ability.extra.stat1, + Xchip_mod = card.ability.extra.stat2, + Xmult_mod = card.ability.extra.stat2, + } + end + end + end, + calc_dollar_bonus = function(self, card) + if card.ability.extra.money > 0 then + return card.ability.extra.money + end + end, + add_to_deck = function(self, card, from_debuff) + if not from_debuff then + play_sound("cry_studiofromhelsinki") + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local membershipcardtwo = { + object_type = "Joker", + name = "cry-membershipcardtwo", + key = "membershipcardtwo", + config = { extra = { chips = 1 } }, + pos = { x = 5, y = 4 }, + rarity = "cry_epic", + cost = 17, + order = 50, + blueprint_compat = true, + atlas = "atlasepic", + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.extra.chips, card.ability.extra.chips * GLOBAL_cry_member_count } } + end, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and not context.before + and not context.after + and card.ability.extra.chips > 0 + then + return { + message = localize({ + type = "variable", + key = "a_chips", + vars = { card.ability.extra.chips * GLOBAL_cry_member_count }, + }), + chip_mod = card.ability.extra.chips * GLOBAL_cry_member_count, + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Jevonn" + } + }, +} +local googol_play = { + object_type = "Joker", + name = "cry-Googol Play Card", + key = "googol_play", + config = { extra = { Xmult = 1e100, odds = 8 } }, + pos = { x = 3, y = 0 }, + rarity = "cry_epic", + cost = 10, + order = 14, + blueprint_compat = true, + atlas = "atlasepic", + soul_pos = { x = 10, y = 0, extra = { x = 4, y = 0 } }, + loc_vars = function(self, info_queue, center) + return { + vars = { + "" .. (G.GAME and G.GAME.probabilities.normal or 1), + center.ability.extra.odds, + center.ability.extra.Xmult, + }, + } + end, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and not context.before + and not context.after + and pseudorandom("cry_googol_play") < G.GAME.probabilities.normal / card.ability.extra.odds + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.Xmult } }), + Xmult_mod = card.ability.extra.Xmult, + } + end + end, + cry_credits = { + idea = { + ".asdom" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Math" + } + }, +} +local sync_catalyst = { + object_type = "Joker", + name = "cry-Sync Catalyst", + key = "sync_catalyst", + pos = { x = 5, y = 2 }, + rarity = "cry_epic", + cost = 12, + order = 54, + blueprint_compat = true, + immutable = true, + atlas = "atlasepic", + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before + and not context.after and not context.debuffed_hand + and hand_chips and mult then + local tot = hand_chips + mult + if not tot.array or #tot.array < 2 or tot.array[2] < 2 then --below eXeY notation + hand_chips = mod_chips(math.floor(tot / 2)) + mult = mod_mult(math.floor(tot / 2)) + else + if hand_chips > mult then + tot = hand_chips + else + tot = mult + end + hand_chips = mod_chips(tot) + mult = mod_chips(tot) + end + update_hand_text({ delay = 0 }, { mult = mult, chips = hand_chips }) + return { + message = localize("k_balanced"), + colour = { 0.8, 0.45, 0.85, 1 }, + } + end + end, + cry_credits = { + idea = { + "Project666" + }, + art = { + "Ein13" + }, + code = { + "Math" + } + }, +} +local negative = { + object_type = "Joker", + name = "cry-Negative Joker", + key = "negative", + pos = { x = 1, y = 3 }, + config = { extra = 4 }, + rarity = "cry_epic", + cost = 10, + order = 70, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + add_to_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit + card.ability.extra + end, + remove_from_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit - card.ability.extra + end, + cry_credits = { + idea = { + "Xero01" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Math" + } + }, +} +local canvas = { + object_type = "Joker", + name = "cry-Canvas", + key = "canvas", + immutable = true, + order = 4, + pos = { x = 2, y = 1 }, + config = { num_retriggers = 0 }, + rarity = "cry_epic", + cost = 18, + blueprint_compat = true, + atlas = "atlasepic", + calculate = function(self, card, context) + if context.retrigger_joker_check and not context.retrigger_joker then + self.config.num_retriggers = 0 + for i = 1, #G.jokers.cards do + if + card.T.x + card.T.w / 2 < G.jokers.cards[i].T.x + G.jokers.cards[i].T.w / 2 + and G.jokers.cards[i].config.center.rarity ~= 1 + then + self.config.num_retriggers = self.config.num_retriggers + 1 + end + end + if card.T.x + card.T.w / 2 > context.other_card.T.x + context.other_card.T.w / 2 then + return { + message = localize("k_again_ex"), + repetitions = self.config.num_retriggers, + card = card, + } + end + end + end, + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Mystic Misclick" + }, + code = { + "Math" + } + }, +} +local error_joker = { + object_type = "Joker", + name = "cry-Error", + key = "error", + pos = { x = 4, y = 2 }, + config = { extra = { sell_rounds = 0, active = false } }, + immutable = true, + rarity = "cry_epic", + cost = 1, + order = 72, + blueprint_compat = false, + eternal_compat = false, + atlas = "atlasepic", + add_to_deck = function(self, card, from_debuff) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_edition_from_deck then + G.GAME.modifiers.cry_force_edition_from_deck = G.GAME.modifiers.cry_force_edition + elseif not G.GAME.modifiers.cry_force_edition_from_deck then + if Cryptid.enabled["Misc."] then + G.GAME.modifiers.cry_force_edition = "cry_glitched" + else + G.GAME.modifiers.cry_force_edition = "foil" + end + G.GAME.modifiers.cry_force_edition_from_deck = "Nope!" + end + end, + remove_from_deck = function(self, card, from_debuff) + if G.GAME.modifiers.cry_force_edition_from_deck ~= "Nope!" then + G.GAME.modifiers.cry_force_edition = G.GAME.modifiers.cry_force_edition_from_deck + else + G.GAME.modifiers.cry_force_edition = nil + end + end, + calculate = function(self, card, context) + if + context.end_of_round + and not context.blueprint + and not context.repetition + and not context.individual + and not card.ability.extra.active + then + if card.ability.extra.sell_rounds == 0 then + card.ability.extra.sell_rounds = math.floor(pseudorandom(pseudoseed("cry_error")) * 10 + 1) + end + card.ability.extra.sell_rounds = card.ability.extra.sell_rounds - 1 + if card.ability.extra.sell_rounds == 0 then + card.ability.extra.active = true + local eval = function(card) return not card.REMOVED end + juice_card_until(card, eval, true) + else + return { + message = "???", + colour = G.C.BLACK, + } + end + end + if + context.selling_self + and card.ability.extra.active + and not context.retrigger_joker + and not context.blueprint + then + local eval = function(card) + return (card and card.ability and card.ability.loyalty_remaining == 0) and not G.RESET_JIGGLES + end + juice_card_until(card, eval, true) + local jokers = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.name ~= "cry-Error" then + jokers[#jokers + 1] = G.jokers.cards[i] + end + end + for i = 1, #jokers do + local card = copy_card(jokers[i]) + card:add_to_deck() + G.jokers:emplace(card) + end + return nil, true + end + end, + cry_credits = { + idea = { + "Coronacht", "Fetch" + }, + art = { + "Mystic Misclick" + }, + code = { + "Math" + } + }, +} +local m = { + object_type = "Joker", + discovered = true, + name = "cry-m", + key = "m", + pos = { x = 3, y = 1 }, + config = { extra = { extra = 13, x_mult = 1 }, jolly = { t_mult = 8, type = "Pair" } }, + pools = {["Meme"] = true}, + rarity = "cry_epic", + order = 1, + cost = 13, + effect = "M Joker", + perishable_compat = false, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { vars = { center.ability.extra.extra, center.ability.extra.x_mult } } + end, + atlas = "atlasepic", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.x_mult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + if + context.selling_card + and context.card:is_jolly() + and not context.blueprint + then + card.ability.extra.x_mult = card.ability.extra.x_mult + card.ability.extra.extra + if not context.retrigger_joker then + --This doesn't display the correct amount of mult if retriggered it display the amount from the first retrigger instead of the final one + --But I would rather have this than constant card_eval_status_text spam + --If anyone knows a solution feel free to do a pr xd + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }) } + ) + end + return nil, true + end + end, + post_process = function(self) + if get_m_retriggers then + local vc = self.calculate + self.calculate = function(self, card, context) + local ret, trig = vc(self, card, context) + local reps = get_m_retriggers(self, card, context) + if context.retrigger_joker_check and context.other_card == card and reps > 0 then + return { + message = localize("k_again_ex"), + repetitions = reps + (ret and ret.repetitions or 0), + card = card, + } + end + return ret, trig + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Math" + } + }, +} +local M = { + object_type = "Joker", + name = "cry-M", + key = "M", + pos = { x = 0, y = 0 }, + order = 250, + config = { jolly = { t_mult = 8, type = "Pair" } }, + rarity = "cry_epic", + effect = "M Joker", + cost = 13, + immutable = true, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + if not center.edition or (center.edition and not center.edition.negative) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_negative + end + end, + atlas = "atlasepic", + calculate = function(self, card, context) + if context.setting_blind and not (context.blueprint_card or self).getting_sliced then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") + card:set_edition({ + negative = true, + }) + card:add_to_deck() + G.jokers:emplace(card) + return nil, true + end + end, + post_process = function(self) + if get_m_retriggers then + local vc = self.calculate + self.calculate = function(self, card, context) + local ret, trig = vc(self, card, context) + local reps = get_m_retriggers(self, card, context) + if context.retrigger_joker_check and context.other_card == card and reps > 0 then + return { + message = localize("k_again_ex"), + repetitions = reps + (ret and ret.repetitions or 0), + card = card, + } + end + return ret, trig + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Math" + } + }, +} +local boredom = { + object_type = "Joker", + name = "cry-Boredom", + key = "boredom", + pos = { x = 1, y = 0 }, + config = { extra = { odds = 2 } }, + pools = {["Meme"] = true}, + rarity = "cry_epic", + order = 32, + cost = 14, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { "" .. (G.GAME and G.GAME.probabilities.normal or 1), center.ability.extra.odds } } + end, + atlas = "atlasepic", + calculate = function(self, card, context) + if context.retrigger_joker_check and not context.retrigger_joker and context.other_card ~= self then + if pseudorandom("cry_boredom_joker") < G.GAME.probabilities.normal / card.ability.extra.odds then + return { + message = localize("k_again_ex"), + repetitions = 1, + card = card, + } + else + return nil, true + end + end + if + context.repetition + and context.cardarea == G.play + and pseudorandom("cry_boredom_card") < G.GAME.probabilities.normal / card.ability.extra.odds + then + return { + message = localize("k_again_ex"), + repetitions = 1, + card = card, + } + end + end, + cry_credits = { + idea = { + "Math" + }, + art = { + "Saturn" + }, + code = { + "Math" + } + }, +} +local number_blocks = { + object_type = "Joker", + name = "cry-Number Blocks", + key = "number_blocks", + config = { extra = { money_mod = 1, money = 1 } }, + pos = { x = 0, y = 2 }, + rarity = "cry_epic", + cost = 14, + order = 12, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + return { + vars = { + center.ability.extra.money, + center.ability.extra.money_mod, + localize(G.GAME.current_round.cry_nb_card and G.GAME.current_round.cry_nb_card.rank or "Ace", "ranks"), + }, + } + end, + calculate = function(self, card, context) + if + context.individual + and not context.end_of_round + and context.cardarea == G.hand + and not context.blueprint + and not context.before + and not context.after + and context.other_card:get_id() == G.GAME.current_round.cry_nb_card.id + then + if context.other_card.debuff then + return { + message = localize("k_debuffed"), + colour = G.C.RED, + card = card, + } + else + card.ability.extra.money = card.ability.extra.money + card.ability.extra.money_mod + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_upgrade_ex") }) + return nil, true + end + end + end, + calc_dollar_bonus = function(self, card) + if card.ability.extra.money > 0 then + return card.ability.extra.money + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Math" + } + }, +} +local double_scale = { + object_type = "Joker", + name = "cry-Double Scale", + key = "Double Scale", + pos = { x = 0, y = 3 }, + order = 6, + rarity = "cry_epic", + cost = 18, + immutable = true, + atlas = "atlasepic", + --todo: support jokers that scale multiple variables + cry_scale_mod = function(self, card, joker, orig_scale_scale, true_base, orig_scale_base, new_scale_base) + return orig_scale_scale + true_base + end, + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "AlezZGreat" + }, + code = { + "Math", "Mathguy" + } + }, +} +local oldcandy = { + object_type = "Joker", + name = "cry_oldcandy", + key = "oldcandy", + pos = { x = 4, y = 1 }, + order = 43, + config = { extra = { hand_size = 3 } }, + loc_vars = function(self, info_queue, center) + return { vars = { math.max(1, math.floor(center.ability.extra.hand_size)) } } + end, + rarity = "cry_epic", + cost = 9, + eternal_compat = false, + atlas = "atlasepic", + calculate = function(self, card, context) + if context.selling_self and not context.blueprint then + G.hand:change_size(math.max(1, math.floor(card.ability.extra.hand_size))) + return nil, true + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local circus = { + object_type = "Joker", + name = "cry-circus", + key = "circus", + pos = { x = 4, y = 4 }, + config = { extra = { Xmult = 1 } }, + atlas = "atlasepic", + order = 33, + loc_vars = function(self, info_queue, center) + return { + vars = { + (math.max(1, center.ability.extra.Xmult) * 2), + (math.max(1, center.ability.extra.Xmult) * 3), + (math.max(1, center.ability.extra.Xmult) * 4), + (math.max(1, center.ability.extra.Xmult) * 20), + }, + } + end, + rarity = "cry_epic", + cost = 16, + blueprint_compat = true, + calculate = function(self, card, context) + if context.other_joker and card ~= context.other_joker then + if context.other_joker.config.center.rarity == 3 then --Rare + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { (math.max(1, card.ability.extra.Xmult) * 2) }, + }), + Xmult_mod = (math.max(1, card.ability.extra.Xmult) * 2), + } + elseif context.other_joker.config.center.rarity == 4 then --Legendary + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { (math.max(1, card.ability.extra.Xmult) * 4) }, + }), + Xmult_mod = (math.max(1, card.ability.extra.Xmult) * 4), + } + elseif context.other_joker.config.center.rarity == "cry_epic" then --Epic + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { (math.max(1, card.ability.extra.Xmult) * 3) }, + }), + Xmult_mod = (math.max(1, card.ability.extra.Xmult) * 3), + } + elseif context.other_joker.config.center.rarity == "cry_exotic" then --Exotic + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { (math.max(1, card.ability.extra.Xmult) * 20) }, + }), + Xmult_mod = (math.max(1, card.ability.extra.Xmult) * 20), + } + end + end + end, + cry_credits = { + idea = { + "Ein13" + }, + art = { + "Ein13" + }, + code = { + "Jevonn" + } + }, +} +local caramel = { + object_type = "Joker", + name = "cry-caramel", + key = "caramel", + config = { extra = { x_mult = 1.75, rounds_remaining = 11 } }, + pos = { x = 0, y = 1 }, + rarity = "cry_epic", + cost = 12, + order = 106, + blueprint_compat = true, + eternal_compat = false, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.x_mult, center.ability.extra.rounds_remaining } } + end, + calculate = function(self, card, context) + if context.individual then + if context.cardarea == G.play then + return { + x_mult = card.ability.extra.x_mult, + colour = G.C.RED, + card = card, + } + end + end + if + context.end_of_round + and not context.blueprint + and not context.individual + and not context.repetition + and not context.retrigger_joker + then + card.ability.extra.rounds_remaining = card.ability.extra.rounds_remaining - 1 + if card.ability.extra.rounds_remaining > 0 then + return { + message = { localize("cry_minus_round") }, + colour = G.C.FILTER, + } + else + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("k_eaten_ex"), + colour = G.C.FILTER, + } + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +--this has to be the most spaghetti code in cryptid +local curse_sob = { + object_type = "Joker", + name = "cry_curse_sob", + key = "curse_sob", + pos = { x = 1, y = 1 }, + pools = {["Meme"] = true}, + rarity = "cry_epic", + cost = 9, + order = 82, + immutable = true, + perishable_compat = true, + atlas = "atlasepic", + calculate = function(self, card, context) + if + context.selling_card + and context.card.ability.name == "Obelisk" + and not context.retrigger_joker + and not context.blueprint + then + return {} + elseif + (-- Compacting all the elseifs into one block for space and readability also maintablity + context.selling_self + or context.discard + or context.pre_discard -- We want 2 obilisks per discard? dunno just copying what was there + or context.reroll_shop --Yes + or context.buying_card + or context.skip_blind + or context.using_consumeable + or context.selling_card + or context.setting_blind + or context.skipping_booster + or context.open_booster + ) + and #G.jokers.cards + G.GAME.joker_buffer < (context.selling_self and (G.jokers.config.card_limit + 1) or G.jokers.config.card_limit) + and not context.retrigger_joker + and not context.blueprint + then + local createjoker = math.min(1, G.jokers.config.card_limit - (#G.jokers.cards + G.GAME.joker_buffer)) + G.GAME.joker_buffer = G.GAME.joker_buffer + createjoker + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_obelisk") + card:add_to_deck() + G.jokers:emplace(card) + G.GAME.joker_buffer = 0 + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("cry_curse_ex"), + colour = G.C.FILTER, + }), + } + end + end, + add_to_deck = function(self, card, from_debuff) + if not from_debuff then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_obelisk") + card:set_edition("e_negative", true, nil, true) + card.sob = true + card:set_eternal(true) + card:add_to_deck() + G.jokers:emplace(card) + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("cry_curse_ex"), + colour = G.C.DARK_EDITION, + }), + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local bonusjoker = { + object_type = "Joker", + name = "cry-Bonus Joker", + key = "bonusjoker", + pos = { x = 3, y = 2 }, + config = { extra = { odds = 8, check = 0 } }, + immutable = true, + rarity = "cry_epic", + cost = 11, + order = 75, + blueprint_compat = true, + enhancement_gate = "m_bonus", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = G.P_CENTERS.m_bonus + return { vars = { "" .. (G.GAME and G.GAME.probabilities.normal or 1), center.ability.extra.odds } } + end, + atlas = "atlasepic", + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if context.other_card.ability.effect == "Bonus Card" then + if + pseudorandom("bonusjoker") < G.GAME.probabilities.normal / card.ability.extra.odds + and card.ability.extra.check < 2 + and not context.retrigger_joker + then + local option = pseudorandom_element({ 1, 2 }, pseudoseed("bonusjoker")) + if option == 1 then + if not context.blueprint then + card.ability.extra.check = card.ability.extra.check + 1 + end + G.jokers.config.card_limit = G.jokers.config.card_limit + 1 + else + if not context.blueprint then + card.ability.extra.check = card.ability.extra.check + 1 + end + G.consumeables.config.card_limit = G.consumeables.config.card_limit + 1 + end + return { + extra = { focus = card, message = localize("k_upgrade_ex") }, + card = card, + colour = G.C.MONEY, + } + end + end + end + if + context.end_of_round + and card.ability.extra.check ~= 0 + and not context.blueprint + and not context.retrigger_joker + and not context.individual + and not context.repetition + then + card.ability.extra.check = 0 + return { + message = localize("k_reset"), + card = card, + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local multjoker = { + object_type = "Joker", + name = "cry-Mult Joker", + key = "multjoker", + pos = { x = 2, y = 3 }, + config = { extra = { odds = 3 } }, + rarity = "cry_epic", + order = 99, + cost = 11, + blueprint_compat = true, + enhancement_gate = "m_mult", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = G.P_CENTERS.m_mult + info_queue[#info_queue + 1] = G.P_CENTERS.c_cryptid + return { vars = { "" .. (G.GAME and G.GAME.probabilities.normal or 1), center.ability.extra.odds } } + end, + atlas = "atlasepic", + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if + context.other_card.ability.effect == "Mult Card" + and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit + then + if pseudorandom("multjoker") < G.GAME.probabilities.normal / card.ability.extra.odds then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + func = function() + local new_card = + create_card("Spectral", G.consumeables, nil, nil, nil, nil, "c_cryptid", "multjoker") + new_card:add_to_deck() + G.consumeables:emplace(new_card) + G.GAME.consumeable_buffer = 0 + return true + end, + })) + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { message = localize("cry_plus_cryptid"), colour = G.C.SECONDARY_SET.Spectral } + ) + return nil, true + end + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local goldjoker = { + object_type = "Joker", + name = "cry-gold Joker", + key = "goldjoker", + config = { extra = { percent_mod = 2, percent = 0 } }, + pos = { x = 0, y = 4 }, + rarity = "cry_epic", + cost = 14, + order = 81, + enhancement_gate = "m_gold", + perishable_compat = false, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = G.P_CENTERS.m_gold + return { vars = { center.ability.extra.percent, center.ability.extra.percent_mod } } + end, + calculate = function(self, card, context) + if context.cardarea == G.play and context.individual and not context.blueprint then + if context.other_card.ability.effect == "Gold Card" then + card.ability.extra.percent = card.ability.extra.percent + card.ability.extra.percent_mod + return { + extra = { focus = card, message = localize("k_upgrade_ex") }, + card = card, + colour = G.C.MONEY, + } + end + end + if context.individual and context.cardarea == G.play then + if context.other_card.ability.effect == "Gold Card" then + card.ability.extra.percent = card.ability.extra.percent + card.ability.extra.percent_mod + return { + message = localize("k_upgrade_ex"), + card = card, + colour = G.C.CHIPS, + } + end + end + end, + calc_dollar_bonus = function(self, card) + local bonus = math.max(0, math.floor(0.01 * card.ability.extra.percent * (G.GAME.dollars or 0))) + if bonus > 0 then + return bonus + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local altgoogol = { + object_type = "Joker", + name = "cry-altgoogol", + key = "altgoogol", + pos = { x = 4, y = 3 }, + immutable = true, + rarity = "cry_epic", + cost = 10, + order = 60, + blueprint_compat = true, + eternal_compat = false, + atlas = "atlasepic", + soul_pos = { x = 10, y = 0, extra = { x = 5, y = 3 } }, + calculate = function(self, card, context) + if context.selling_self and not context.retrigger_joker then + local jokers = {} + for i=1, #G.jokers.cards do + if G.jokers.cards[i] ~= card then + jokers[#jokers+1] = G.jokers.cards[i] + end + end + if #jokers > 0 then + if G.jokers.cards[1].ability.name ~= "cry-altgoogol" then + local spawn = {G.jokers.cards[1]} + G.E_MANAGER:add_event(Event({ + func = function() + for i = 1, 2 do + local card = copy_card(pseudorandom_element(spawn, pseudoseed("cry_ngpc")), nil) + card:add_to_deck() + G.jokers:emplace(card) + end + return true + end, + })) + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { + message = localize("k_duplicated_ex"), + colour = G.C.RARITY.cry_epic, + } + ) + return nil, true + else + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { + message = localize("k_nope_ex"), + colour = G.C.RARITY.cry_epic, + } + ) + return nil, true + end + else + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { + message = localize("k_no_other_jokers"), + colour = G.C.RARITY.cry_epic, + } + ) + return nil, true + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local soccer = { + object_type = "Joker", + name = "cry-soccer", + key = "soccer", + pos = { x = 1, y = 4 }, + config = { extra = { holygrail = 1 } }, + immutable = true, + rarity = "cry_epic", + order = 58, + cost = 20, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.holygrail } } + end, + add_to_deck = function(self, card, from_debuff) --TODO: Card in booster packs, Voucher slots + card.ability.extra.holygrail = math.floor(card.ability.extra.holygrail) + G.jokers.config.card_limit = G.jokers.config.card_limit + card.ability.extra.holygrail + G.consumeables.config.card_limit = G.consumeables.config.card_limit + card.ability.extra.holygrail + G.hand:change_size(card.ability.extra.holygrail) + if not G.GAME.modifiers.cry_booster_packs then + G.GAME.modifiers.cry_booster_packs = 2 + end + G.GAME.modifiers.cry_booster_packs = G.GAME.modifiers.cry_booster_packs + card.ability.extra.holygrail + change_shop_size(card.ability.extra.holygrail) + end, + remove_from_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit - card.ability.extra.holygrail + G.consumeables.config.card_limit = G.consumeables.config.card_limit - card.ability.extra.holygrail + G.hand:change_size(-card.ability.extra.holygrail) + if not G.GAME.modifiers.cry_booster_packs then + G.GAME.modifiers.cry_booster_packs = 2 + end + G.GAME.modifiers.cry_booster_packs = G.GAME.modifiers.cry_booster_packs - card.ability.extra.holygrail + change_shop_size(card.ability.extra.holygrail * -1) + end, + cry_credits = { + idea = { + "Mjiojio" + }, + art = { + "HexaCryonic" + }, + code = { + "Jevonn" + } + }, +} +local fleshpanopticon = { + object_type = "Joker", + name = "cry-fleshpanopticon", + key = "fleshpanopticon", + pos = { x = 0, y = 5 }, + config = { extra = { boss_size = 20 } }, + immutable = true, + rarity = "cry_epic", + cost = 15, + order = 146, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + if Cryptid.enabled["Exotic Jokers"] then + info_queue[#info_queue + 1] = { set = "Spectral", key = "c_cry_gateway" } + end + if not center.edition or (center.edition and not center.edition.negative) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_negative + end + return { vars = { center.ability.extra.boss_size } } + end, + calculate = function(self, card, context) + if context.setting_blind and not context.blueprint and context.blind.boss and not card.getting_sliced then + local eval = function(card) return not card.REMOVED and not G.RESET_JIGGLES end + juice_card_until(card, eval, true) + card.gone = false + G.GAME.blind.chips = G.GAME.blind.chips * card.ability.extra.boss_size + G.GAME.blind.chip_text = number_format(G.GAME.blind.chips) + G.HUD_blind:recalculate(true) + G.E_MANAGER:add_event(Event({func = function() + G.E_MANAGER:add_event(Event({func = function() + play_sound('timpani') + delay(0.4) + return true end })) + card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize("cry_good_luck_ex")}) + return true end })) + end + if context.end_of_round and not context.individual and not context.repetition and not context.blueprint and G.GAME.blind.boss and not card.gone then + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local card = create_card(nil,G.consumeables, nil, nil, nil, nil, Cryptid.enabled["Exotic Jokers"] and 'c_cry_gateway' or 'c_soul', 'sup') + card:set_edition({negative = true}, true) + card:add_to_deck() + G.consumeables:emplace(card) + return true + end)})) + if not card.ability.eternal then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true; end})) + return true + end + })) + end + card.gone = true + end + end, + cry_credits = { + idea = { + "notmario" + }, + art = { + "notmario" + }, + code = { + "notmario" + } + }, +} +return { + name = "Epic Jokers", + init = function() + --Error Patches + cry_error_operators = { "+", "-", "X", "/", "^", "=", ">", "<", "m" } + cry_error_numbers = { + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "69", + "404", + "420", + "-1", + "0.5", + "m", + "nan", + "inf", + "nil", + "pi", + "1e9", + "???", + } + cry_error_msgs = { + { string = "rand()", colour = G.C.RARITY["cry_exotic"] }, + { string = "m", colour = G.C.UI.TEXT_DARK }, + { string = "Chips", colour = G.C.CHIPS }, + { string = "Mult", colour = G.C.MULT }, + { string = "Jokers", colour = G.C.FILTER }, + { string = "dollars", colour = G.C.FILTER }, + { string = "hands", colour = G.C.FILTER }, + { string = "slots", colour = G.C.FILTER }, + { string = "Antes", colour = G.C.FILTER }, + { string = "ERROR", colour = G.C.UI.TEXT_INACTIVE }, + { string = "Tarots", colour = G.C.SECONDARY_SET.Tarot }, + { string = "Planets", colour = G.C.SECONDARY_SET.Planet }, + { string = "Specls", colour = G.C.SECONDARY_SET.Spectral }, + { string = "%%ERROR", colour = G.C.CRY_ASCENDANT }, --temp string, this will be modified + } + + function predict_pseudoseed(key) + local M = G.GAME.pseudorandom[key] or pseudohash(key .. (G.GAME.pseudorandom.seed or "")) + local m = math.abs(tonumber(string.format("%.13f", (2.134453429141 + M * 1.72431234) % 1))) + return (m + (G.GAME.pseudorandom.hashed_seed or 0)) / 2 + end + + function predict_card_for_shop() + local total_rate = G.GAME.joker_rate + G.GAME.playing_card_rate + for _, v in ipairs(SMODS.ConsumableType.obj_buffer) do + total_rate = total_rate + (G.GAME[v:lower() .. "_rate"] or 0) + end + local polled_rate = pseudorandom(predict_pseudoseed("cdt" .. G.GAME.round_resets.ante)) * total_rate + local check_rate = 0 + -- need to preserve order to leave RNG unchanged + local rates = { + { type = "Joker", val = G.GAME.joker_rate }, + { type = "Tarot", val = G.GAME.tarot_rate }, + { type = "Planet", val = G.GAME.planet_rate }, + { + type = (G.GAME.used_vouchers["v_illusion"] and pseudorandom(predict_pseudoseed("illusion")) > 0.6) + and "Enhanced" + or "Base", + val = G.GAME.playing_card_rate, + }, + { type = "Spectral", val = G.GAME.spectral_rate }, + } + for _, v in ipairs(SMODS.ConsumableType.obj_buffer) do + if not (v == "Tarot" or v == "Planet" or v == "Spectral") then + table.insert(rates, { type = v, val = G.GAME[v:lower() .. "_rate"] }) + end + end + for _, v in ipairs(rates) do + if polled_rate > check_rate and polled_rate <= check_rate + v.val then + local c = create_card(v.type, "ERROR", nil, nil, nil, nil, nil, "sho") + if not c.set then + return v.type:sub(1, 1) .. c.suit:sub(1, 1) .. c.value:sub(1, 2) + else + for i = 1, #G.P_CENTER_POOLS[c.set] do + if G.P_CENTER_POOLS[c.set][i].key == c.key then + return c.set:sub(1, 1) .. i + end + end + end + end + check_rate = check_rate + v.val + end + end + --Number Blocks Patches + local gigo = Game.init_game_object + function Game:init_game_object() + local g = gigo(self) + g.current_round.cry_nb_card = { rank = "Ace" } + return g + end + local rcc = reset_castle_card + function reset_castle_card() + rcc() + G.GAME.current_round.cry_nb_card = { rank = "Ace" } + local valid_castle_cards = {} + for k, v in ipairs(G.playing_cards) do + if v.ability.effect ~= "Stone Card" then + valid_castle_cards[#valid_castle_cards + 1] = v + end + end + if valid_castle_cards[1] then + local castle_card = + pseudorandom_element(valid_castle_cards, pseudoseed("cry_nb" .. G.GAME.round_resets.ante)) + if not G.GAME.current_round.cry_nb_card then + G.GAME.current_round.cry_nb_card = {} + end + G.GAME.current_round.cry_nb_card.rank = castle_card.base.value + G.GAME.current_round.cry_nb_card.id = castle_card.base.id + end + end + + --For Double Scale, modify Green Joker to use one variable + SMODS.Joker:take_ownership("green_joker", { + config = { extra = 1, mult = 0 }, + name = "cry-Green Joker", --will prevent old calculation code from working + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra, center.ability.extra, center.ability.mult } } + end, + calculate = function(self, card, context) + if + context.discard + and not context.blueprint + and context.other_card == context.full_hand[#context.full_hand] + then + local prev_mult = card.ability.mult + card.ability.mult = math.max(0, card.ability.mult - card.ability.extra) + if card.ability.mult ~= prev_mult then + return { + message = localize({ + type = "variable", + key = "a_mult_minus", + vars = { card.ability.extra }, + }), + colour = G.C.RED, + card = card, + } + end + end + if context.cardarea == G.jokers and context.before and not context.blueprint then + card.ability.mult = card.ability.mult + card.ability.extra + return { + card = card, + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.extra } }), + } + end + if context.cardarea == G.jokers and not context.before and not context.after then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.mult } }), + mult_mod = card.ability.mult, + } + end + end, + loc_txt = {}, + }, true) + end, + items = { + supercell, + membershipcardtwo, + googol_play, + sync_catalyst, + negative, + canvas, + error_joker, + M, + m, + boredom, + double_scale, + number_blocks, + oldcandy, + circus, + caramel, + curse_sob, + bonusjoker, + multjoker, + goldjoker, + altgoogol, + soccer, + fleshpanopticon, + }, +} diff --git a/Cryptid/Items/Exotic.lua b/Cryptid/Items/Exotic.lua new file mode 100644 index 0000000..93b142c --- /dev/null +++ b/Cryptid/Items/Exotic.lua @@ -0,0 +1,1394 @@ +--TIP!!! for coding exotics, make sure you know which layer corresponds to which value! +--The Normal pos correponds to the background. use this for the layer that goes all the way in the back! +--The soul_pos = {blahblahblah, extra = {blahblahblah}} correspomds to the other two layers. the value in the extra table is for the layer that goes in the middle, and the other value is the one that goes all the way in the front +local gateway = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Gateway", + key = "gateway", + pos = { x = 0, y = 0 }, + cost = 4, + atlas = "atlasnotjokers", + order = 90, + hidden = true, --default soul_set and soul_rate of 0.3% in spectral packs is used + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + if (#SMODS.find_card("j_jen_saint") + #SMODS.find_card("j_jen_saint_attuned")) <= 0 then + local deletable_jokers = {} + for k, v in pairs(G.jokers.cards) do + if not v.ability.eternal then + deletable_jokers[#deletable_jokers + 1] = v + end + end + local _first_dissolve = nil + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.75, + func = function() + for k, v in pairs(deletable_jokers) do + if v.config.center.rarity == "cry_exotic" then + check_for_unlock({ type = "what_have_you_done" }) + end + v:start_dissolve(nil, _first_dissolve) + _first_dissolve = true + end + return true + end, + })) + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("timpani") + local card = create_card("Joker", G.jokers, nil, "cry_exotic", nil, nil, nil, "cry_gateway") + card:add_to_deck() + G.jokers:emplace(card) + card:juice_up(0.3, 0.5) + return true + end, + })) + delay(0.6) + end, +} +local iterum = { + object_type = "Joker", + name = "cry-Iterum", + key = "iterum", + config = { extra = { x_mult = 2, repetitions = 1 } }, + pos = { x = 0, y = 1 }, + rarity = "cry_exotic", + order = 500, + cost = 50, + blueprint_compat = true, + atlas = "atlasexotic", + soul_pos = { x = 1, y = 1, extra = { x = 2, y = 1 } }, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.x_mult, math.min(40, center.ability.extra.repetitions) } } + end, + calculate = function(self, card, context) + if context.repetition then + if context.cardarea == G.play then + return { + message = localize("k_again_ex"), + repetitions = math.min(40, card.ability.extra.repetitions), + card = card, + } + end + elseif context.individual then + if context.cardarea == G.play then + return { + x_mult = card.ability.extra.x_mult, + colour = G.C.RED, + card = card, + } + end + end + end, + cry_credits = { + idea = {"Math"}, + art = {"Ein13"}, + code = {"Math"} + }, +} +local universum = { + object_type = "Joker", + name = "cry-Universum", + key = "universum", + config = { extra = 2 }, + pos = { x = 3, y = 3 }, + rarity = "cry_exotic", + cost = 50, + order = 501, + blueprint_compat = true, + atlas = "atlasexotic", + soul_pos = { x = 4, y = 3, extra = { x = 5, y = 3 } }, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + calculate = function(self, card, context) + if context.cry_universum then + return { mod = to_big(card.ability.extra) } + end + end, + cry_credits = { + idea = {"Ein13"}, + art = {"Ein13"}, + }, +} +local exponentia = { + object_type = "Joker", + name = "cry-Exponentia", + key = "exponentia", + config = { extra = { Emult = 1, Emult_mod = 0.03 } }, + pos = { x = 0, y = 0 }, + rarity = "cry_exotic", + cost = 50, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasexotic", + order = 503, + soul_pos = { x = 2, y = 0, extra = { x = 1, y = 0 } }, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.Emult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize{type='variable',key='a_powmult',vars={number_format(card.ability.extra.Emult)}}, + Emult_mod = card.ability.extra.Emult, + colour = G.C.DARK_EDITION, + } + end + end, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.Emult_mod, center.ability.extra.Emult } } + end, + cry_credits = { + idea = {"Enemui"}, + art = {"Jevonn"}, + code = {"Math"} + }, +} +local speculo = { + object_type = "Joker", + name = "cry-Speculo", + key = "speculo", + pos = { x = 3, y = 1 }, + rarity = "cry_exotic", + cost = 50, + blueprint_compat = true, + immutable = true, + atlas = "atlasexotic", + order = 504, + soul_pos = { x = 4, y = 1, extra = { x = 5, y = 1 } }, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.negative) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_negative + end + end, + calculate = function(self, card, context) + if context.ending_shop then + local eligibleJokers = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.name ~= card.ability.name and G.jokers.cards[i].ability.set == "Joker" then + eligibleJokers[#eligibleJokers + 1] = G.jokers.cards[i] + end + end + if #eligibleJokers > 0 then + G.E_MANAGER:add_event(Event({ + func = function() + local card = copy_card(pseudorandom_element(eligibleJokers, pseudoseed("cry_speculo")), nil) + card:set_edition({ negative = true }, true) + card:add_to_deck() + G.jokers:emplace(card) + return true + end, + })) + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { message = localize("k_duplicated_ex") } + ) + return nil, true + end + return + end + end, + cry_credits = { + idea = {"Mystic"}, + art = {"Mystic"}, + code = {"Math"} + }, +} +local redeo = { + object_type = "Joker", + name = "cry-Redeo", + key = "redeo", + config = { extra = { ante_reduction = 1, money_req = 10, money_remaining = 0, money_mod = 10 } }, + loc_vars = function(self, info_queue, center) + return { + vars = { + center.ability.extra.ante_reduction, + center.ability.extra.money_req, + center.ability.extra.money_remaining, + center.ability.extra.money_mod, + }, + } + end, + pos = { x = 3, y = 0 }, + immutable = true, + rarity = "cry_exotic", + cost = 50, + order = 506, + atlas = "atlasexotic", + soul_pos = { x = 4, y = 0, extra = { x = 5, y = 0 } }, + calculate = function(self, card, context) + if context.cry_ease_dollars and context.cry_ease_dollars < 0 and not context.blueprint then + card.ability.extra.money_remaining = card.ability.extra.money_remaining - context.cry_ease_dollars + local ante_mod = 0 + while card.ability.extra.money_remaining >= card.ability.extra.money_req do + card.ability.extra.money_remaining = card.ability.extra.money_remaining - card.ability.extra.money_req + card.ability.extra.money_req = card.ability.extra.money_req + card.ability.extra.money_mod + card.ability.extra.money_mod = math.min(1e300, math.ceil(card.ability.extra.money_mod * 1.06)) + ante_mod = ante_mod - card.ability.extra.ante_reduction + end + if ante_mod < 0 then + ease_ante(ante_mod) + end + return nil, true + end + end, + cry_credits = { + idea = {"Enemui"}, + art = {"Jevonn"}, + code = {"Math", "jenwalter666"} + }, +} +local tenebris = { + object_type = "Joker", + name = "cry-Tenebris", + key = "tenebris", + pos = { x = 3, y = 2 }, + soul_pos = { x = 4, y = 2, extra = { x = 5, y = 2 } }, + config = { extra = { slots = 25, money = 25 } }, + rarity = "cry_exotic", + cost = 50, + order = 507, + atlas = "atlasexotic", + calc_dollar_bonus = function(self, card) + return card.ability.extra.money + end, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.slots, center.ability.extra.money } } + end, + add_to_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit + card.ability.extra.slots + end, + remove_from_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit - card.ability.extra.slots + end, + cry_credits = { + idea = {"Gold"}, + art = {"Mystic"}, + code = {"jenwalter666"} + }, +} +local effarcire = { + object_type = "Joker", + name = "cry-Effarcire", + key = "effarcire", + config = {}, + immutable = true, + pos = { x = 0, y = 0 }, + soul_pos = { x = 1, y = 0, extra = { x = 2, y = 0 } }, + cost = 50, + order = 505, + atlas = "effarcire", + rarity = "cry_exotic", + calculate = function(self, card, context) + if not context.blueprint and not context.retrigger_joker then + if context.first_hand_drawn then + G.FUNCS.draw_from_deck_to_hand(#G.deck.cards) + return nil, true + elseif G.hand.config.card_limit < 1 then + G.hand.config.card_limit = 1 + end + end + end, + cry_credits = { + idea = {"Frix"}, + art = {"AlexZGreat"}, + code = {"jenwalter666"} + }, +} +local effarcire_sprite = { + object_type = "Atlas", + key = "effarcire", + path = "goofy.png", + px = 71, + py = 95, +} +local crustulum = { + object_type = "Joker", + name = "cry-crustulum", + key = "crustulum", + config = { extra = { chips = 0, chip_mod = 4 } }, + pos = { x = 0, y = 2 }, + soul_pos = { x = 2, y = 2, extra = { x = 1, y = 2 } }, + rarity = "cry_exotic", + cost = 50, + order = 508, + atlas = "atlasexotic", + blueprint_compat = true, + perishable_compat = false, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.chips, center.ability.extra.chip_mod } } + end, + calculate = function(self, card, context) + if context.reroll_shop and not context.blueprint then + card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + colour = G.C.CHIPS, + } + ) + return nil, true + end + if + context.cardarea == G.jokers + and to_big(card.ability.extra.chips) > to_big(0) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + chip_mod = card.ability.extra.chips, + } + end + end, + add_to_deck = function(self, card, from_debuff) + --This makes the reroll immediately after obtaining free because the game doesn't do that for some reason + G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + 1 + calculate_reroll_cost(true) + end, + remove_from_deck = function(self, card, from_debuff) + calculate_reroll_cost(true) + end, + cry_credits = { + idea = {"AlexZGreat"}, + art = {"lolxddj"}, + code = {"Jevonn"} + }, +} +--todo: make the Emult always prime +local primus = { + object_type = "Joker", + name = "cry-primus", + key = "primus", + config = { extra = { Emult = 1.01, Emult_mod = 0.17 } }, + pos = { x = 0, y = 4 }, + rarity = "cry_exotic", + cost = 53, + order = 510, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasexotic", + soul_pos = { x = 2, y = 4, extra = { x = 1, y = 4 } }, + calculate = function(self, card, context) + local check = true + if context.cardarea == G.jokers and context.before and not context.blueprint then + if context.scoring_hand then + for k, v in ipairs(context.full_hand) do + if + v:get_id() == 4 + or v:get_id() == 6 + or v:get_id() == 8 + or v:get_id() == 9 + or v:get_id() == 10 + or v:get_id() == 11 + or v:get_id() == 12 + or v:get_id() == 13 + then + check = false + end + end + end + if check then + card.ability.extra.Emult = card.ability.extra.Emult + card.ability.extra.Emult_mod + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("k_upgrade_ex"), + colour = G.C.DARK_EDITION, + }), + } + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.Emult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize{type='variable',key='a_powmult',vars={number_format(card.ability.extra.Emult)}}, + Emult_mod = card.ability.extra.Emult, + colour = G.C.DARK_EDITION, + } + end + end, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.Emult_mod, center.ability.extra.Emult } } + end, + cry_credits = { + idea = {"Jevonn"}, + art = {"Jevonn"}, + code = {"Jevonn"} + }, +} +local big_num_whitelist = { + j_ride_the_bus = true, + j_egg = true, + j_runner = true, + j_ice_cream = true, + j_constellation = true, + j_green_joker = true, + j_red_card = true, + j_madness = true, + j_square = true, + j_vampire = true, + j_hologram = true, + j_obelisk = true, + j_turtle_bean = true, + j_lucky_cat = true, + j_flash = true, + j_popcorn = true, + j_trousers = true, + j_ramen = true, + j_castle = true, + j_campfire = true, + j_throwback = true, + j_glass = true, + j_wee = true, + j_hit_the_road = true, + j_caino = true, + j_yorick = true, + j_cry_dropshot = true, + j_cry_wee_fib = true, + j_cry_whip = true, + j_cry_pickle = true, + j_cry_chili_pepper = true, + j_cry_cursor = true, + j_cry_jimball = true, + j_cry_eternalflame = true, + j_cry_fspinner = true, + j_cry_krustytheclown = true, + j_cry_antennastoheaven = true, + j_cry_mondrian = true, + j_cry_spaceglobe = true, + j_cry_m = true, + -- j_cry_bonk = true, + j_cry_exponentia = true, + j_cry_crustulum = true, + j_cry_primus = true, + j_cry_stella_mortis = true, + j_cry_hugem = true, + j_cry_mprime = true, +} +local scalae = { + object_type = "Joker", + name = "cry-Scalae", + key = "Scalae", + pos = { x = 3, y = 4 }, + soul_pos = { x = 5, y = 4, extra = { x = 4, y = 4 } }, + immutable = false, + rarity = "cry_exotic", + cost = 50, + atlas = "atlasexotic", + order = 311, + config = { extra = { scale = 1, scale_mod = 1, shadow_scale = 1, shadow_scale_mod = 1 } }, + --todo: support jokers that scale multiple variables + calculate = function(self, card, context) + --initialize tracking object + card.ability.extra.scale = to_big(card.ability.extra.scale) + card.ability.extra.scale_mod = to_big(card.ability.extra.scale_mod) + card.ability.extra.shadow_scale = to_big(card.ability.extra.shadow_scale) + card.ability.extra.shadow_scale_mod = to_big(card.ability.extra.shadow_scale_mod) + if context.end_of_round and not context.individual and not context.repetition and not context.blueprint then + card.ability.extra.scale = card.ability.extra.scale + card.ability.extra.scale_mod + card.ability.extra.shadow_scale = card.ability.extra.scale + card.ability.extra.scale = card.ability.extra.shadow_scale + card.ability.extra.scale_mod = card.ability.extra.shadow_scale_mod + return { + message = localize("k_upgrade_ex"), + colour = G.C.DARK_EDITION, + } + end + card.ability.extra.scale = card.ability.extra.shadow_scale + card.ability.extra.scale_mod = card.ability.extra.shadow_scale_mod + return + end, + cry_scale_mod = function(self, card, joker, orig_scale_scale, true_base, orig_scale_base, new_scale_base) + if joker.ability.name ~= "cry-Scalae" then + local new_scale = ( + to_big(true_base) + * ( + ( + 1 + ( + (to_big(orig_scale_scale) / to_big(true_base)) ^ ( + to_big(1) / to_big(card.ability.extra.scale) + ) + ) + ) ^ card.ability.extra.scale + ) + ) + if + (new_scale < to_big(1e100)) + or not ( + ( + joker.config + and joker.config.center + and joker.config.center.key + and big_num_whitelist[joker.config.center.key] + ) or (joker.ability and joker.ability.big_num_scaler) + ) + then + if new_scale >= to_big(1e300) then + new_scale = 1e300 + else + new_scale = to_number(new_scale) + end + end + return new_scale + end + end, + loc_vars = function(self, info_queue, card) + return { vars = { number_format(card.ability.extra.scale + 1), number_format(card.ability.extra.scale_mod) } } + end, + cry_credits = { + idea = {"Mathguy"}, + art = {"Mathguy"}, + code = {"Mathguy"} + }, +} +local stella_mortis = { + object_type = "Joker", + name = "cry-Stella Mortis", + key = "stella_mortis", + config = { extra = { Emult = 1, Emult_mod = 0.4 } }, + pos = { x = 3, y = 5 }, + rarity = "cry_exotic", + cost = 50, + order = 502, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasexotic", + soul_pos = { x = 5, y = 5, extra = { x = 4, y = 5 } }, + calculate = function(self, card, context) + if context.ending_shop then + local destructable_planet = {} + local quota = 1 + for i = 1, #G.consumeables.cards do + if + G.consumeables.cards[i].ability.set == "Planet" + and not G.consumeables.cards[i].getting_sliced + and not G.consumeables.cards[i].ability.eternal + then + destructable_planet[#destructable_planet + 1] = G.consumeables.cards[i] + end + end + local planet_to_destroy = #destructable_planet > 0 + and pseudorandom_element(destructable_planet, pseudoseed("stella_mortis")) + or nil + + if planet_to_destroy then + if Incantation then + quota = planet_to_destroy:getEvalQty() + end + planet_to_destroy.getting_sliced = true + card.ability.extra.Emult = card.ability.extra.Emult + card.ability.extra.Emult_mod * quota + G.E_MANAGER:add_event(Event({ + func = function() + (context.blueprint_card or card):juice_up(0.8, 0.8) + planet_to_destroy:start_dissolve({ G.C.RED }, nil, 1.6) + return true + end, + })) + if not (context.blueprint_card or self).getting_sliced then + card_eval_status_text( + (context.blueprint_card or card), + "extra", + nil, + nil, + nil, + { + message = localize{type='variable',key='a_powmult',vars={number_format( + to_big(card.ability.extra.Emult + card.ability.extra.Emult_mod * quota) + )}}, + } + ) + end + return nil, true + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.Emult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize{type='variable',key='a_powmult',vars={number_format(card.ability.extra.Emult)}}, + Emult_mod = card.ability.extra.Emult, + colour = G.C.DARK_EDITION, + } + end + end, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.Emult_mod, center.ability.extra.Emult } } + end, + cry_credits = { + idea = {"SMG9000"}, + art = {"SMG9000"}, + code = {"SMG9000"} + }, +} +local circulus_pistoris = { + object_type = "Joker", + name = "cry-Circulus Pistoris", + key = "circulus_pistoris", + config = { extra = { Emult = math.pi, Echips = math.pi, hands_remaining = 3 } }, + pos = { x = 0, y = 3 }, + rarity = "cry_exotic", + cost = 10 * math.pi, + order = 509, + blueprint_compat = true, + atlas = "atlasexotic", + soul_pos = { x = 2, y = 3, extra = { x = 1, y = 3 } }, + loc_vars = function(self, info_queue, center) + return { + vars = { + center.edition and center.edition.cry_oversat and "tau" or "pi", + center.ability.extra.hands_remaining, + }, + } + end, + calculate = function(self, card, context) + if + context.joker_main + and ( + G.GAME.current_round.hands_left >= card.ability.extra.hands_remaining + and G.GAME.current_round.hands_left < card.ability.extra.hands_remaining + 1 + ) + then + local pi = math.pi + if card.edition and card.edition.cry_oversat then + pi = 2 * pi + end + return { + Echip_mod = pi, + Emult_mod = pi, + message = localize{type='variable',key='a_powmultchips',vars={(card.edition and card.edition.cry_oversat and "tau" or "pi")}}, + colour = { 0.8, 0.45, 0.85, 1 }, --plasma colors + } + end + end, + cry_credits = { + idea = {"SMG9000", "Math"}, --not sure if there's more ppl I'm missing + art = {"HexaCryonic"}, + code = {"SMG9000", "Math"} + }, +} +local aequilibrium = { + object_type = "Joker", + name = "Ace Aequilibrium", --WARNING!!!! if name is changed, the aeqactive function in Cryptid.lua's create_card must also be changed since it checks for this! + key = "equilib", + config = { extra = { jokers = 2, card = nil } }, + rarity = "cry_exotic", + pos = { x = 7, y = 0 }, + soul_pos = { x = 69, y = 0, extra = { x = 8, y = 0 } }, + atlas = "atlasexotic", + cost = 50, + order = 512, + blueprint_compat = true, + immutable = true, + eternal_compat = true, + perishable_compat = true, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.negative) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_negative + end + local joker_generated = "???" + if G.GAME.aequilibriumkey and G.GAME.aequilibriumkey > 1 then + joker_generated = localize({ + type = "name_text", + set = "Joker", + key = G.P_CENTER_POOLS["Joker"][math.floor(G.GAME.aequilibriumkey or 1) - 1].key, + }) + end + return { vars = { center.ability.extra.jokers, joker_generated } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.retrigger_joker then + for i = 1, math.min(200, card.ability.extra.jokers) do + local newcard = create_card("Joker", G.jokers, nil, nil, nil, nil, nil) + newcard:add_to_deck() + G.jokers:emplace(newcard) + newcard:set_edition({ negative = true }, true) + end + return nil, true + end + end, + add_to_deck = function(self, card, from_debuff) + if not from_debuff then + if card.ability.extra.card then + card.ability.extra.card = nil + end + card.ability.extra.card = + Card(G.jokers.T.x, G.jokers.T.y, G.CARD_W * 0.675, G.CARD_H * 0.675, G.P_CARDS.S_A, G.P_CENTERS.c_base) + --G.hand:emplace(card.ability.extra.card) + --card.ability.extra.card:set_card_area(G.hand) + card.ability.extra.card:start_materialize({ G.C.WHITE, G.C.WHITE }, nil, 1.2) + card.ability.extra.card:set_seal("Gold", true, true) + card.ability.extra.card:set_edition({ cry_glitched = true }, true) + --card.ability.extra.card.T.x = card.T.x + + if card.ability.extra.card and G.P_CENTERS.j_blueprint.unlocked then + local viable_unlockables = {} + for k, v in ipairs(G.P_LOCKED) do + if (v.set == "Voucher" or v.set == "Joker") and not v.demo then + viable_unlockables[#viable_unlockables + 1] = v + end + end + if #viable_unlockables > 0 then + local card2 = card.ability.extra.card + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 4.04, + func = function() + card2 = Card( + G.jokers.T.x, + G.jokers.T.y, + G.CARD_W * 0.675, + G.CARD_H * 0.675, + nil, + pseudorandom_element(viable_unlockables) or self.P_CENTERS.j_joker + ) + card2.no_ui = #viable_unlockables == 0 + card2.states.visible = false + card.ability.extra.card.parent = nil + card.ability.extra.card:start_dissolve({ G.C.BLACK, G.C.ORANGE, G.C.RED, G.C.GOLD }) + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 1.04, + func = function() + card2:start_materialize() + --G.:emplace(card) + return true + end, + })) + end + end + end + end, + --Known bug: card does not reappear after save reopened + update = function(self, card, front) + if card.ability.extra.card then + if card.ability.extra.card.states and not card.ability.extra.card.states.drag.is then + card.ability.extra.card.T.x = card.T.x + card.T.w / 5 + card.ability.extra.card.T.y = card.T.y + card.T.h / 5 + end + end + end, + remove_from_deck = function(self, card, from_debuff) + if not from_debuff and card.ability.extra.card then + card.ability.extra.card:start_dissolve() + end + end, + cry_credits = { + idea = {"Elial2"}, + art = {"Elial2"}, + code = {"Elial2"} + }, +} +local cc = copy_card +function copy_card(card, a, b, c, d) + local m + if + card + and card.ability + and card.ability.extra + and type(card.ability.extra) == "table" + and card.ability.extra.card + then + m = card.ability.extra.card + card.ability.extra.card = nil + end + local ret = cc(card, a, b, c, d) + if + card + and card.ability + and card.ability.extra + and type(card.ability.extra) == "table" + and card.ability.extra.card + and m + then + card.ability.extra.card = m + end + return ret +end +local facile = { + object_type = "Joker", + name = "cry-facile", + key = "facile", + config = { extra = { Emult = 3, check = 10, check2 = 0 } }, + pos = { x = 6, y = 2 }, + soul_pos = { x = 8, y = 2, extra = { x = 7, y = 2 } }, + rarity = "cry_exotic", + cost = 50, + order = 513, + blueprint_compat = true, + atlas = "atlasexotic", + loc_vars = function(self, info_queue, center) + return { + vars = { center.ability.extra.Emult, center.ability.extra.check }, + } + end, + calculate = function(self, card, context) + if context.individual then + if context.cardarea == G.play then + card.ability.extra.check2 = card.ability.extra.check2 + 1 + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.Emult) > to_big(1)) + and not context.before + and not context.after + then + if card.ability.extra.check2 <= card.ability.extra.check then + card.ability.extra.check2 = 0 + return { + message = localize{type='variable',key='a_powmult',vars={number_format(card.ability.extra.Emult)}}, + Emult_mod = card.ability.extra.Emult, + colour = G.C.DARK_EDITION, + } + else + card.ability.extra.check2 = 0 + end + end + end, + cry_credits = { + idea = {"Enemui"}, + art = {"Kailen"}, + code = {"Jevonn"} + }, +} +local gemino = { + object_type = "Joker", + name = "cry-Gemino", + key = "gemino", + pos = { x = 6, y = 1 }, + soul_pos = { x = 8, y = 1, extra = { x = 7, y = 1 } }, + immutable = true, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Requiacity", + }, + art = {"Requiacity"}, + code = {"Math"} + }, + rarity = "cry_exotic", + blueprint_compat = true, + cost = 50, + order = 515, + atlas = "atlasexotic", + calculate = function(self, card2, context) + if context.end_of_round and not context.repetition and not context.individual then + local check = false + local card = G.jokers.cards[1] + if not Card.no(G.jokers.cards[1], "immutable", true) then + cry_with_deck_effects(G.jokers.cards[1], function(card) + cry_misprintize(card, { min = 2, max = 2 }, nil, true) + end) + check = true + end + if check then + card_eval_status_text( + context.blueprint_card or card2, + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex"), colour = G.C.GREEN } + ) + end + return nil, true + end + end, +} + +local energia = { + object_type = "Joker", + name = "cry-Energia", + key = "energia", + pos = { x = 6, y = 3 }, + soul_pos = { x = 8, y = 3, extra = { x = 7, y = 3 } }, + blueprint_compat = false, + perishable_compat = false, + order = 514, + config = { extra = { tags = 1, tag_mod = 1 } }, + loc_vars = function(self, info_queue, center) + return { + vars = { math.min(20, center.ability.extra.tags), center.ability.extra.tag_mod }, + } + end, + rarity = "cry_exotic", + cost = 50, + atlas = "atlasexotic", + calculate = function(self, card, context) + if context.cry_add_tag then + local t = math.min(20, card.ability.extra.tags) + card.ability.extra.tags = card.ability.extra.tags + card.ability.extra.tag_mod + if card.ability.extra.tags < 20 then + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex"), colour = G.C.DARK_EDITION } + ) + end + return { tags = t } + end + end, + cry_credits = { + idea = {"jenwalter666"}, + art = {"Kailen"}, + code = {"Math"} + }, +} + +--why is this an exotic??? +local verisimile = { + object_type = "Joker", + name = "cry-verisimile", + key = "verisimile", + pos = { x = 0, y = 1 }, + soul_pos = { x = 1, y = 1, extra = { x = 2, y = 1 } }, + config = { extra = { xmult = 1 } }, + rarity = "cry_exotic", + cost = 50, + order = 516, + immutable = true, + blueprint_compat = true, + atlas = "placeholders", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.xmult } } + end, + calculate = function(self, card, context) + if context.post_trigger and not context.blueprint then + --Todo: Gros Michel, Cavendish, Planet.lua + --Bus driver is ignored because it always triggers anyway + if context.other_joker.ability.name == "8 Ball" + or context.other_joker.ability.name == "Space Joker" + or context.other_joker.ability.name == "Business Card" + or context.other_joker.ability.name == "Hallucination" then + local variable = context.other_joker + card.ability.extra.xmult = card.ability.extra.xmult + variable.ability.extra + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.xmult } }) } + ) + elseif + context.other_joker.ability.name == "Reserved Parking" + or context.other_joker.ability.name == "Bloodstone" + or context.other_joker.ability.name == "cry-Googol Play Card" + or context.other_joker.ability.name == "cry-Boredom" + or context.other_joker.ability.name == "cry-bonusjoker" + or context.other_joker.ability.name == "cry-multjoker" + or context.other_joker.ability.name == "cry-scrabble" + then + local variable = context.other_joker + card.ability.extra.xmult = card.ability.extra.xmult + variable.ability.extra.odds + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.xmult } }) } + ) + elseif context.other_joker.ability.name == "cry-notebook" then + --This also triggers at notebook's end of round which isn't intentional but i'm not bothered enough about this to find a workaround + local variable = context.other_joker + card.ability.extra.xmult = card.ability.extra.xmult + variable.ability.extra.odds + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.xmult } }) } + ) + end + return nil, true + elseif context.consumeable and not context.blueprint then + if context.consumeable.ability.name == "The Wheel of Fortune" and context.consumeable.cry_wheel_success then + local variable = context.consumeable + card.ability.extra.xmult = card.ability.extra.xmult + variable.ability.extra --Doesn't account for misprintizing for some reason + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.xmult } }) } + ) + end + elseif + context.cardarea == G.jokers + and (to_big(card.ability.extra.xmult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.xmult } }), + Xmult_mod = card.ability.extra.xmult, + } + end + end, + cry_credits = { + idea = {"Enemui"}, + code = {"Jevonn"} + }, +} + +local duplicare = { + object_type = "Joker", + name = "cry-duplicare", + key = "duplicare", + config = {extra = {Xmult = 1, Xmult_mod = 1}}, + pos = { x = 0, y = 6 }, + soul_pos = { x = 2, y = 6, extra = { x = 1, y = 6 } }, + rarity = "cry_exotic", + cost = 50, + order = 517, + blueprint_compat = true, + atlas = "atlasexotic", + loc_vars = function(self, info_queue, center) + return { + vars = {center.ability.extra.Xmult, center.ability.extra.Xmult_mod} + } + end, + calculate = function(self, card, context) + if not context.blueprint and ((context.post_trigger and context.other_joker ~= card) or (context.individual and context.cardarea == G.play)) then + card.ability.extra.Xmult = card.ability.extra.Xmult + card.ability.extra.Xmult_mod + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_upgrade_ex") }) + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.Xmult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize{type='variable',key='a_xmult',vars={number_format(card.ability.extra.Xmult)}}, + Xmult_mod = card.ability.extra.Xmult, + colour = G.C.MULT, + } + end + end, + cry_credits = { + idea = {"Enemui"}, + art = {"Shellular"}, + code = {"elial2"} + }, +} + +-- to be honest, this needs a refactor because +-- rescribed jokers are forgotten on save reload +-- they are not saved in a good way right now +-- status text is not handled properly +local rescribere = { + object_type = "Joker", + name = "cry-Rescribere", + key = "rescribere", + pos = { x = 0, y = 1 }, + soul_pos = { x = 1, y = 1, extra = { x = 2, y = 1 } }, + blueprint_compat = false, + perishable_compat = false, + rarity = "cry_exotic", + cost = 50, + order = 69420, + atlas = "placeholders", + calculate = function(self, card, context) + local eligibleJokers = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.name ~= card.ability.name then eligibleJokers[#eligibleJokers+1] = G.jokers.cards[i] end + end + + for i = 1, #eligibleJokers do + if context.selling_card and context.card.ability.name ~= card.ability.name and context.card ~= eligibleJokers[i] then + local oldfunc = eligibleJokers[i].calculate_joker + + + eligibleJokers[i].ability.rescribere_jokers = eligibleJokers[i].ability.rescribere_jokers or {} + eligibleJokers[i].ability.rescribere_jokers[#eligibleJokers[i].ability.rescribere_jokers+1] = context.card + + + eligibleJokers[i].calculate_joker = function(cardd,contextt) + local totalret = oldfunc(cardd,contextt) + + v = eligibleJokers[i].ability.rescribere_jokers[#eligibleJokers[i].ability.rescribere_jokers] + + local ret = v:calculate_joker(contextt) + if ret and type(ret) == 'table' then + totalret = totalret or {message = "Copying", card = eligibleJokers[i]} + for _i,_v in pairs(ret) do + if not totalret[_i] then + totalret[_i] = ret[_i] or _v + --print(totalret[_i] .. "--------------") + else + if type(totalret[_i]) == 'number' then + totalret[_i] = totalret[_i] + ret[_i] + end + end + end + totalret.card = eligibleJokers[i] + end + return totalret + + end + end + end + end +} + +local formidiulosus = { + object_type = "Joker", + name = "cry-Formidiulosus", + key = "formidiulosus", + pos = { x = 6, y = 4 }, + soul_pos = { x = 8, y = 4, extra = { x = 7, y = 4 } }, + blueprint_compat = true, + config = { extra = { candy = 3, Emult_mod = 0.01, Emult = 1 } }, + loc_vars = function(self, info_queue, center) + return { + vars = { 3, center.ability.extra.Emult_mod, center.ability.extra.Emult }, + } + end, + rarity = "cry_exotic", + cost = 50, + order = 518, + atlas = "atlasexotic", + no_dbl = true, + update = function(self, card, front) + local value = 0 + if G.jokers then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].config.center.rarity == "cry_candy" then + value = value + 1 + end + end + end + card.ability.extra.Emult = 1 + (card.ability.extra.Emult_mod * value) + end, + calculate = function(self, card, context) + if (context.buying_card or context.cry_creating_card) and context.card.ability.set == "Joker" and context.card.config.center.rarity == "cry_cursed" and not context.blueprint and not (context.card == card) then + G.E_MANAGER:add_event(Event({ + func = function() + context.card:start_dissolve() + card_eval_status_text(card, 'extra', nil, nil, nil, { + message = localize("k_nope_ex"), + colour = G.C.BLACK, + }) + return true + end + })) + end + if context.ending_shop then + for i = 1, 3 do + local card = create_card("Joker", G.jokers, nil, "cry_candy", nil, nil, nil, "cry_trick_candy") + card:set_edition({ negative = true }, true) + card:add_to_deck() + G.jokers:emplace(card) + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.Emult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize{type='variable',key='a_powmult',vars={number_format(card.ability.extra.Emult)}}, + Emult_mod = card.ability.extra.Emult, + colour = G.C.DARK_EDITION, + } + end + end, + cry_credits = { + idea = {"HexaCryonic","Kailen"}, + art = {"Foegro"}, + code = {"Foegro"} + }, +} +local items = { + gateway, + iterum, + universum, + exponentia, + speculo, + redeo, + tenebris, + effarcire, + effarcire_sprite, + crustulum, + primus, + scalae, + stella_mortis, + circulus_pistoris, + aequilibrium, + facile, + gemino, + energia, + --verisimile, WHY IS THIS AN EXOTIC???????????????????? + --rescribere, [NEEDS REFACTOR] + duplicare, +} +if Cryptid.enabled["Spooky"] then + items[#items + 1] = formidiulosus +end +return { + name = "Exotic Jokers", + init = function() + --Universum Patches + local uht = update_hand_text + function update_hand_text(config, vals) + if next(find_joker("cry-Universum")) and not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ --This is the Hand name text for the poker hand + trigger = "before", + blockable = not config.immediate, + delay = config.delay or 0.8, + func = function() + local col = G.C.GREEN + if vals.chips and G.GAME.current_round.current_hand.chips ~= vals.chips then + local delta = vals.chips + if is_number(vals.chips) and is_number(G.GAME.current_round.current_hand.chips) then + delta = "X" .. number_format(vals.chips / G.GAME.current_round.current_hand.chips) + end + G.GAME.current_round.current_hand.chips = vals.chips + G.hand_text_area.chips:update(0) + if vals.StatusText then + attention_text({ + text = delta, + scale = 0.8, + hold = 1, + cover = G.hand_text_area.chips.parent, + cover_colour = mix_colours(G.C.CHIPS, col, 0.1), + emboss = 0.05, + align = "cm", + cover_align = "cr", + }) + end + end + if vals.mult and G.GAME.current_round.current_hand.mult ~= vals.mult then + local delta = vals.mult + if is_number(vals.mult) and is_number(G.GAME.current_round.current_hand.mult) then + delta = "X" .. number_format(vals.mult / G.GAME.current_round.current_hand.mult) + end + G.GAME.current_round.current_hand.mult = vals.mult + G.hand_text_area.mult:update(0) + if vals.StatusText then + attention_text({ + text = delta, + scale = 0.8, + hold = 1, + cover = G.hand_text_area.mult.parent, + cover_colour = mix_colours(G.C.MULT, col, 0.1), + emboss = 0.05, + align = "cm", + cover_align = "cl", + }) + end + if not G.TAROT_INTERRUPT then + G.hand_text_area.mult:juice_up() + end + end + if vals.handname and G.GAME.current_round.current_hand.handname ~= vals.handname then + G.GAME.current_round.current_hand.handname = vals.handname + if not config.nopulse then + G.hand_text_area.handname.config.object:pulse(0.2) + end + end + if vals.chip_total then + G.GAME.current_round.current_hand.chip_total = vals.chip_total + G.hand_text_area.chip_total.config.object:pulse(0.5) + end + if + vals.level + and G.GAME.current_round.current_hand.hand_level + ~= " " .. localize("k_lvl") .. tostring(vals.level) + then + if vals.level == "" then + G.GAME.current_round.current_hand.hand_level = vals.level + else + G.GAME.current_round.current_hand.hand_level = " " + .. localize("k_lvl") + .. tostring(vals.level) + if is_number(vals.level) then + G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[math.min(vals.level, 7)] + else + G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[1] + end + G.hand_text_area.hand_level:juice_up() + end + end + if config.sound and not config.modded then + play_sound(config.sound, config.pitch or 1, config.volume or 1) + end + if config.modded then + if + G.HUD_blind + and G.HUD_blind.get_UIE_by_ID + and G.HUD_blind:get_UIE_by_ID("HUD_blind_debuff_1") + and G.HUD_blind:get_UIE_by_ID("HUD_blind_debuff_2") + then + G.HUD_blind:get_UIE_by_ID("HUD_blind_debuff_1"):juice_up(0.3, 0) + G.HUD_blind:get_UIE_by_ID("HUD_blind_debuff_2"):juice_up(0.3, 0) + end + G.GAME.blind:juice_up() + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.06 * G.SETTINGS.GAMESPEED, + blockable = false, + blocking = false, + func = function() + play_sound("tarot2", 0.76, 0.4) + return true + end, + })) + play_sound("tarot2", 1, 0.4) + end + return true + end, + })) + else + uht(config, vals) + end + end + + --Redeo Patches + local ed = ease_dollars + function ease_dollars(mod, x) + ed(mod, x) + for i = 1, #G.jokers.cards do + local effects = G.jokers.cards[i]:calculate_joker({ cry_ease_dollars = mod }) + end + end + end, + items = items, +} diff --git a/Cryptid/Items/M.lua b/Cryptid/Items/M.lua new file mode 100644 index 0000000..b91a44b --- /dev/null +++ b/Cryptid/Items/M.lua @@ -0,0 +1,1492 @@ +local jollysus = { + object_type = "Joker", + name = "cry-jollysus Joker", + key = "jollysus", + effect = "M Joker", + pos = { x = 3, y = 1 }, + config = { extra = { spawn = true, active = localize("k_active_ex") } }, + rarity = 1, + cost = 4, + order = 267, + blueprint_compat = true, + eternal_compat = false, + immutable = true, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_m + return { vars = { center.ability.extra.active } } + end, + atlas = "atlastwo", + calculate = function(self, card, context) + if context.end_of_round and not context.retrigger_joker and not context.blueprint then + if not card.ability.extra.spawn then + card.ability.extra.active = localize("k_active_ex") + card.ability.extra.spawn = true + return { + message = localize("k_reset"), + card = card, + } + end + end + if context.selling_card and card.ability.extra.spawn and not context.retrigger_joker then + if context.card.ability.set == "Joker" then + if not context.blueprint and not context.retrigger_joker then + card.ability.extra.active = localize("cry_no_triggers") + card.ability.extra.spawn = false + end + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, nil, "jollysus") + card:set_edition({ cry_m = true }) + card:add_to_deck() + G.jokers:emplace(card) + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("cry_m_ex"), + colour = G.C.FILTER, + card = card, + }), + } + end + elseif context.selling_self and card.ability.extra.spawn and not context.retrigger_joker then + if not context.blueprint and not context.retrigger_joker then + card.ability.extra.active = localize("cry_no_triggers") + card.ability.extra.spawn = false + end + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, nil, "jollysus") + card:set_edition({ cry_m = true }) + card:add_to_deck() + G.jokers:emplace(card) + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("cry_m_ex"), + colour = G.C.FILTER, + card = card, + }), + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +--TODO +--Fix Incompatiblity with Brainstorm (the joker not the mod) +--Make Blueprints create copies when this is sold to the right of Blueprint +local bubblem = { + object_type = "Joker", + name = "cry-bubblem", + key = "bubblem", + effect = "M Joker", + order = 251, + pos = { x = 0, y = 0 }, + config = { extra = { spawn = false, type = "Three of a Kind" }, jolly = { t_mult = 8, type = "Pair" } }, + rarity = 1, + cost = 2, + eternal_compat = false, + immutable = true, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + if not center.edition or (center.edition and not center.edition.foil) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_foil + end + return { vars = { localize(center.ability.extra.type, "poker_hands") } } + end, + atlas = "atlasone", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and context.before + and next(context.poker_hands[card.ability.extra.type]) + and not context.blueprint + and not context.retrigger_joker + then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(self) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") + card:set_edition({ + foil = true, + }) + card:add_to_deck() + G.jokers:emplace(card) + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("cry_m_ex"), + colour = G.C.FILTER, + }), + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local foodm = { + object_type = "Joker", + name = "cry-foodm", + key = "foodm", + effect = "M Joker", + config = { + extra = { mult = 40, rounds_remaining = 2, round_inc = 1 }, + jolly = { t_mult = 8, type = "Pair" }, + }, + pos = { x = 4, y = 2 }, + rarity = 1, + order = 252, + cost = 5, + atlas = "atlasone", + blueprint_compat = true, + eternal_compat = false, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { + vars = { + center.ability.extra.mult, + center.ability.extra.rounds_remaining, + center.ability.extra.round_inc, + }, + } + end, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (card.ability.extra.mult > 0) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.extra.mult } }), + mult_mod = card.ability.extra.mult, + colour = G.C.MULT, + } + end + if + context.end_of_round + and not context.blueprint + and not context.individual + and not context.repetition + and not context.retrigger_joker + then + card.ability.extra.rounds_remaining = card.ability.extra.rounds_remaining - 1 + if card.ability.extra.rounds_remaining > 0 then + return { + message = { localize("cry_minus_round") }, + colour = G.C.FILTER, + } + else + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("cry_m_ex"), + } + end + end + if + context.selling_card + and not context.blueprint + and not context.retrigger_joker + and context.card:is_jolly() + then + card.ability.extra.rounds_remaining = card.ability.extra.rounds_remaining + card.ability.extra.round_inc + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize{type='variable',key='a_round',vars={card.ability.extra.round_inc}}, + colour = G.C.FILTER, + }), + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local mstack = { + object_type = "Joker", + name = "cry-mstack", + key = "mstack", + effect = "M Joker", + order = 253, + config = { extra = { sell = 0, sell_req = 3, retriggers = 1, check = false }, jolly = { t_mult = 8, type = "Pair" } }, + pos = { x = 2, y = 3 }, + atlas = "atlastwo", + rarity = 3, + cost = 7, + blueprint_compat = true, + perishable_compat = false, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { vars = { center.ability.extra.retriggers, center.ability.extra.sell_req, center.ability.extra.sell } } + end, + calculate = function(self, card, context) --note: hardcoded like this intentionally + if context.repetition then + if context.cardarea == G.play then + return { + message = localize("k_again_ex"), + repetitions = card.ability.extra.retriggers, + card = card, + } + end + end + + if + context.selling_card + and context.card:is_jolly() + and not context.blueprint + and not context.retrigger_joker + then + card.ability.extra.check = true + if card.ability.extra.sell + 1 >= card.ability.extra.sell_req then + if not context.blueprint or context.retrigger_joker then + card.ability.extra.retriggers = card.ability.extra.retriggers + 1 + end + card.ability.extra.sell = 0 + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("k_upgrade_ex"), + colour = G.C.FILTER, + }), + } + else + card.ability.extra.sell = card.ability.extra.sell + 1 + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = card.ability.extra.sell .. "/" .. card.ability.extra.sell_req, + colour = G.C.FILTER, + }), + } + end + end + end, + add_to_deck = function(self, card, from_debuff) --Force retriggers to be 1 when bought/obtained on misprint deck (no 0.43 retriggers that do nothing) + card.ability.extra.retriggers = math.floor(card.ability.extra.retriggers) + if card.ability.extra.retriggers < 1 and not card.ability.extra.check then + card.ability.extra.retriggers = 1 + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local mneon = { + object_type = "Joker", + name = "cry-mneon", + key = "mneon", + effect = "M Joker", + pos = { x = 4, y = 2 }, + order = 254, + config = { extra = { bonus = 1, money = 0 }, jolly = { t_mult = 8, type = "Pair" } }, + rarity = 2, + cost = 7, + perishable_compat = false, + blueprint_compat = false, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { vars = { center.ability.extra.bonus, center.ability.extra.money } } + end, + atlas = "atlastwo", + calculate = function(self, card, context) + if context.end_of_round and not context.blueprint and not context.individual and not context.repetition then + local jollycount = 0 + for i = 1, #G.jokers.cards do + if + G.jokers.cards[i]:is_jolly() + or G.jokers.cards[i].ability.effect == "M Joker" + then + jollycount = jollycount + 1 + end + end + card.ability.extra.money = card.ability.extra.money + math.max(1, card.ability.extra.bonus) * (jollycount or 1) + return { message = localize("cry_m_ex") } + end + end, + calc_dollar_bonus = function(self, card) + if card.ability.extra.money > 0 then + return card.ability.extra.money + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local notebook = { + object_type = "Joker", + name = "cry-notebook", + key = "notebook", + effect = "M Joker", + pos = { x = 1, y = 0 }, + order = 255, + config = { + extra = { odds = 7, slot = 0, jollies = 4, check = true, active = "Active", inactive = "" }, + jolly = { t_mult = 8, type = "Pair" }, + }, + immutable = true, + rarity = 3, + cost = 9, + perishable_compat = false, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { + vars = { + "" .. (G.GAME and G.GAME.probabilities.normal or 1), + center.ability.extra.odds, + center.ability.extra.slot, + center.ability.extra.active, + center.ability.extra.jollies, + }, + } + end, + atlas = "atlasone", + calculate = function(self, card, context) + if + context.reroll_shop + and card.ability.extra.check + and not context.blueprint + and not context.retrigger_joker + then + local jollycount = 0 + for i = 1, #G.jokers.cards do + if + G.jokers.cards[i]:is_jolly() + then + jollycount = jollycount + 1 + end + end + if + jollycount >= card.ability.extra.jollies --if there are 5 or more jolly jokers + or pseudorandom("cry_notebook") < G.GAME.probabilities.normal / card.ability.extra.odds + then + card.ability.extra.slot = card.ability.extra.slot + 1 + G.jokers.config.card_limit = G.jokers.config.card_limit + 1 + card.ability.extra.check = false + card.ability.extra.active = localize("cry_inactive") + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("k_upgrade_ex"), + colour = G.C.DARK_EDITION, + }), + } + end + end + if context.end_of_round and not context.retrigger_joker and not context.blueprint then + if not card.ability.extra.check then + card.ability.extra.check = true + card.ability.extra.active = localize("cry_active") + return { + message = localize("k_reset"), + card = card, + } + end + end + end, + add_to_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit + card.ability.extra.slot + end, + remove_from_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit - card.ability.extra.slot + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local bonk = { + object_type = "Joker", + name = "cry-bonk", + key = "bonk", + effect = "M Joker", + order = 256, + pos = { x = 2, y = 2 }, + config = { extra = { chips = 6, bonus = 1, xchips = 3, type = "Pair" }, jolly = { t_mult = 8, type = "Pair" } }, + pools = {["Meme"] = true}, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { + vars = { + center.ability.extra.chips, + center.ability.extra.bonus, + localize(center.ability.extra.type, "poker_hands"), + (center.ability.extra.chips * center.ability.extra.xchips), + }, + } + end, + rarity = 2, + cost = 5, + blueprint_compat = true, + atlas = "atlasone", + perishable_compat = false, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.blueprint then + if context.scoring_name == card.ability.extra.type then + card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.bonus + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("k_upgrade_ex"), + colour = G.C.CHIPS, + }) + return nil, true + end + end + if context.other_joker and context.other_joker.ability.set == "Joker" then + if + context.other_joker:is_jolly() + then + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ + type = "variable", + key = "a_chips", + vars = { card.ability.extra.chips * card.ability.extra.xchips }, + }), + chip_mod = card.ability.extra.chips * card.ability.extra.xchips, + } + else + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + chip_mod = card.ability.extra.chips, + } + end + end + end, + add_to_deck = function(self, card, from_debuff) + card.ability.extra.xchips = math.floor(card.ability.extra.xchips + 0.5) --lua moment + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local loopy = { + object_type = "Joker", + name = "cry-loopy", + key = "loopy", + effect = "M Joker", + config = { extra = { retrigger = 0}, jolly = { t_mult = 8, type = "Pair" } }, + pos = { x = 4, y = 1 }, + order = 257, + atlas = "atlastwo", + immutable = true, + rarity = 1, + cost = 4, + joker_gate = "Jolly Joker", + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { vars = { center.ability.extra.retrigger } } + end, + calculate = function(self, card, context) + if + context.selling_card + and context.card:is_jolly() + and not context.blueprint + and not context.retrigger_joker + then + card.ability.extra.retrigger = card.ability.extra.retrigger + 1 + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("cry_m_ex"), + colour = G.C.GREEN, + }), + } + end + if + context.retrigger_joker_check + and not context.retrigger_joker + and context.other_card ~= self + and card.ability.extra.retrigger ~= 0 + then + return { + message = localize("k_again_ex"), + colour = G.C.GREEN, + repetitions = card.ability.extra.retrigger, + card = card, + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local scrabble = { + object_type = "Joker", + name = "cry-scrabble", + key = "scrabble", + effect = "M Joker", + config = { extra = { odds = 4 } }, + pos = { x = 0, y = 2 }, + order = 258, + rarity = 2, + cost = 8, + blueprint_compat = true, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_m + return { vars = { "" .. (G.GAME and G.GAME.probabilities.normal or 1), center.ability.extra.odds } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.retrigger_joker then + local check = false + --if pseudorandom('scrabble') < G.GAME.probabilities.normal/card.ability.extra.odds then + --check = true + --local card = create_card('Joker', G.jokers, nil, nil, nil, nil, 'j_jolly') + --card:add_to_deck() + --G.jokers:emplace(card) + --end + if pseudorandom("scrabbleother") < G.GAME.probabilities.normal / card.ability.extra.odds then + check = true + local card = create_card("Joker", G.jokers, nil, 0.9, nil, nil, nil, "scrabbletile") + card:set_edition({ cry_m = true }) + card:add_to_deck() + G.jokers:emplace(card) + end + if check then + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("cry_m_ex"), colour = G.C.DARK_EDITION }) + return nil, true + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local sacrifice = { + object_type = "Joker", + name = "cry-sacrifice", + key = "sacrifice", + effect = "M Joker", + config = { extra = { text = localize("k_active_ex"), spawn = true }, jolly = { t_mult = 8, type = "Pair" } }, + pos = { x = 5, y = 2 }, + order = 259, + immutable = true, + rarity = 1, + cost = 4, + blueprint_compat = true, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { vars = { center.ability.extra.text } } + end, + calculate = function(self, card, context) + if context.using_consumeable and card.ability.extra.spawn and not context.retrigger_joker then + if context.consumeable.ability.set == "Spectral" then + if not context.blueprint then + card.ability.extra.spawn = false + end + if not card.ability.extra.spawn then + card.ability.extra.text = localize("cry_no_triggers") + end + for i = 1, 3 do + local jolly = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") + jolly:add_to_deck() + G.jokers:emplace(jolly) + end + local card = create_card("Joker", G.jokers, nil, 0.9, nil, nil, nil, "sacrifice") + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("cry_m_ex"), colour = G.C.SPECTRAL }) + return nil, true + end + end + if context.end_of_round and not context.retrigger_joker and not context.blueprint then + if not card.ability.extra.spawn then + card.ability.extra.spawn = true + card.ability.extra.text = localize("k_active_ex") + return { + message = localize("k_reset"), + card = card, + } + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +--TODO: Fix Brainstorm incompatibility (the joker not the mod) +local reverse = { + object_type = "Joker", + name = "cry-reverse", + key = "reverse", + effect = "M Joker", + config = { extra = { type = "Pair", spawn = 0 }, jolly = { t_mult = 8, type = "Pair" } }, + pools = {["Meme"] = true}, + pos = { x = 0, y = 0 }, + rarity = 2, + order = 260, + cost = 4, + eternal_compat = false, + immutable = true, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + if not center.edition or (center.edition and not center.edition.holo) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_holo + end + return { vars = { localize(center.ability.extra.type, "poker_hands") } } + end, + calculate = function(self, card, context) + if context.pre_discard and not context.retrigger_joker and not context.blueprint then + if + G.FUNCS.get_poker_hand_info(G.hand.highlighted) == card.ability.extra.type + and #G.jokers.cards + G.GAME.joker_buffer <= G.jokers.config.card_limit + then + G.E_MANAGER:add_event(Event({ --self destruct + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(self) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + local spawnamount = math.min(100, G.jokers.config.card_limit - (#G.jokers.cards + G.GAME.joker_buffer)) + + 1 -- +1 to account for reverse card self destruct + G.GAME.joker_buffer = G.GAME.joker_buffer + spawnamount + G.E_MANAGER:add_event(Event({ + func = function() + for i = 1, spawnamount do + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") + card:set_edition({ + holo = true, --certified Thunk moment, this literally took ten minutes to figure out why is it set up like this + }) + card:add_to_deck() + G.jokers:emplace(card) + G.GAME.joker_buffer = 0 + end + return true + end, + })) + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("cry_m_ex"), colour = G.C.DARK_EDITION }) + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local doodlem = { + object_type = "Joker", + name = "cry-doodlem", + key = "doodlem", + atlas = "atlasepic", + effect = "M Joker", + config = { jolly = { t_mult = 8, type = "Pair" } }, + pos = { x = 2, y = 0 }, + immutable = true, + rarity = "cry_epic", + cost = 13, + order = 266, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { key = "e_negative_consumable", set = "Edition", config = { extra = 1 } } + end, + calculate = function(self, card, context) + if context.setting_blind and not (context.blueprint_card or self).getting_sliced then + local jollycount = 2 + for i = 1, #G.jokers.cards do + if + G.jokers.cards[i]:is_jolly() + then + jollycount = jollycount + 1 + end + end + if jollycount > 18 then + jollycount = 18 + end --reduce excessive consumeable spam (Lag) + for i = 1, jollycount do + local card = create_card("Consumeables", G.consumeables, nil, nil, nil, nil, nil, "cry_doodlem") + card:set_edition({ negative = true }) + card:add_to_deck() + G.consumeables:emplace(card) + end + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { message = localize("cry_m_ex"), colour = G.C.DARK_EDITION } + ) + return nil, true + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local virgo = { + object_type = "Joker", + name = "cry-virgo", + key = "virgo", + effect = "M Joker", + pos = { x = 1, y = 2 }, + soul_pos = { x = 10, y = 0, extra = { x = 2, y = 2 } }, + config = { extra = { bonus = 4, type = "Pair" }, jolly = { t_mult = 8, type = "Pair" } }, + rarity = "cry_epic", + cost = 8, + order = 265, + eternal_compat = false, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + if not center.edition or (center.edition and not center.edition.polychrome) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_polychrome + end + return { vars = { center.ability.extra.bonus, localize(center.ability.extra.type, "poker_hands") } } + end, + atlas = "atlasepic", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and context.before + and next(context.poker_hands["Pair"]) + and not context.blueprint + then + card.ability.extra_value = card.ability.extra_value + card.ability.extra.bonus --this doesn't seem to work with retrigger jokers. Intentional? + card:set_cost() + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("k_val_up"), + colour = G.C.MONEY, + }) + end + if context.selling_self and not context.blueprint and not context.retrigger_joker then + G.E_MANAGER:add_event(Event({ + func = function() + G.E_MANAGER:add_event(Event({ + func = function() + local summon = math.floor((card.ability.extra_value + 4) / 4) + if summon < 1 or summon == nil then + summon = 1 + end --precautionary measure, just in case + for i = 1, math.min(80, summon) do --another precautionary measure + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") + card:set_edition({ + polychrome = true, + }) + card:add_to_deck() + G.jokers:emplace(card) + end + return true + end, + })) + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("cry_m_ex"), colour = G.C.DARK_EDITION }) + return true + end, + })) + return nil, true + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local smallestm = { + object_type = "Joker", + name = "cry-smallestm", + key = "smallestm", + effect = "M Joker", + config = { extra = { type = "Pair" } }, + pos = { x = 5, y = 0 }, + rarity = "cry_epic", + cost = 8, + order = 264, + blueprint_compat = true, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { set = "Tag", key = "tag_cry_double_m" } + return { + vars = { + localize(center.ability.extra.type, "poker_hands"), + }, + } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before then + --This isn't retrigger joker compatible for some reason + if context.scoring_name == card.ability.extra.type then + add_tag(Tag("tag_cry_double_m")) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + card_eval_status_text(context.blueprint_card or card, "extra", nil, nil, nil, { + message = localize("cry_m_ex"), + colour = G.C.FILTER, + }) + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local biggestm = { + object_type = "Joker", + name = "cry-biggestm", + key = "biggestm", + config = { extra = { x_mult = 7, type = "Pair", check = false, text = "Inactive" } }, + pos = { x = 3, y = 3 }, + rarity = "cry_epic", + effect = "M Joker", + cost = 12, + order = 268, + blueprint_compat = true, + atlas = "atlasepic", + loc_vars = function(self, info_queue, center) + return { + vars = { + center.ability.extra.x_mult, + localize(center.ability.extra.type, "poker_hands"), + center.ability.extra.text, + }, + } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and card.ability.extra.check and not context.before and not context.after then + return { + message = localize{type='variable',key='a_xmult',vars={card.ability.extra.x_mult}}, + Xmult_mod = card.ability.extra.x_mult, + colour = G.C.MULT, + } + end + if context.cardarea == G.jokers and context.before and not context.blueprint then + if context.scoring_name == card.ability.extra.type and not card.ability.extra.check then + card.ability.extra.check = true + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("k_active_ex"), + colour = G.C.FILTER, + }) + card.ability.extra.text = localize("cry_active") + end + end + if + context.end_of_round + and card.ability.extra.check + and not context.blueprint + and not context.retrigger_joker + and not context.individual + and not context.repetition + then + card.ability.extra.check = false + card.ability.extra.text = localize("cry_inactive") + return { + message = localize("k_reset"), + card = card, + } + end + end, + cry_credits = { + idea = { + "Kailen" + }, + art = { + "Kailen" + }, + code = { + "Kailen" + } + }, +} +local mprime = { + object_type = "Joker", + name = "cry-mprime", + key = "mprime", + pos = { x = 0, y = 5 }, + soul_pos = { x = 2, y = 5, extra = { x = 1, y = 5 } }, + config = { extra = { mult = 1.05, bonus = 0.04 }, jolly = { t_mult = 8, type = "Pair" } }, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { vars = { center.ability.extra.mult, center.ability.extra.bonus } } + end, + rarity = "cry_exotic", + cost = 50, + order = 1000000, + blueprint_compat = true, + atlas = "atlasexotic", + perishable_compat = false, + calculate = function(self, card, context) + if + context.selling_card + and ( + context.card:is_jolly() + ) + then + if not context.blueprint then + card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.bonus + end + if not context.retrigger_joker then + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("cry_m_ex"), colour = G.C.DARK_EDITION }) + end + elseif context.end_of_round and not context.individual and not context.repetition + and #G.jokers.cards + G.GAME.joker_buffer < G.jokers.config.card_limit + and not context.retrigger_joker then + local loyalservants = {} + for k, _ in pairs(Cryptid.M_jokers) do + if G.P_CENTERS[k] then + loyalservants[#loyalservants + 1] = k + end + end + local mjoker = math.min(1, G.jokers.config.card_limit - (#G.jokers.cards + G.GAME.joker_buffer)) + G.GAME.joker_buffer = G.GAME.joker_buffer + mjoker + G.E_MANAGER:add_event(Event({ + func = function() + if mjoker > 0 then + local card = create_card( + "Joker", + G.jokers, + nil, + nil, + nil, + nil, + pseudorandom_element(loyalservants, pseudoseed("mprime")) + ) + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + G.GAME.joker_buffer = 0 + end + return true + end, + })) + elseif context.other_joker then + if + context.other_joker + and ( + context.other_joker:is_jolly() + or context.other_joker.ability.effect == "M Joker" + ) + then + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize{type='variable',key='a_powmult',vars={card.ability.extra.mult}}, + Emult_mod = card.ability.extra.mult, + colour = G.C.DARK_EDITION, + card = card, + } + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local macabre = { + object_type = "Joker", + name = "cry-Macabre Joker", + key = "macabre", + effect = "M Joker", + order = 263, + pos = { x = 1, y = 2 }, + immutable = true, + config = { jolly = { t_mult = 8, type = "Pair" } }, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + end, + rarity = 1, + cost = 5, + atlas = "atlasthree", + calculate = function(self, card, context) + if context.setting_blind and not (context.blueprint or context.retrigger_joker) and not card.getting_sliced then + G.E_MANAGER:add_event(Event({ + func = function() + local triggered = false + local destroyed_jokers = {} + for _, v in pairs(G.jokers.cards) do + if + v ~= card + and not v:is_jolly() + and v.config.center.key ~= "j_cry_mprime" + and not ( + v.ability.eternal + or v.getting_sliced + or Cryptid.M_jokers[v.config.center.key] + ) + then + destroyed_jokers[#destroyed_jokers + 1] = v + end + end + for _, v in pairs(destroyed_jokers) do + if v.config.center.rarity == "cry_exotic" then + check_for_unlock({ type = "what_have_you_done" }) + end + triggered = true + v.getting_sliced = true + v:start_dissolve({ HEX("57ecab") }, nil, 1.6) + local jolly_card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") + jolly_card:add_to_deck() + G.jokers:emplace(jolly_card) + end + if triggered then + card:juice_up(0.8, 0.8) + play_sound("slice1", 0.96 + math.random() * 0.08) + end + return true + end, + })) + end + end, + cry_credits = { + idea = { + "SDM_0" + }, + art = { + "SDM_0" + }, + code = { + "SDM_0" + } + }, +} +local megg = { + object_type = "Joker", + name = "cry-megg", + key = "Megg", + effect = "M Joker", + blueprint_compat = false, + eternal_compat = false, + pos = { x = 0, y = 4 }, + order = 262, + config = { extra = { amount = 0, amount_mod = 1 }, jolly = { t_mult = 8, type = "Pair" } }, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + return { + vars = { + math.max(1, center.ability.extra.amount_mod), + math.min(200, math.floor(center.ability.extra.amount)), + (center.ability.extra.amount > 1 and "Jokers") or "Joker", + }, + } + end, + rarity = 1, + cost = 4, + atlas = "atlasthree", + calculate = function(self, card, context) + if + context.end_of_round + and card.ability.extra.amount < 200 + and not (context.individual or context.repetition or context.blueprint) + then + card.ability.extra.amount = card.ability.extra.amount + math.max(1, card.ability.extra.amount_mod) + if card.ability.extra.amount > 200 then + card.ability.extra.amount = 200 + end + card_eval_status_text(card, "extra", nil, nil, nil, { message = { localize("cry_jolly_ex") }, colour = G.C.FILTER }) + return nil, true + end + if + context.selling_self + and not (context.blueprint or context.retrigger_joker_check or context.retrigger_joker) + and card.ability.extra.amount > 0 + then + for i = 1, math.min(200, math.floor(card.ability.extra.amount)) do + local jolly_card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_jolly") + jolly_card:add_to_deck() + G.jokers:emplace(jolly_card) + end + end + end, + cry_credits = { + idea = { + "Watermelon Lover" + }, + art = { + "Watermelon Lover" + }, + code = { + "SDM_0" + } + }, +} +local longboi = { + object_type = "Joker", + name = "cry-longboi", + key = "longboi", + pos = { x = 5, y = 4 }, + config = { extra = { mult = nil, bonus = 0.75 } }, + rarity = 1, + cost = 5, + order = 261, + effect = "M Joker", + no_dbl = true, + blueprint_compat = true, + eternal_compat = false, + loc_vars = function(self, info_queue, center) + return { vars = { math.max(0.75, math.floor(center.ability.extra.bonus)), (center.ability.extra.mult ~= nil and center.ability.extra.mult or (G.GAME.monstermult or 1)) } } + end, + atlas = "atlasthree", + calculate = function(self, card, context) + if + context.end_of_round + and not context.individual + and not context.repetition + then + if not G.GAME.monstermult then G.GAME.monstermult = 1 end + G.GAME.monstermult = G.GAME.monstermult + math.max(0.75, math.floor(card.ability.extra.bonus)) + if not context.retrigger_joker then + return { + card_eval_status_text(context.blueprint_card or card, "extra", nil, nil, nil, { + message = localize("cry_m_ex"), + colour = G.C.FILTER, + }), + } + end + elseif + context.cardarea == G.jokers + and ((card.ability.extra.mult or 1) > 1) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.mult } }), + Xmult_mod = card.ability.extra.mult, + } + end + end, + add_to_deck = function(self, card, from_debuff) + if (not from_debuff and card.ability.extra.mult == nil) or card.checkmonster then + --Stops Things like Gemini from updating mult when it isn't supposed to + if card.checkmonster then card.checkmonster = nil end + + card.ability.extra.mult = G.GAME.monstermult or 1 + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Watermelon Lover" + }, + code = { + "Jevonn" + } + }, +} +local ret_items = { + bubblem, + foodm, + mstack, + mneon, + notebook, + bonk, + loopy, + sacrifice, + reverse, + macabre, + megg, + longboi +} +--retriggering system for M Vouchers +function get_m_retriggers(self, card, context) + local text, disp_text, poker_hands, scoring_hand, non_loc_disp_text = G.FUNCS.get_poker_hand_info(G.play.cards) + if G.GAME.used_vouchers.v_cry_pairamount_plus then + local pairs = 0 + for i = 1, #G.play.cards - 1 do + for j = i + 1, #G.play.cards do + local m, n = G.play.cards[i], G.play.cards[j] + if m:get_id() == n:get_id() then + pairs = pairs + 1 + end + end + end + return pairs + end + if G.GAME.used_vouchers.v_cry_repair_man and poker_hands["Pair"] then + return 1 + end + if G.GAME.used_vouchers.v_cry_pairing and text == "Pair" then + return 1 + end + return 0 +end +return { + name = "M Jokers", + init = function() + --Load In Jokers if specific Cryptid configs are enabled + if Cryptid.enabled["Epic Jokers"] then + Cryptid.M_jokers["j_cry_m"] = true + Cryptid.M_jokers["j_cry_M"] = true + for _, jkr in pairs({ doodlem, virgo, biggestm }) do + ret_items[#ret_items + 1] = jkr + end + end + if Cryptid.enabled["Exotic Jokers"] then + for _, jkr in pairs({ mprime }) do + ret_items[#ret_items + 1] = jkr + end + end + if Cryptid.enabled["Misc."] then + for _, jkr in pairs({ jollysus, scrabble }) do + ret_items[#ret_items + 1] = jkr + end + end + --there must be a better way than this + if Cryptid.enabled["Misc."] and Cryptid.enabled["Epic Jokers"] and Cryptid.enabled["Tags"] then + for _, jkr in pairs({ smallestm }) do + ret_items[#ret_items + 1] = jkr + end + end + --end of cryptid config loading + + for i = 1, #ret_items do + Cryptid.M_jokers["j_cry_" .. ret_items[i].key] = true + local vc = ret_items[i].calculate + ret_items[i].calculate = function(self, card, context) + local ret, trig = vc(self, card, context) + local reps = get_m_retriggers(self, card, context) + if context.retrigger_joker_check and context.other_card == card and reps > 0 then + return { + message = localize("k_again_ex"), + repetitions = reps + (ret and ret.repetitions or 0), + card = card, + } + end + return ret, trig + end + end + if Cryptid.enabled["Exotic Jokers"] then + Cryptid.M_jokers.j_cry_mprime = nil + end + end, + items = ret_items, +} diff --git a/Cryptid/Items/Misc.lua b/Cryptid/Items/Misc.lua new file mode 100644 index 0000000..b9dcf0d --- /dev/null +++ b/Cryptid/Items/Misc.lua @@ -0,0 +1,1950 @@ +local memepack_atlas = { + object_type = "Atlas", + key = "memepack", + path = "pack_cry.png", + px = 71, + py = 95, +} +local meme_object_type = { + object_type = "ObjectType", + key = "Meme", + default = "j_mr_bones", + cards = {}, + inject = function(self) + SMODS.ObjectType.inject(self) + -- insert base game meme jokers + self:inject_card(G.P_CENTERS.j_mr_bones) + self:inject_card(G.P_CENTERS.j_obelisk) + self:inject_card(G.P_CENTERS.j_jolly) + self:inject_card(G.P_CENTERS.j_space) + for i, v in ipairs(Cryptid.memepack) do + self.cards[v] = true + end + end +} +local meme1 = { + object_type = "Booster", + key = "meme_1", + kind = "meme", + atlas = "memepack", + pos = { x = 0, y = 1 }, + order = 5, + config = { extra = 5, choose = 2 }, + cost = 14, + weight = 0.18 / 3, --0.18 base ÷ 3 since there are 3 identical packs + create_card = function(self, card) + if Cryptid.enabled["Misc. Jokers"] and not (G.GAME.used_jokers['j_cry_waluigi'] and not next(find_joker("Showman"))) then + if pseudorandom('meme1_'..G.GAME.round_resets.ante) > 0.997 then + return create_card(nil, G.pack_cards, nil, nil, true, true, "j_cry_waluigi", nil) + end + end + return create_card("Meme", G.pack_cards, nil, nil, true, true, nil, "cry_meme") + end, + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.CRY_ASCENDANT) + ease_background_colour({ new_colour = G.C.CRY_ASCENDANT, special_colour = G.C.BLACK, contrast = 2 }) + end, + loc_vars = function(self, info_queue, card) + return { vars = { card.config.center.config.choose, card.ability.extra } } + end, --For some reason, I need to keep the loc_txt or else it crashes + loc_txt = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + update_pack = function(self, dt) + ease_colour(G.C.DYN_UI.MAIN, G.C.CRY_ASCENDANT) + ease_background_colour({ new_colour = G.C.CRY_ASCENDANT, special_colour = G.C.BLACK, contrast = 2 }) + SMODS.Booster.update_pack(self, dt) + end, + group_key = "k_cry_meme_pack", +} +local meme2 = { + object_type = "Booster", + key = "meme_two", + kind = "meme", + atlas = "memepack", + pos = { x = 1, y = 1 }, + order = 6, + config = { extra = 5, choose = 2 }, + cost = 14, + weight = 0.18 / 3, --0.18 base ÷ 3 since there are 3 identical packs + create_card = function(self, card) + if Cryptid.enabled["Misc. Jokers"] and not (G.GAME.used_jokers['j_cry_waluigi'] and not next(find_joker("Showman"))) then + if pseudorandom('memetwo_'..G.GAME.round_resets.ante) > 0.997 then + return create_card(nil, G.pack_cards, nil, nil, true, true, "j_cry_waluigi", nil) + end + end + return create_card("Meme", G.pack_cards, nil, nil, true, true, nil, "cry_memetwo") + end, + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.CRY_ASCENDANT) + ease_background_colour({ new_colour = G.C.CRY_ASCENDANT, special_colour = G.C.BLACK, contrast = 2 }) + end, + loc_vars = function(self, info_queue, card) + return { vars = { card.config.center.config.choose, card.ability.extra } } + end, + loc_txt = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + update_pack = function(self, dt) + ease_colour(G.C.DYN_UI.MAIN, G.C.CRY_ASCENDANT) + ease_background_colour({ new_colour = G.C.CRY_ASCENDANT, special_colour = G.C.BLACK, contrast = 2 }) + SMODS.Booster.update_pack(self, dt) + end, + group_key = "k_cry_meme_pack", +} +local meme3 = { + object_type = "Booster", + key = "meme_three", + kind = "meme", + atlas = "memepack", + pos = { x = 2, y = 1 }, + order = 7, + config = { extra = 5, choose = 2 }, + cost = 14, + weight = 0.18 / 3, --0.18 base ÷ 3 since there are 3 identical packs + create_card = function(self, card) + if Cryptid.enabled["Misc. Jokers"] and not (G.GAME.used_jokers['j_cry_waluigi'] and not next(find_joker("Showman"))) then + if pseudorandom('memethree_'..G.GAME.round_resets.ante) > 0.997 then + return create_card(nil, G.pack_cards, nil, nil, true, true, "j_cry_waluigi", nil) + end + end + return create_card("Meme", G.pack_cards, nil, nil, true, true, nil, "cry_memethree") + end, + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.CRY_ASCENDANT) + ease_background_colour({ new_colour = G.C.CRY_ASCENDANT, special_colour = G.C.BLACK, contrast = 2 }) + end, + loc_vars = function(self, info_queue, card) + return { vars = { card.config.center.config.choose, card.ability.extra } } + end, + loc_txt = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + update_pack = function(self, dt) + ease_colour(G.C.DYN_UI.MAIN, G.C.CRY_ASCENDANT) + ease_background_colour({ new_colour = G.C.CRY_ASCENDANT, special_colour = G.C.BLACK, contrast = 2 }) + SMODS.Booster.update_pack(self, dt) + end, + group_key = "k_cry_meme_pack", +} + +if not AurinkoAddons then + AurinkoAddons = {} +end + +--Edition code based on Bunco's Glitter Edition + +local mosaic_shader = { + object_type = "Shader", + key = "mosaic", + path = "mosaic.fs", +} +local mosaic = { + object_type = "Edition", + key = "mosaic", + order = 2, + weight = 0.8, --slightly rarer than Polychrome + shader = "mosaic", + in_shop = true, + extra_cost = 6, + config = { x_chips = 2.5 }, + sound = { + sound = "cry_e_mosaic", + per = 1, + vol = 0.2, + }, + get_weight = function(self) + return G.GAME.edition_rate * self.weight + end, + loc_vars = function(self, info_queue) + return { vars = { self.config.x_chips } } + end, +} +local oversat_shader = { + object_type = "Shader", + key = "oversat", + path = "oversat.fs", +} +local oversat = { + object_type = "Edition", + key = "oversat", + order = 3, + weight = 3, + shader = "oversat", + in_shop = true, + extra_cost = 5, + sound = { + sound = "cry_e_oversaturated", + per = 1, + vol = 0.25, + }, + get_weight = function(self) + return G.GAME.edition_rate * self.weight + end, + on_apply = function(card) + cry_with_deck_effects(card, function(card) + cry_misprintize(card, nil, true) + cry_misprintize(card, { + min = 2 * (G.GAME.modifiers.cry_misprint_min or 1), + max = 2 * (G.GAME.modifiers.cry_misprint_max or 1), + }) + end) + if card.config.center.apply_oversat then + card.config.center:apply_oversat(card, function(val) + return cry_misprintize_val(val, { + min = 2 * (G.GAME.modifiers.cry_misprint_min or 1), + max = 2 * (G.GAME.modifiers.cry_misprint_max or 1), + }) + end) + end + end, + on_remove = function(card) + cry_with_deck_effects(card, function(card) + cry_misprintize(card, {min = 1, max = 1}, true) -- + cry_misprintize(card) + end) + end, +} + +AurinkoAddons.cry_oversat = function(card, hand, instant, amount) + G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].chips + (G.GAME.hands[hand].l_chips * amount), 0) + G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].mult + (G.GAME.hands[hand].l_mult * amount), 1) + if not instant then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + func = function() + play_sound("chips1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 1.3 }, { chips = G.GAME.hands[hand].chips, StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + func = function() + play_sound("multhit1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 1.3 }, { mult = G.GAME.hands[hand].mult, StatusText = true }) + elseif hand == G.handlist[#G.handlist] then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("chips1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 1.3 }, { chips = (amount > 0 and "++" or "--"), StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("multhit1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 1.3 }, { mult = (amount > 0 and "++" or "--"), StatusText = true }) + end +end + +local glitched_shader = { + object_type = "Shader", + key = "glitched", + path = "glitched.fs", +} +local glitched = { + object_type = "Edition", + key = "glitched", + order = 1, + weight = 15, + shader = "glitched", + in_shop = true, + extra_cost = 3, + sound = { + sound = "cry_e_glitched", + per = 1, + vol = 0.25, + }, + get_weight = function(self) + return G.GAME.edition_rate * self.weight + end, + on_apply = function(card) + cry_with_deck_effects(card, function(card) + cry_misprintize(card, nil, true) + cry_misprintize(card, { + min = 0.1 * (G.GAME.modifiers.cry_misprint_min or 1), + max = 10 * (G.GAME.modifiers.cry_misprint_max or 1), + }) + end) + if card.config.center.apply_glitched then + card.config.center:apply_glitched(card, function(val) + return cry_misprintize_val(val, { + min = 0.1 * (G.GAME.modifiers.cry_misprint_min or 1), + max = 10 * (G.GAME.modifiers.cry_misprint_max or 1), + }) + end) + end + end, + on_remove = function(card) + cry_with_deck_effects(card, function(card) + cry_misprintize(card, {min = 1, max = 1}, true) + cry_misprintize(card) -- Correct me if i'm wrong but this is for misprint deck. or atleast it is after this patch + end) + end, +} + +local randtext = { + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + " ", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "+", + "-", + "?", + "!", + "$", + "%", + "[", + "]", + "(", + ")", +} + +local function obfuscatedtext(length) + local str = "" + for i = 1, length do + str = str .. randtext[math.random(#randtext)] + end + return str +end + +AurinkoAddons.cry_glitched = function(card, hand, instant, amount) + local modc = G.GAME.hands[hand].l_chips + * cry_log_random( + pseudoseed("cry_aurinko_chips_misprint" .. G.GAME.round_resets.ante), + (G.GAME.modifiers.cry_misprint_min or 1) / 10, + (G.GAME.modifiers.cry_misprint_max or 1) * 10 + ) + * amount + local modm = G.GAME.hands[hand].l_mult + * cry_log_random( + pseudoseed("cry_aurinko_mult_misprint" .. G.GAME.round_resets.ante), + (G.GAME.modifiers.cry_misprint_min or 1) / 10, + (G.GAME.modifiers.cry_misprint_max or 1) * 10 + ) + * amount + G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].chips + modc, 1) + G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].mult + modm, 1) + if not instant then + for i = 1, math.random(2, 4) do + update_hand_text( + { sound = "button", volume = 0.4, pitch = 1.1, delay = 0.2 }, + { chips = obfuscatedtext(3) } + ) + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + func = function() + play_sound("chips1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text( + { delay = 0 }, + { chips = (amount > 0 and "+" or "-") .. number_format(math.abs(modc)), StatusText = true } + ) + update_hand_text({ delay = 1.3 }, { chips = G.GAME.hands[hand].chips }) + for i = 1, math.random(2, 4) do + update_hand_text({ sound = "button", volume = 0.4, pitch = 1.1, delay = 0.2 }, { mult = obfuscatedtext(3) }) + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + func = function() + play_sound("multhit1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text( + { delay = 0 }, + { mult = (amount > 0 and "+" or "-") .. number_format(math.abs(modm)), StatusText = true } + ) + update_hand_text({ delay = 1.3 }, { mult = G.GAME.hands[hand].mult }) + elseif hand == G.handlist[#G.handlist] then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("chips1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 1.3 }, { chips = (amount > 0 and "+" or "-") .. "???", StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("multhit1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 1.3 }, { mult = (amount > 0 and "+" or "-") .. "???", StatusText = true }) + end +end + +local astral_shader = { + object_type = "Shader", + key = "astral", + path = "astral.fs", +} +local astral = { + object_type = "Edition", + key = "astral", + order = 30, + weight = 0.3, --very rare + shader = "astral", + in_shop = true, + extra_cost = 3, + sound = { + sound = "talisman_emult", + per = 1, + vol = 0.5, + }, + get_weight = function(self) + return G.GAME.edition_rate * self.weight + end, + config = { e_mult = 1.1 }, + loc_vars = function(self, info_queue) + return { vars = { self.config.e_mult } } + end, +} +local blurred_shader = { + object_type = "Shader", + key = "blur", + path = "blur.fs", +} +local blurred = { + object_type = "Edition", + key = "blur", + order = 6, + weight = 0.5, --very rare + shader = "blur", + in_shop = true, + extra_cost = 3, + sound = { + sound = "cry_e_blur", + per = 1, + vol = 0.5, + }, + get_weight = function(self) + return G.GAME.edition_rate * self.weight + end, + config = { retrigger_chance = 2, retriggers = 1, extra_retriggers = 1 }, + loc_vars = function(self, info_queue, center) + local chance = center and center.edition.retrigger_chance or self.config.retrigger_chance + local retriggers = center and center.edition.retriggers or self.config.retriggers + + return { vars = { G.GAME.probabilities.normal, chance, retriggers } } + end, + calculate = function(self, card, context) + if context.retrigger_edition_check then + if pseudorandom("cry_blurred") <= G.GAME.probabilities.normal / self.config.retrigger_chance then + return { + message = localize("cry_again_q"), + repetitions = self.config.extra_retriggers, + card = card, + } + end + end + end, +} +local noisy_shader = { + object_type = "Shader", + key = "noisy", + path = "noisy.fs", +} +local noisy_stats = { + min = { + mult = 0, + chips = 0 + }, + max = { + mult = 30, + chips = 150 + } +} +local noisy = { + object_type = "Edition", + key = "noisy", + order = 7, + weight = 3, + shader = "noisy", + in_shop = true, + extra_cost = 4, + config = { min_mult = noisy_stats.min.mult, max_mult = noisy_stats.max.mult, min_chips = noisy_stats.min.chips, max_chips = noisy_stats.max.chips }, + sound = { + sound = "cry_e_noisy", + per = 1, + vol = 0.25, + }, + calculate = function(self, card, context) + if context.edition_main and context.edition_val then + context.edition_val.mult_mod = pseudorandom("cry_noisy_mult", self.config.min_mult, self.config.max_mult) + context.edition_val.chip_mod = pseudorandom("cry_noisy_chips", self.config.min_chips, self.config.max_chips) + end + end, + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + if not full_UI_table.name then + full_UI_table.name = localize({ type = "name", set = self.set, key = self.key, nodes = full_UI_table.name }) + end + local r_mults = {} + for i = self.config.min_mult, self.config.max_mult do + r_mults[#r_mults + 1] = tostring(i) + end + local loc_mult = " " .. (localize("k_mult")) .. " " + local r_chips = {} + for i = self.config.min_chips, self.config.max_chips do + r_chips[#r_chips + 1] = tostring(i) + end + local loc_chips = " Chips " + mult_ui = { + { n = G.UIT.T, config = { text = " +", colour = G.C.MULT, scale = 0.32 } }, + { + n = G.UIT.O, + config = { + object = DynaText({ + string = r_mults, + colours = { G.C.MULT }, + pop_in_rate = 9999999, + silent = true, + random_element = true, + pop_delay = 0.5, + scale = 0.32, + min_cycle_time = 0, + }), + }, + }, + { + n = G.UIT.O, + config = { + object = DynaText({ + string = { + { string = "rand()", colour = G.C.JOKER_GREY }, + { + string = "#@" + .. (G.deck and G.deck.cards[1] and G.deck.cards[#G.deck.cards].base.id or 11) + .. ( + G.deck + and G.deck.cards[1] + and G.deck.cards[#G.deck.cards].base.suit + and G.deck.cards[#G.deck.cards].base.suit:sub(1, 1) + or "D" + ), + colour = G.C.RED, + }, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + loc_mult, + }, + colours = { G.C.UI.TEXT_DARK }, + pop_in_rate = 9999999, + silent = true, + random_element = true, + pop_delay = 0.2011, + scale = 0.32, + min_cycle_time = 0, + }), + }, + }, + } + chip_ui = { + { n = G.UIT.T, config = { text = " +", colour = G.C.CHIPS, scale = 0.32 } }, + { + n = G.UIT.O, + config = { + object = DynaText({ + string = r_chips, + colours = { G.C.CHIPS }, + pop_in_rate = 9999999, + silent = true, + random_element = true, + pop_delay = 0.5, + scale = 0.32, + min_cycle_time = 0, + }), + }, + }, + { + n = G.UIT.O, + config = { + object = DynaText({ + string = { + { string = "rand()", colour = G.C.JOKER_GREY }, + { + string = "@#" + .. (G.deck and G.deck.cards[1] and G.deck.cards[1].base.suit and G.deck.cards[1].base.suit:sub(2, 2) or "m") + .. (G.deck and G.deck.cards[1] and G.deck.cards[1].base.id or 7), + colour = G.C.BLUE, + }, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + loc_chips, + }, + colours = { G.C.UI.TEXT_DARK }, + pop_in_rate = 9999999, + silent = true, + random_element = true, + pop_delay = 0.2011, + scale = 0.32, + min_cycle_time = 0, + }), + }, + }, + } + desc_nodes[#desc_nodes + 1] = mult_ui + desc_nodes[#desc_nodes + 1] = chip_ui + end, +} + +AurinkoAddons.cry_noisy = function(card, hand, instant, amount) + local modc = pseudorandom("cry_noisy_chips_aurinko", noisy_stats.min.chips, noisy_stats.max.chips) + local modm = pseudorandom("cry_noisy_mult_aurinko", noisy_stats.min.mult, noisy_stats.max.mult) + G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].chips + modc, 1) + G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].mult + modm, 1) + if not instant then + for i = 1, math.random(2, 4) do + update_hand_text( + { sound = "button", volume = 0.4, pitch = 1.1, delay = 0.2 }, + { chips = obfuscatedtext(3) } + ) + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + func = function() + play_sound("chips1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text( + { delay = 0 }, + { chips = (amount > 0 and "+" or "-") .. number_format(math.abs(modc)), StatusText = true } + ) + update_hand_text({ delay = 1.3 }, { chips = G.GAME.hands[hand].chips }) + for i = 1, math.random(2, 4) do + update_hand_text({ sound = "button", volume = 0.4, pitch = 1.1, delay = 0.2 }, { mult = obfuscatedtext(3) }) + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + func = function() + play_sound("multhit1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text( + { delay = 0 }, + { mult = (amount > 0 and "+" or "-") .. number_format(math.abs(modm)), StatusText = true } + ) + update_hand_text({ delay = 1.3 }, { mult = G.GAME.hands[hand].mult }) + elseif hand == G.handlist[#G.handlist] then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("chips1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 1.3 }, { chips = (amount > 0 and "+" or "-") .. "???", StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("multhit1") + card:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 1.3 }, { mult = (amount > 0 and "+" or "-") .. "???", StatusText = true }) + end +end + +local jollyeditionshader = { + object_type = "Shader", + key = "m", + path = "m.fs", +} +local jollyedition = { + object_type = "Edition", + in_shop = false, + order = 31, + weight = 0, + pos = {x = 2, y = 0}, + name = "cry-jollyedition", + sound = { + sound = "cry_e_jolly", + per = 1, + vol = 0.3, + }, + extra_cost = 0, + config = { mult = 8 }, + apply_to_float = true, + key = "m", + shader = "m", + disable_base_shader = true, + disable_shadow = true, + loc_vars = function(self, info_queue) + return { vars = { self.config.mult } } + end, +} + +local glass_shader = { + object_type = "Shader", + key = "glass", + path = "glass.fs", + send_vars = function(sprite, card) + return { + lines_offset = card and card.edition and card.edition.cry_glass_seed or 0, + } + end, +} +local glass_edition = { + object_type = "Edition", + key = "glass", + order = 4, + shader = "glass", + in_shop = true, + disable_base_shader = true, + disable_shadow = true, + on_apply = function(card) + -- Randomize offset to -1..1 + card.edition.cry_glass_seed = pseudorandom("e_cry_glass") * 2 - 1 + end, + sound = { + sound = "cry_e_fragile", + per = 1, + vol = 0.3, + }, + weight = 7, + extra_cost = 2, + config = { x_mult = 3, shatter_chance = 8 }, + loc_vars = function(self, info_queue) + return { + vars = { + (G.GAME.probabilities.normal or 1) * (self.config.shatter_chance - 1), + self.config.shatter_chance, + self.config.x_mult, + }, + } + end, + calculate = function(self, card, context) + if + context.joker_triggered + or ( + context.from_playing_card + and context.cardarea + and context.cardarea == G.play + and not context.repetition + ) + then + if + pseudorandom("cry_fragile") + > G.GAME.probabilities.normal * (self.config.shatter_chance - 1) / self.config.shatter_chance + and not card.ability.eternal + then + card.will_shatter = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + func = function() + card:shatter() + return true + end, + })) + end + end + end, +} + +local gold_shader = { + object_type = "Shader", + key = "gold", + path = "gold.fs", + send_vars = function(sprite, card) + return { + lines_offset = card and card.edition and card.edition.cry_gold_seed or 0, + } + end, +} +local gold_edition = { + object_type = "Edition", + key = "gold", + order = 5, + shader = "gold", + weight = 7, + extra_cost = 2, + in_shop = true, + config = { dollars = 2 }, + loc_vars = function(self, info_queue) + return { vars = { self.config.dollars } } + end, + sound = { + sound = "cry_e_golden", + per = 1, + vol = 0.3, + }, + on_apply = function(card) + -- Randomize offset to -1..1 + card.edition.cry_gold_seed = pseudorandom("e_cry_gold") * 2 - 1 + end, + calculate = function(self, card, context) + if + context.joker_triggered + or context.from_consumable + or ( + context.from_playing_card + and context.cardarea + and context.cardarea == G.play + and not context.repetition + ) + then + ease_dollars(self.config.dollars) + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize("$") .. self.config.dollars, colour = G.C.MONEY } + ) + end + end, +} + +local double_sided = { + object_type = "Edition", + key = "double_sided", + shader = false, + order = 32, + weight = 10, + extra_cost = 0, + in_shop = true, + sound = { + sound = "cry_e_double_sided", + per = 1, + vol = 0.3, + }, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Axolotolus", + }, + }, + get_weight = function(self) + return G.GAME.edition_rate * self.weight * (G.GAME.used_vouchers.v_cry_double_vision and 4 or 1) + end, +} +local echo = { + object_type = "Enhancement", + key = "echo", + atlas = "cry_misc", + pos = { x = 2, y = 0 }, + config = { retriggers = 2, extra = 2 }, + loc_vars = function(self, info_queue) + return { vars = { self.config.retriggers, G.GAME.probabilities.normal, self.config.extra } } + end, +} +local eclipse = { + object_type = "Consumable", + set = "Tarot", + name = "cry-Eclipse", + key = "eclipse", + order = 1, + pos = { x = 4, y = 0 }, + config = { mod_conv = "m_cry_echo", max_highlighted = 1 }, + atlas = "atlasnotjokers", + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.m_cry_echo + + return { vars = { self.config.max_highlighted } } + end, +} +local blessing = { + object_type = "Consumable", + set = "Tarot", + name = "cry-theblessing", + key = "theblessing", + order = 6, + pos = { x = 2, y = 3 }, + cost = 3, + atlas = "atlasnotjokers", + can_use = function(self, card) + return #G.consumeables.cards < G.consumeables.config.card_limit or card.area == G.consumeables + end, + can_bulk_use = true, + use = function(self, card, area, copier) + local used_consumable = copier or card + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + if G.consumeables.config.card_limit > #G.consumeables.cards then + play_sound("timpani") + local forced_key = get_random_consumable("blessing", nil, "c_cry_blessing") + local _card = create_card("Consumeables", G.consumables, nil, nil, nil, nil, forced_key.config.center_key, "blessing") + _card:add_to_deck() + G.consumeables:emplace(_card) + used_consumable:juice_up(0.3, 0.5) + end + return true + end, + })) + delay(0.6) + end, +} +local azure_seal = { + object_type = "Seal", + name = "cry-Azure-Seal", + key = "azure", + badge_colour = HEX("1d4fd7"), + config = { planets_amount = 3 }, + loc_vars = function(self, info_queue) + return { vars = { self.config.planets_amount } } + end, + atlas = "cry_misc", + pos = { x = 0, y = 2 }, + calculate = function(self, card, context) + if context.destroying_card then + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.0, + func = function() + local card_type = "Planet" + local _planet = nil + if G.GAME.last_hand_played then + for k, v in pairs(G.P_CENTER_POOLS.Planet) do + if v.config.hand_type == G.GAME.last_hand_played then + _planet = v.key + break + end + end + end + + for i = 1, self.config.planets_amount do + local card = create_card(card_type, G.consumeables, nil, nil, nil, nil, _planet, "cry_azure") + + card:set_edition({ negative = true }, true) + card:add_to_deck() + G.consumeables:emplace(card) + end + return true + end, + })) + + return true + end + end, +} + +local typhoon = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Typhoon", + key = "typhoon", + order = 8, + config = { + -- This will add a tooltip. + mod_conv = "cry_azure_seal", + -- Tooltip args + seal = { planets_amount = 3 }, + max_highlighted = 1, + }, + loc_vars = function(self, info_queue, center) + -- Handle creating a tooltip with set args. + info_queue[#info_queue + 1] = + { set = "Other", key = "cry_azure_seal", specific_vars = { self.config.seal.planets_amount } } + return { vars = { center.ability.max_highlighted } } + end, + cost = 4, + atlas = "atlasnotjokers", + pos = { x = 0, y = 4 }, + use = function(self, card, area, copier) --Good enough + local used_consumable = copier or card + for i = 1, #G.hand.highlighted do + local highlighted = G.hand.highlighted[i] + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + highlighted:juice_up(0.3, 0.5) + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.1, + func = function() + if highlighted then + highlighted:set_seal("cry_azure") + end + return true + end, + })) + delay(0.5) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + G.hand:unhighlight_all() + return true + end, + })) + end + end, +} + +local meld = { + object_type = "Consumable", + set = "Tarot", + name = "cry-Meld", + key = "meld", + order = 3, + pos = { x = 4, y = 4 }, + config = { extra = 4 }, + cost = 4, + atlas = "atlasnotjokers", + can_use = function(self, card) + if #G.jokers.highlighted + + #G.hand.highlighted + - (G.hand.highlighted[1] and G.hand.highlighted[1] == self and 1 or 0) + == 1 then + if #G.jokers.highlighted == 1 and Card.no(G.jokers.highlighted[1], "dbl") then return false end + return true + end + end, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Axolotolus", + }, + }, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_double_sided + end, + use = function(self, card, area, copier) + if #G.jokers.highlighted == 1 then + G.jokers.highlighted[1]:remove_from_deck(true) + G.jokers.highlighted[1]:set_edition({ cry_double_sided = true }) + G.jokers.highlighted[1]:add_to_deck(true) + G.jokers:remove_from_highlighted(G.jokers.highlighted[1]) + else + G.hand.highlighted[1]:set_edition({ cry_double_sided = true }) + G.hand:remove_from_highlighted(G.hand.highlighted[1]) + end + end, + in_pool = function() + return G.GAME.used_vouchers.v_cry_double_slit + end +} + +local bwark = { + object_type = "PokerHand", + key = 'Bulwark', + visible = false, + chips = 100, + mult = 10, + l_chips = 50, + l_mult = 1, + example = { + { 'S_A', true, 'm_stone' }, + { 'S_A', true, 'm_stone' }, + { 'S_A', true, 'm_stone' }, + { 'S_A', true, 'm_stone' }, + { 'S_A', true, 'm_stone' }, + }, + evaluate = function(parts, hand) + local stones = {} + for i, card in ipairs(hand) do + if card.config.center_key == 'm_stone' or (card.config.center.no_rank and card.config.center.no_suit) then stones[#stones+1] = card end + end + return #stones >= 5 and {stones} or {} + end, +} +local cluster = { + object_type = "PokerHand", + key = 'Clusterfuck', + visible = false, + chips = 200, + mult = 19, + l_chips = 40, + l_mult = 4, + example = { + { 'S_A', true }, + { 'C_K', true }, + { 'H_J', true }, + { 'S_T', true }, + { 'D_9', true }, + { 'D_8', true }, + { 'S_6', true }, + { 'C_5', true }, + }, + evaluate = function(parts, hand) + local other_hands = next(parts._flush) or next(parts._straight) or next(parts._all_pairs) + if #hand > 7 then + if not other_hands then return {hand} end + end + end, +} +local upair = { + object_type = "PokerHand", + key = 'UltPair', + visible = false, + chips = 220, + mult = 22, + l_chips = 40, + l_mult = 4, + example = { + { 'S_A', true }, + { 'S_A', true }, + { 'S_T', true }, + { 'S_T', true }, + { 'H_K', true }, + { 'H_K', true }, + { 'H_7', true }, + { 'H_7', true }, + }, + evaluate = function(parts, hand) + local scoring_pairs = {} + local unique_suits = 0 + for suit, _ in pairs(SMODS.Suits) do + local scoring_suit_pairs = {} + for i = 1, #parts._2 do + if parts._2[i][1]:is_suit(suit) and parts._2[i][2]:is_suit(suit) then + scoring_suit_pairs[#scoring_suit_pairs+1] = i + end + end + if #scoring_suit_pairs >= 2 then + unique_suits = unique_suits + 1 + for i = 1, #scoring_suit_pairs do + scoring_pairs[scoring_suit_pairs[i]] = (scoring_pairs[scoring_suit_pairs[i]] or 0) + 1 + end + end + end + if unique_suits < 2 then return end + local scored_cards = {} + local sc_max = 0 + local sc_unique = 0 + for i = 1, #parts._2 do + if scoring_pairs[i] then + if scoring_pairs[i] > 1 then + sc_unique = sc_unique + 1 + end + sc_max = math.max(sc_max, scoring_pairs[i]) + scored_cards[#scored_cards+1] = parts._2[i][1] + scored_cards[#scored_cards+1] = parts._2[i][2] + end + end + if sc_max == #scored_cards/2 - 1 and sc_unique == 1 then + return + end + if #scored_cards >= 8 then + return {scored_cards} + end +end, +} +local fulldeck = { + object_type = "PokerHand", + key = 'WholeDeck', + visible = false, + chips = 5200, + mult = 520, + l_chips = 520, + l_mult = 52, + example = { + { 'S_A', true }, + { 'H_A', true }, + { 'C_A', true }, + { 'D_A', true }, + { 'S_K', true }, + { 'H_K', true }, + { 'C_K', true }, + { 'D_K', true }, + { 'S_Q', true }, + { 'H_Q', true }, + { 'C_Q', true }, + { 'D_Q', true }, + { 'S_J', true }, + { 'H_J', true }, + { 'C_J', true }, + { 'D_J', true }, + { 'S_T', true }, + { 'H_T', true }, + { 'C_T', true }, + { 'D_T', true }, + { 'S_9', true }, + { 'H_9', true }, + { 'C_9', true }, + { 'D_9', true }, + { 'S_8', true }, + { 'H_8', true }, + { 'C_8', true }, + { 'D_8', true }, + { 'S_7', true }, + { 'H_7', true }, + { 'C_7', true }, + { 'D_7', true }, + { 'S_6', true }, + { 'H_6', true }, + { 'C_6', true }, + { 'D_6', true }, + { 'S_5', true }, + { 'H_5', true }, + { 'C_5', true }, + { 'D_5', true }, + { 'S_4', true }, + { 'H_4', true }, + { 'C_4', true }, + { 'D_4', true }, + { 'S_3', true }, + { 'H_3', true }, + { 'C_3', true }, + { 'D_3', true }, + { 'S_2', true }, + { 'H_2', true }, + { 'C_2', true }, + { 'D_2', true }, + }, + evaluate = function(parts, hand) + if #hand >= 52 then + local deck_booleans = {} + local scored_cards = {} + for i = 1, 52 do + table.insert(deck_booleans, false) -- i could write this out but nobody wants to see that + end + local wilds = {} + for i, card in ipairs(hand) do + if (card.config.center_key ~= 'm_wild' and not card.config.center.any_suit) + and (card.config.center_key ~= 'm_stone' and not card.config.center.no_rank) then -- i don't know if these are different... this could be completely redundant but redundant is better than broken + local rank = card:get_id() + local suit = card.base.suit + local suit_int = 0 + suit_table = {"Spades", "Hearts", "Clubs", "Diamonds"} + for i = 1, 4 do + if suit == suit_table[i] then suit_int = i end + end + if suit_int > 0 then -- check for custom rank here to prevent breakage? + deck_booleans[suit_int+((rank-2)*4)] = true + table.insert(scored_cards, card) + end + elseif (card.config.center_key == 'm_wild' or card.config.center.any_suit) then + table.insert(wilds, card) + end + end + for i, card in ipairs(wilds) do -- this 100% breaks with custom ranks + local rank = card:get_id() + for i = 1, 4 do + if not deck_booleans[i+((rank-2)*4)] then + deck_booleans[i+((rank-2)*4)] = true + break + end + end + table.insert(scored_cards, card) + end + local entire_fucking_deck = true + for i = 1, #deck_booleans do + if deck_booleans[i] == false then entire_fucking_deck = false break end + end + if entire_fucking_deck == true then + return {scored_cards} + end + end + return + end, +} +local abelt = { + object_type = "Consumable", + set = 'Planet', + key = 'asteroidbelt', + config = { hand_type = 'cry_Bulwark', softlock = true }, + pos = {x = 1, y = 5 }, + order = 2, + atlas = 'atlasnotjokers', + aurinko = true, + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_planet_disc"), get_type_colour(self or card.config, card), nil, 1.2) + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["cry_Bulwark"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + return { + vars = { + localize("cry_hand_bulwark"), + G.GAME.hands["cry_Bulwark"].level, + G.GAME.hands["cry_Bulwark"].l_mult, + G.GAME.hands["cry_Bulwark"].l_chips, + colours = { planetcolourone }, + }, + } + end, + generate_ui = 0, +} +local void = { + object_type = "Consumable", + set = 'Planet', + key = 'void', + order = 3, + config = { hand_type = 'cry_Clusterfuck', softlock = true }, + pos = {x = 0, y = 5 }, + atlas = 'atlasnotjokers', + aurinko = true, + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge("", get_type_colour(self or card.config, card), nil, 1.2) + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["cry_Clusterfuck"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + return { + vars = { + localize("cry_Clusterfuck"), + G.GAME.hands["cry_Clusterfuck"].level, + G.GAME.hands["cry_Clusterfuck"].l_mult, + G.GAME.hands["cry_Clusterfuck"].l_chips, + colours = { planetcolourone }, + }, + } + end, + generate_ui = 0, +} +local marsmoons = { + object_type = "Consumable", + set = 'Planet', + key = 'marsmoons', + order = 4, + config = { hand_type = 'cry_UltPair', softlock = true }, + pos = {x = 2, y = 5 }, + atlas = 'atlasnotjokers', + aurinko = true, + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_planet_satellite"), get_type_colour(self or card.config, card), nil, 1.2) + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["cry_UltPair"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + return { + vars = { + localize("cry_UltPair"), + G.GAME.hands["cry_UltPair"].level, + G.GAME.hands["cry_UltPair"].l_mult, + G.GAME.hands["cry_UltPair"].l_chips, + colours = { planetcolourone }, + }, + } + end, + generate_ui = 0, +} +local universe = { + object_type = "Consumable", + set = 'Planet', + key = 'universe', + config = { hand_type = 'cry_WholeDeck', softlock = true }, + pos = {x = 4, y = 5 }, + order = 5, + atlas = 'atlasnotjokers', + aurinko = true, + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_planet_universe"), get_type_colour(self or card.config, card), nil, 1.2) + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["cry_WholeDeck"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + return { + vars = { + localize("cry_UltPair"), + G.GAME.hands["cry_WholeDeck"].level, + G.GAME.hands["cry_WholeDeck"].l_mult, + G.GAME.hands["cry_WholeDeck"].l_chips, + colours = { planetcolourone }, + }, + } + end, + generate_ui = 0, +} +local miscitems = { + memepack_atlas, + meme_object_type, + meme1, + meme2, + meme3, + mosaic_shader, + oversat_shader, + glitched_shader, + astral_shader, + blurred_shader, + glass_shader, + gold_shader, + noisy_shader, + glass_edition, + gold_edition, + glitched, + noisy, + mosaic, + oversat, + blurred, + astral, + echo, + eclipse, + blessing, + typhoon, + azure_seal, + double_sided, + meld, + bwark, + cluster, + upair, + fulldeck, + abelt, + void, + marsmoons, + universe, +} +if Cryptid.enabled["M Jokers"] then + miscitems[#miscitems + 1] = jollyeditionshader + miscitems[#miscitems + 1] = jollyedition +end +return { + name = "Misc.", + init = function() + --echo card + cs = Card.calculate_seal + function Card:calculate_seal(context) + local ret = cs(self, context) + if context.repetition then + local total_repetitions = ret and ret.repetitions or 0 + + if self.config.center == G.P_CENTERS.m_cry_echo then + if pseudorandom("echo") < G.GAME.probabilities.normal / (self.ability.extra or 2) then --hacky crash fix + total_repetitions = total_repetitions + self.ability.retriggers + end + end + + if total_repetitions > 0 then + return { + message = localize("k_again_ex"), + repetitions = total_repetitions, + card = self, + } + end + end + return ret + end + --Change name of cards with Jolly edition + local gcui = generate_card_ui + function generate_card_ui( + _c, + full_UI_table, + specific_vars, + card_type, + badges, + hide_desc, + main_start, + main_end, + card + ) + local full_UI_table = + gcui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end, card) + if + card + and card.edition + and card.edition.cry_m + and (not card.ability or card.ability.set ~= "Edition") + and full_UI_table + and full_UI_table.name + and type(full_UI_table.name) == "table" + and full_UI_table.name[1] + and full_UI_table.name[1].config + and full_UI_table.name[1].config.object + and full_UI_table.name[1].config.object.config + then + local conf = full_UI_table.name[1].config.object.config + if conf.string and #conf.string > 0 then + local function m_ify_word(text) + -- Define a pattern for vowels + local vowels = "AEIOUaeiou" + + -- Use gsub to replace the first consonant of each word with 'M' + local result = text:gsub("(%a)(%w*)", function(first, rest) + if vowels:find(first) then + -- If the first character is a vowel, add an M + if (not rest[1]) or (rest:lower()[1] == rest[1]) then --this check doesn't work properly + return "M" .. first:lower() .. rest + else + return "M" .. first:upper() .. rest + end + elseif first:lower() == "m" then + -- If the word already starts with 'M', keep it unchanged + return first .. rest + else + -- Replace the first consonant with 'M' + return "M" .. rest + end + end) + + return result + end + function m_ify(text) + -- Use gsub to apply the m_ify_word function to each word + local result = text:gsub("(%S+)", function(word) + return m_ify_word(word) + end) + + return result + end + conf.string[1] = m_ify(conf.string[1]) + full_UI_table.name[1].config.object:remove() + full_UI_table.name[1].config.object = DynaText(conf) + end + end + return full_UI_table + end + + -- Double-Sided - create FLIP button + -- kinda based on Fusion Jokers + local card_focus_ui = G.UIDEF.card_focus_ui + function G.FUNCS.can_flip_card(e) + e.config.colour = G.C.DARK_EDITION + e.config.button = "flip" + end + function G.FUNCS.can_flip_merge_card(e) + local area = e.config.ref_table.area + local mergable = 0 + for i = 1, #area.highlighted do + if area.highlighted[i].edition and area.highlighted[i].edition.cry_double_sided then + mergable = mergable + 1 + mergedcard = area.highlighted[i] + end + end + if mergable == 1 then + e.config.colour = G.C.DARK_EDITION + e.config.button = "flip_merge" + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + end + end + function G.FUNCS.flip(e) + e.config.ref_table:flip() + e.config.ref_table.area:remove_from_highlighted(e.config.ref_table) + end + function G.FUNCS.flip_merge(e) + e.config.ref_table:flip() + e.config.ref_table.area:remove_from_highlighted(e.config.ref_table) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 1, + func = function() + local area = e.config.ref_table.area + area:remove_card(e.config.ref_table) + mergedcard:init_dbl_side() + copy_dbl_card(e.config.ref_table, mergedcard.dbl_side) + e.config.ref_table:remove() + e.config.ref_table = nil + return true + end, + })) + end + local use_and_sell_buttonsref = G.UIDEF.use_and_sell_buttons + function G.UIDEF.use_and_sell_buttons(card) + local retval = use_and_sell_buttonsref(card) + if + card.area + and card.edition + and (card.area == G.jokers or card.area == G.consumeables or card.area == G.hand) + and card.edition.cry_double_sided + and not Card.no(card, "dbl") + then + local use = { + n = G.UIT.C, + config = { align = "cr" }, + nodes = { + { + n = G.UIT.C, + config = { + ref_table = card, + align = "cr", + maxw = 1.25, + padding = 0.1, + r = 0.08, + hover = true, + shadow = true, + colour = G.C.UI.BACKGROUND_INACTIVE, + one_press = true, + button = "flip", + func = "can_flip_card", + }, + nodes = { + { n = G.UIT.B, config = { w = 0.1, h = 0.3 } }, + { + n = G.UIT.T, + config = { + text = localize("b_flip"), + colour = G.C.UI.TEXT_LIGHT, + scale = 0.3, + shadow = true, + }, + }, + }, + }, + }, + } + local m = retval.nodes[1] + if not card.added_to_deck then + use.nodes[1].nodes = { use.nodes[1].nodes[2] } + if card.ability.consumeable then + m = retval + end + end + m.nodes = m.nodes or {} + table.insert(m.nodes, { n = G.UIT.R, config = { align = "cl" }, nodes = { + use, + } }) + return retval + end + if + card.area + and (card.area == G.jokers or card.area == G.consumeables or card.area == G.hand) + and (not card.edition or not card.edition.cry_double_sided) + and not card.ability.eternal + and not Card.no(card, "dbl") + then + for i = 1, #card.area.cards do + if card.area.cards[i].edition and card.area.cards[i].edition.cry_double_sided then + local use = { + n = G.UIT.C, + config = { align = "cr" }, + nodes = { + { + n = G.UIT.C, + config = { + ref_table = card, + align = "cr", + maxw = 1.25, + padding = 0.1, + r = 0.08, + hover = true, + shadow = true, + colour = G.C.UI.BACKGROUND_INACTIVE, + one_press = true, + button = "flip_merge", + func = "can_flip_merge_card", + }, + nodes = { + { n = G.UIT.B, config = { w = 0.1, h = 0.3 } }, + { + n = G.UIT.T, + config = { + text = localize("b_merge"), + colour = G.C.UI.TEXT_LIGHT, + scale = 0.3, + shadow = true, + }, + }, + }, + }, + }, + } + local m = retval.nodes[1] + if not card.added_to_deck then + use.nodes[1].nodes = { use.nodes[1].nodes[2] } + if card.ability.consumeable then + m = retval + end + end + m.nodes = m.nodes or {} + table.insert(m.nodes, { n = G.UIT.R, config = { align = "cl" }, nodes = { + use, + } }) + return retval + end + end + end + return retval + end + local cupd = Card.update + function Card:update(dt) + cupd(self, dt) + if self.area then + if self.area.config.type == "discard" or self.area.config.type == "deck" then + return --prevent lagging event queues with unneeded flips + end + end + if self.sprite_facing == "back" and self.edition and self.edition.cry_double_sided then + self.sprite_facing = "front" + self.facing = "front" + if self.flipping == "f2b" then + self.flipping = "b2f" + end + self:dbl_side_flip() + end + end + function copy_dbl_card(C, c, deck_effects) + if not deck_effects then + Cdeck = C.added_to_deck + cdeck = c.added_to_deck + C.added_to_deck = true + c.added_to_deck = false + end + copy_card(C, c) + c.config.center_key = C.config.center_key + end + function Card:init_dbl_side() + if Card.no(self, "dbl") then + self:set_edition(nil, true) + end + if not self.dbl_side then + self.dbl_side = cry_deep_copy(self) + self.dbl_side:set_ability(G.P_CENTERS.c_base) + -- self.dbl_side:set_base(G.P_CARDS.empty) -- RIGHT HERE THIS RIGHT HERE THATS YOUR DAM CULPRIT + if self.area == G.hand then + self.dbl_side.config.center = cry_deep_copy(self.dbl_side.config.center) + self.dbl_side.config.center.no_rank = true + end + self.dbl_side.added_to_deck = false + return true + end + end + function Card:dbl_side_flip() + local init_dbl_side = self:init_dbl_side() + local tmp_side = cry_deep_copy(self.dbl_side) + self.children.center.scale = { x = self.children.center.atlas.px, y = self.children.center.atlas.py } + self.T.w, self.T.h = G.CARD_W, G.CARD_H + local active_side = self + if next(find_joker("cry-Flip Side")) and self.dbl_side then + active_side = self.dbl_side + end + if not init_dbl_side then + active_side:remove_from_deck(true) + end + copy_dbl_card(self, self.dbl_side, false) + copy_dbl_card(tmp_side, self, false) + active_side:add_to_deck(true) + self.children.center:set_sprite_pos(G.P_CENTERS[self.config.center.key].pos) + if self.base then + --Note: this causes a one-frame stutter + for k, v in pairs(G.P_CARDS) do + if self.base.suit == v.suit and self.base.value == v.value then + self.config.card_key = k + end + end + self:set_sprites(nil, self.config.card) + if self.children and self.children.front and self.config.card_key then self.children.front:set_sprite_pos(G.P_CARDS[self.config.card_key].pos) end + end + if (not self.base or not self.base.name) and self.children.front then + self.children.front:remove() + self.children.front = nil + end + self:set_edition({cry_double_sided = true},true,true) + end + local cgcb = Card.get_chip_bonus + function Card:get_chip_bonus() + if self.ability.set == "Joker" then return 0 end + return cgcb(self) + end + local csave = Card.save + function Card:save() + local cardTable = csave(self) + if self.dbl_side then + cardTable.dbl_side = csave(self.dbl_side) + end + return cardTable + end + local cload = Card.load + function Card:load(cardTable, other_card) + cload(self, cardTable, other_card) + if self.ability.set == "Default" then + self:set_ability(G.P_CENTERS.c_base, true) + end + if not self.base.name then + self:set_base(G.P_CARDS.empty, true) + if self.children.front then + self.children.front:remove() + self.children.front = nil + end + end + if cardTable.dbl_side then + self.dbl_side = cry_deep_copy(self) + cload(self.dbl_side, cardTable.dbl_side) + if self.dbl_side.ability.set == "Default" and self.ability.set ~= "Default" then + self.dbl_side:set_ability(G.P_CENTERS.c_base, true) + end + if not self.dbl_side.base.name then + self.dbl_side:set_base(G.P_CARDS.empty, true) + end + end + end + local rma = remove_all + function remove_all(t) + if t then + rma(t) + end + end + --prevent chaos the clown's ability from being applied on debuff + local catd = Card.add_to_deck + local crfd = Card.remove_from_deck + function Card:add_to_deck(debuff) + if debuff and self.ability.name == 'Chaos the Clown' then + return + end + return catd(self, debuff) + end + function Card:remove_from_deck(debuff) + if debuff and self.ability.name == 'Chaos the Clown' then + return + end + return crfd(self, debuff) + end + local cae = CardArea.emplace + function CardArea:emplace(card,m1,m2) + if not (card.will_shatter or card.destroyed or card.shattered) then + cae(self,card,m1,m2) + else + if card.area then + card.area:remove_card(card) + end + card:remove() + card = nil + end + end + local sjw = set_joker_win + function set_joker_win() + sjw() + for k, v in pairs(G.jokers.cards) do + if v.dbl_side and v.dbl_side.config.center_key and v.dbl_side.ability.set == 'Joker' then + G.PROFILES[G.SETTINGS.profile].joker_usage[v.dbl_side.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.dbl_side.config.center_key] or {count = 1, order = v.dbl_side.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} + if G.PROFILES[G.SETTINGS.profile].joker_usage[v.dbl_side.config.center_key] then + G.PROFILES[G.SETTINGS.profile].joker_usage[v.dbl_side.config.center_key].wins = G.PROFILES[G.SETTINGS.profile].joker_usage[v.dbl_side.config.center_key].wins or {} + G.PROFILES[G.SETTINGS.profile].joker_usage[v.dbl_side.config.center_key].wins[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.dbl_side.config.center_key].wins[G.GAME.stake] or 0) + 1 + end + end + end + G:save_settings() + end + end, + items = miscitems, +} diff --git a/Cryptid/Items/MiscJokers.lua b/Cryptid/Items/MiscJokers.lua new file mode 100644 index 0000000..4b31cb2 --- /dev/null +++ b/Cryptid/Items/MiscJokers.lua @@ -0,0 +1,6676 @@ +local dropshot = { + object_type = "Joker", + name = "cry-Dropshot", + key = "dropshot", + order = 3, + config = { extra = { Xmult_mod = 0.2, x_mult = 1 } }, + pos = { x = 5, y = 0 }, + rarity = 3, + cost = 8, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + return { + vars = { + center.ability.extra.Xmult_mod, + localize( + G.GAME.current_round.cry_dropshot_card and G.GAME.current_round.cry_dropshot_card.suit or "Spades", + "suits_singular" + ), + center.ability.extra.x_mult, + colours = { + G.C.SUITS[G.GAME.current_round.cry_dropshot_card and G.GAME.current_round.cry_dropshot_card.suit or "Spades"], + }, + }, + } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.blueprint then + cards = 0 + for k, v in ipairs(context.scoring_hand) do + v.cry_dropshot_incompat = true + end + for k, v in ipairs(context.full_hand) do + if not v.cry_dropshot_incompat and v:is_suit(G.GAME.current_round.cry_dropshot_card.suit) then + cards = cards + 1 + G.E_MANAGER:add_event(Event({ + func = function() + v:juice_up() + return true + end, + })) + end + end + for k, v in ipairs(context.scoring_hand) do + v.cry_dropshot_incompat = nil + end + if cards > 0 then + card.ability.extra.x_mult = card.ability.extra.x_mult + cards * card.ability.extra.Xmult_mod + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }) } + ) + return nil, true + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.x_mult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + end, + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Mystic Misclick" + }, + code = { + "Math" + } + }, +} +local happyhouse = { + object_type = "Joker", + name = "cry-happyhouse", + key = "happyhouse", + pos = { x = 2, y = 4 }, + order = 2, + config = { extra = { mult = 4, check = 0 } }, + immutable = true, + pools = {["Meme"] = true}, + rarity = 2, + cost = 2, + blueprint_compat = true, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.mult, center.ability.extra.check } } + end, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and context.before + and not context.blueprint + and not context.retrigger_joker + then + card.ability.extra.check = card.ability.extra.check + 1 + if + card.ability.extra.check == 114 + and G.GAME.round_resets.ante < 8 + and not ( + G.GAME.selected_back.effect.center.key == "antimatter" + or G.GAME.selected_back.effect.center.key == "equilibrium" + ) + then --Yes, the cut off point is boss blind Ante 7. I'm evil >:3. + check_for_unlock({ type = "home_realtor" }) + end + if card.ability.extra.check < 114 then --Hardcoded, dont want misprint to mess with this hehe + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = card.ability.extra.check .. "/114", + colour = G.C.DARK_EDITION, + }), + } + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.mult) > to_big(1)) + and card.ability.extra.check > 113 + and not context.before + and not context.after + then + return { + message = localize{type='variable',key='a_powmult',vars={card.ability.extra.mult}}, + Emult_mod = card.ability.extra.mult, + colour = G.C.DARK_EDITION, + card = card, + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local maximized = { + object_type = "Joker", + name = "cry-Maximized", + key = "maximized", + pos = { x = 5, y = 2 }, + rarity = 3, + order = 13, + cost = 11, + immutable = true, + atlas = "atlastwo", + cry_credits = { + idea = { + "Gold" + }, + art = { + "Gold" + }, + code = { + "Math" + } + }, +} +local potofjokes = { + object_type = "Joker", + name = "cry-Pot of Jokes", + key = "pot_of_jokes", + config = { extra = { h_size = -2, h_mod = 1 } }, + pos = { x = 5, y = 0 }, + immutable = true, + rarity = 3, + order = 104, + cost = 10, + perishable_compat = false, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + return { + vars = { + center.ability.extra.h_size < 0 and center.ability.extra.h_size or "+" .. center.ability.extra.h_size, + center.ability.extra.h_mod, + }, + } + end, + calculate = function(self, card, context) + if context.end_of_round and not context.individual and not context.repetition and not context.blueprint then + card.ability.extra.h_size = card.ability.extra.h_size + card.ability.extra.h_mod + G.hand:change_size(card.ability.extra.h_mod) + return { + message = localize({ type = "variable", key = "a_handsize", vars = { card.ability.extra.h_mod } }), + colour = G.C.FILTER, + card = card, + } + end + end, + add_to_deck = function(self, card, from_debuff) + G.hand:change_size(card.ability.extra.h_size) + end, + remove_from_deck = function(self, card, from_debuff) + G.hand:change_size(-card.ability.extra.h_size) + end, + cry_credits = { + idea = { + "Mjiojio" + }, + art = { + "Ein13" + }, + code = { + "Math" + } + }, +} +local queensgambit = { + object_type = "Joker", + name = "cry-Queen's Gambit", + key = "queens_gambit", + pos = { x = 1, y = 0 }, + rarity = 1, + order = 7, + cost = 7, + immutable = true, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.negative) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_negative + end + end, + atlas = "atlastwo", + config = { extra = { type = "Straight Flush" } }, + calculate = function(self, card, context) + if context.destroying_card and not context.blueprint then + if + G.GAME.current_round.current_hand.handname == "Royal Flush" + and SMODS.Ranks[context.destroying_card.base.value].key == "Queen" + then + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize("k_plus_joker"), colour = G.C.FILTER } + ) + G.E_MANAGER:add_event(Event({ + trigger = "after", + func = function() + local card = create_card("Joker", G.jokers, nil, 0.99, nil, nil, nil, "cry_gambit") + card:set_edition({ negative = true }) + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + return true + end, + })) + return nil, true + end + end + end, + cry_credits = { + idea = { + "Project666" + }, + art = { + "Ein13" + }, + code = { + --wonder what happened to this guy + "Thedrunkenbrick" + } + }, +} +local wee_fib = { + object_type = "Joker", + name = "cry-Wee Fibonacci", + key = "wee_fib", + config = { extra = { mult = 0, mult_mod = 3 } }, + pos = { x = 1, y = 5 }, + rarity = 3, + cost = 9, + order = 98, + blueprint_compat = true, + perishable_compat = false, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.mult, center.ability.extra.mult_mod } } + end, + calculate = function(self, card, context) + if context.cardarea == G.play and context.individual and not context.blueprint then + local rank = SMODS.Ranks[context.other_card.base.value].key + if rank == "Ace" or rank == "2" or rank == "3" or rank == "5" or rank == "8" then + card.ability.extra.mult = card.ability.extra.mult + card.ability.extra.mult_mod + + return { + extra = { focus = card, message = localize("k_upgrade_ex") }, + card = card, + colour = G.C.MULT, + } + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.mult) > to_big(0)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.extra.mult } }), + mult_mod = card.ability.extra.mult, + colour = G.C.MULT, + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "LocalThunk" + }, + code = { + "Math" + } + }, +} +local whip = { + object_type = "Joker", + name = "cry-The WHIP", + key = "whip", + pos = { x = 5, y = 3 }, + config = { extra = { Xmult_mod = 0.5, x_mult = 1 } }, + rarity = 2, + cost = 8, + order = 15, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.Xmult_mod, center.ability.extra.x_mult } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.blueprint then + for i = 1, #context.full_hand do + if SMODS.Ranks[context.full_hand[i].base.value].key == "2" then + for j = 1, #context.full_hand do + if SMODS.Ranks[context.full_hand[j].base.value].key == "7" then + --Different suits + for k, v in pairs(SMODS.Suits) do + if + context.full_hand[i]:is_suit(k, nil, true) + and context.full_hand[j]:is_suit(k, nil, true) + then + return + end + end + card.ability.extra.x_mult = card.ability.extra.x_mult + card.ability.extra.Xmult_mod + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { card.ability.extra.x_mult }, + }), + } + ) + return nil, true + end + end + end + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.x_mult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + end, + cry_credits = { + idea = { + "Gold" + }, + art = { + "Ein13" + }, + code = { + "Math" + } + }, +} +local lucky_joker = { + object_type = "Joker", + name = "cry-Lucky Joker", + key = "lucky_joker", + config = { extra = { dollars = 5 } }, + pos = { x = 4, y = 3 }, + rarity = 1, + cost = 4, + order = 36, + blueprint_compat = true, + atlas = "atlasone", + enhancement_gate = "m_lucky", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = G.P_CENTERS.m_lucky + return { vars = { center.ability.extra.dollars } } + end, + calculate = function(self, card, context) + if context.individual and context.other_card.lucky_trigger then + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + card.ability.extra.dollars + G.E_MANAGER:add_event(Event({ func = function() + G.GAME.dollar_buffer = 0 + return true + end })) + return { + dollars = card.ability.extra.dollars, + card = card, + } + end + end, + cry_credits = { + idea = { + "Ein13" + }, + art = { + "Jevonn" + }, + code = { + "WilsontheWolf" + } + }, +} +local cursor = { + object_type = "Joker", + name = "cry-Cursor", + key = "cursor", + config = { extra = { chips = 0, chip_mod = 8 } }, + pos = { x = 4, y = 1 }, + rarity = 1, + cost = 5, + order = 5, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.chips, center.ability.extra.chip_mod } } + end, + calculate = function(self, card, context) + if context.buying_card and not context.blueprint and not (context.card == card) then + card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + colour = G.C.CHIPS, + } + ) + return nil, true + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.chips) > to_big(0)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + chip_mod = card.ability.extra.chips, + } + end + end, + cry_credits = { + idea = { + "Math" + }, + art = { + "Jevonn" + }, + code = { + "Math" + } + }, +} +local pickle = { + object_type = "Joker", + name = "cry-Pickle", + key = "pickle", + config = { extra = { tags = 3, tags_mod = 1 } }, + pos = { x = 3, y = 3 }, + immutable = true, + rarity = 2, + order = 45, + cost = 6, + blueprint_compat = true, + eternal_compat = false, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + return { vars = { math.min(20, center.ability.extra.tags), center.ability.extra.tags_mod } } + end, + calculate = function(self, card, context) + if context.skip_blind then + for i = 1, math.min(20, card.ability.extra.tags) do + local tag = Tag(get_next_tag_key("cry_pickle")) + if tag.name == "Orbital Tag" then + local _poker_hands = {} + for k, v in pairs(G.GAME.hands) do + if v.visible then + _poker_hands[#_poker_hands + 1] = k + end + end + tag.ability.orbital_hand = pseudorandom_element(_poker_hands, pseudoseed("cry_pickle_orbital")) + end + if tag.name == "Boss Tag" then + i = i - 1 --skip these, as they can cause bugs with pack opening from other tags + else + add_tag(tag) + end + end + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = "+"..localize({ type = "variable", key = "a_tag" .. (card.ability.extra.tags > 1 and "s" or ""), vars = { card.ability.extra.tags } })[1], + colour = G.C.FILTER, + } + ) + return nil, true + end + if context.setting_blind and not context.blueprint then + card.ability.extra.tags = card.ability.extra.tags - card.ability.extra.tags_mod + if to_big(card.ability.extra.tags) > to_big(0) then + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = "-"..localize({ type = "variable", key = "a_tag" .. (card.ability.extra.tags > 1 and "s" or ""), vars = { card.ability.extra.tags } })[1], + colour = G.C.FILTER, + } + ) + return nil, true + else + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("k_eaten_ex"), + colour = G.C.FILTER, + } + end + end + end, + cry_credits = { + idea = { + "5381" + }, + art = { + "Mystic Misclick" + }, + code = { + "Math" + } + }, +} +local cube = { + object_type = "Joker", + name = "cry-Cube", + key = "cube", + config = { extra = { chips = 6 } }, + pos = { x = 5, y = 4 }, + rarity = 1, + order = 11, + cost = -27, + blueprint_compat = true, + atlas = "atlasone", + pools = {["Meme"] = true}, + source_gate = "sho", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.chips } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + chip_mod = card.ability.extra.chips, + } + end + end, + cry_credits = { + idea = { + "Ein13", "Math" + }, + art = { + "Ein13" + }, + code = { + "Math" + } + }, +} +local triplet_rhythm = { + object_type = "Joker", + name = "cry-Triplet Rhythm", + key = "triplet_rhythm", + config = { extra = { Xmult = 3 } }, + pos = { x = 0, y = 4 }, + rarity = 1, + order = 10, + cost = 6, + blueprint_compat = true, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.Xmult } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.scoring_hand then + local threes = 0 + for i = 1, #context.scoring_hand do + if SMODS.Ranks[context.scoring_hand[i].base.value].key == "3" then + threes = threes + 1 + end + end + if threes == 3 then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.Xmult } }), + Xmult_mod = card.ability.extra.Xmult, + } + end + end + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "HexaCryonic" + }, + code = { + "Math" + } + }, +} +local booster = { + object_type = "Joker", + name = "cry-Booster Joker", + key = "booster", + config = { extra = { booster_slots = 1 } }, + pos = { x = 2, y = 0 }, + order = 34, + immutable = true, + rarity = 2, + cost = 6, + blueprint_compat = false, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.booster_slots } } + end, + add_to_deck = function(self, card, from_debuff) + if not G.GAME.modifiers.cry_booster_packs then + G.GAME.modifiers.cry_booster_packs = 2 + end + G.GAME.modifiers.cry_booster_packs = G.GAME.modifiers.cry_booster_packs + card.ability.extra.booster_slots + end, + remove_from_deck = function(self, card, from_debuff) + if not G.GAME.modifiers.cry_booster_packs then + G.GAME.modifiers.cry_booster_packs = 2 + end + G.GAME.modifiers.cry_booster_packs = G.GAME.modifiers.cry_booster_packs - card.ability.extra.booster_slots + end, + cry_credits = { + idea = { + "Ein13" + }, + art = { + "Jevonn" + }, + code = { + "Math" + } + }, +} +local chili_pepper = { + object_type = "Joker", + name = "cry-Chili Pepper", + key = "chili_pepper", + config = { extra = { Xmult = 1, Xmult_mod = 0.5, rounds_remaining = 8 } }, + pos = { x = 0, y = 1 }, + rarity = 2, + cost = 6, + order = 48, + blueprint_compat = true, + eternal_compat = false, + perishable_compat = false, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + return { + vars = { center.ability.extra.Xmult, center.ability.extra.Xmult_mod, center.ability.extra.rounds_remaining }, + } + end, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and not context.before + and not context.after + and to_big(card.ability.extra.Xmult) > to_big(1) + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.Xmult } }), + Xmult_mod = card.ability.extra.Xmult, + } + end + if + context.end_of_round + and not context.blueprint + and not context.individual + and not context.repetition + and not context.retrigger_joker + then + card.ability.extra.Xmult = card.ability.extra.Xmult + card.ability.extra.Xmult_mod + card.ability.extra.rounds_remaining = card.ability.extra.rounds_remaining - 1 + if card.ability.extra.rounds_remaining > 0 then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.Xmult } }), + colour = G.C.FILTER, + } + else + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("k_eaten_ex"), + colour = G.C.FILTER, + } + end + end + end, + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Mystic Misclick" + }, + code = { + "Math" + } + }, +} +local compound_interest = { + object_type = "Joker", + name = "cry-Compound Interest", + key = "compound_interest", + config = { extra = { percent_mod = 3, percent = 12 } }, + pos = { x = 3, y = 2 }, + rarity = 3, + order = 9, + cost = 10, + perishable_compat = false, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.percent, center.ability.extra.percent_mod } } + end, + calc_dollar_bonus = function(self, card) + local bonus = math.max(0, math.floor(0.01 * card.ability.extra.percent * (G.GAME.dollars or 1))) + local old = card.ability.extra.percent + card.ability.extra.percent = card.ability.extra.percent + card.ability.extra.percent_mod + compound_interest_scale_mod(card, card.ability.extra.percent_mod, old, card.ability.extra.percent) + if bonus > 0 then + return bonus + end + end, + cry_credits = { + idea = { + "Mjiojio" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Math" + } + }, +} +local big_cube = { + object_type = "Joker", + name = "cry-Big Cube", + key = "big_cube", + joker_gate = "cry-Cube", + config = { extra = { x_chips = 6 } }, + pos = { x = 4, y = 4 }, + rarity = 1, + order = 105, + cost = 27, + blueprint_compat = true, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.x_chips } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after then + return { + message = localize({ type = "variable", key = "a_xchips", vars = { card.ability.extra.x_chips } }), + Xchip_mod = card.ability.extra.x_chips, + colour = G.C.CHIPS, + } + end + end, + cry_credits = { + idea = { + "Mystick Myclick" + }, + art = { + "AlexZGreat" + }, + code = { + "Math" + } + }, +} +local eternalflame = { + object_type = "Joker", + name = "cry-eternalflame", + key = "eternalflame", + pos = { x = 0, y = 4 }, + config = { extra = { extra = 0.1, x_mult = 1 } }, + rarity = 3, + order = 100, + cost = 9, + perishable_compat = false, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.extra, center.ability.extra.x_mult } } + end, + atlas = "atlasone", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.x_mult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + if context.selling_card and not context.blueprint then + card.ability.extra.x_mult = card.ability.extra.x_mult + card.ability.extra.extra + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }) } + ) + return nil, true + end + end, + cry_credits = { + idea = { + "Dovahkiin1307" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local nice = { + object_type = "Joker", + name = "cry-Nice", + key = "nice", + config = { extra = { chips = 420, sixcount = 0, ninecount = 0 } }, + pos = { x = 2, y = 3 }, + pools = {["Meme"] = true}, + rarity = 3, + cost = 6.9, + order = 84, + atlas = "atlasone", + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.chips } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.after then + card.ability.extra.sixcount = 0 + card.ability.extra.ninecount = 0 + for i, v in pairs(context.full_hand) do + if v:get_id() == 6 then + card.ability.extra.sixcount = card.ability.extra.sixcount + 1 + elseif v:get_id() == 9 then + card.ability.extra.ninecount = card.ability.extra.ninecount + 1 + end + end + elseif context.cardarea == G.jokers and not context.before and not context.after then + if card.ability.extra.sixcount > 0 and card.ability.extra.ninecount > 0 then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips or 0 } }), + chip_mod = card.ability.extra.chips or 0, + } + end + end + end, + cry_credits = { + idea = { + "AlexZGreat" + }, + art = { + "Jevonn" + }, + code = { + "AlexZGreat" + } + }, +} +local seal_the_deal = { + object_type = "Joker", + name = "cry-Seal The Deal", + key = "seal_the_deal", + config = { extra = nil }, + pos = { x = 2, y = 4 }, + rarity = 2, + cost = 5, + order = 101, + immutable = true, + atlas = "atlasone", + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play and not context.blueprint and not context.retrigger_joker then + if G.GAME.current_round.hands_left == 0 and not context.other_card.seal then + G.E_MANAGER:add_event(Event({ + func = function() + local seal_type = pseudorandom(pseudoseed("seal_the_deal")) + if seal_type > 0.75 then + context.other_card:set_seal("Red", true) + elseif seal_type > 0.5 then + context.other_card:set_seal("Blue", true) + elseif seal_type > 0.25 then + context.other_card:set_seal("Gold", true) + else + context.other_card:set_seal("Purple", true) + end + card:juice_up(0.3, 0.4) + context.other_card:juice_up(0.3, 0.3) + play_sound("gold_seal", 1.2, 0.4) + return true + end, + })) + delay(0.5) + return nil, true + end + end + end, + set_ability = function(self, card, initial, delay_sprites) + local sealtable = { "blue", "red", "purple" } + if Cryptid.enabled["Misc."] then sealtable[#sealtable + 1] = "azure" end + if Cryptid.enabled["Code Cards"] then sealtable[#sealtable + 1] = "green" end + card.ability.extra = pseudorandom_element(sealtable, pseudoseed('abc')) + --Gold (ULTRA RARE!!!!!!!!) + if pseudorandom('xyz') <= 0.000001 and not (card.area and card.area.config.collection) then + card.children.center:set_sprite_pos({x = 6, y = 4}) + --Others + elseif card.ability.extra == "red" then + card.children.center:set_sprite_pos({x = 6, y = 0}) + elseif card.ability.extra == "azure" then + card.children.center:set_sprite_pos({x = 6, y = 2}) + elseif card.ability.extra == "purple" then + card.children.center:set_sprite_pos({x = 6, y = 3}) + elseif card.ability.extra == "green" then + card.children.center:set_sprite_pos({x = 6, y = 1}) + end + end, + cry_credits = { + idea = { + "Zak" + }, + art = { + "5381" + }, + code = { + "AlexZGreat" + } + }, +} +local chad = { + object_type = "Joker", + name = "cry-Chad", + key = "chad", + pos = { x = 0, y = 3 }, + order = 71, + config = { extra = { retriggers = 2 } }, + immutable = true, + pools = {["Meme"] = true}, + rarity = 3, + cost = 10, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.retriggers } } + end, + atlas = "atlasone", + calculate = function(self, card, context) + if context.retrigger_joker_check and not context.retrigger_joker and context.other_card ~= self then + if context.other_card == G.jokers.cards[1] then + return { + message = localize("k_again_ex"), + repetitions = card.ability.extra.retriggers, + card = card, + } + else + return nil, true + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "SDM_0" + }, + code = { + "Math" + } + }, +} +local jimball = { + object_type = "Joker", + name = "cry-Jimball", + key = "jimball", + pos = { x = 0, y = 0 }, + order = 8, + config = { x_mult = 1, extra = 0.15, override_x_mult_check = true }, + pools = {["Meme"] = true}, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra, center.ability.x_mult } } + end, + rarity = 3, + cost = 9, + blueprint_compat = true, + perishable_compat = false, + calculate = function(self, card, context) + if context.before and not context.blueprint then + local reset = false + local play_more_than = (G.GAME.hands[context.scoring_name].played or 0) + for k, v in pairs(G.GAME.hands) do + if k ~= context.scoring_name and v.played >= play_more_than and v.visible then + reset = true + end + end + if reset then + if to_big(card.ability.x_mult) > to_big(1) then + card.ability.x_mult = 1 + return { + card = self, + message = localize("k_reset"), + } + end + else + card.ability.x_mult = card.ability.x_mult + card.ability.extra + return nil, true + end + end + end, + add_to_deck = function(self, card, from_debuff) + if not from_debuff then + create_cryptid_notif_overlay("jimball") + end + end, + atlas = "jimball", + cry_credits = { + idea = { + "Math" + }, + art = { + "Grazzy", "Zakosek Artworks" + }, + code = { + "Math" + } + }, +} +G.FUNCS.notif_jimball = function() + Cryptid_config.Cryptid.jimball_music = false + G:save_settings() + G.FUNCS:exit_overlay_menu() + -- todo: autosave settings (Not sure if this autosaves it) +end +local jimball_sprite = { --left this one on it's own atlas for obvious reasons + object_type = "Atlas", + key = "jimball", + path = "j_cry_jimball.png", + px = 71, + py = 95, +} +local sus = { + object_type = "Joker", + name = "cry-SUS", + key = "sus", + pos = { x = 1, y = 3 }, + pools = {["Meme"] = true}, + rarity = 3, + cost = 7, + order = 79, + blueprint_compat = true, + atlas = "atlasone", + calculate = function(self, card, context) + local function is_impostor(card) + return card.base.value and SMODS.Ranks[card.base.value].key == "King" and card:is_suit("Hearts") + end + if context.end_of_round and not context.cardarea then + if not card.ability.used_round or card.ability.used_round ~= G.GAME.round then + card.ability.chosen_card = nil + end + local choosable_cards = {} + local has_impostor = false + for i = 1, #G.hand.cards do + if not G.hand.cards[i].murdered_by_impostor then + choosable_cards[#choosable_cards + 1] = G.hand.cards[i] + if is_impostor(G.hand.cards[i]) then + has_impostor = true + end + end + end + if has_impostor then + choosable_cards = {} + for i = 1, #G.hand.cards do + if not G.hand.cards[i].murdered_by_impostor and is_impostor(G.hand.cards[i]) then + choosable_cards[#choosable_cards + 1] = G.hand.cards[i] + end + end + end + card.ability.chosen_card = card.ability.chosen_card + or pseudorandom_element(choosable_cards, pseudoseed("cry_sus")) + if not card.ability.used_round or card.ability.used_round ~= G.GAME.round then + card.ability.used_round = G.GAME.round + local deletable_cards = {} + for k, v in pairs(G.hand.cards) do + if not v.ability.eternal and not is_impostor(v) then + deletable_cards[#deletable_cards + 1] = v + end + end + if #deletable_cards ~= 0 then + local _first_dissolve = nil + for j=1, #G.jokers.cards do + eval_card(G.jokers.cards[j], {cardarea = G.jokers, remove_playing_cards = true, removed = deletable_cards}) + end + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.75, + func = function() + for k, v in pairs(deletable_cards) do + if v ~= card.ability.chosen_card then + v.murdered_by_impostor = true + v:start_dissolve(nil, _first_dissolve) + _first_dissolve = true + end + end + return true + end, + })) + + end + end + if card.ability.chosen_card ~= nil then + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.4, + func = function() + card:juice_up(0.3, 0.4) + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local _c = copy_card(card.ability.chosen_card, nil, nil, G.playing_card) + _c:start_materialize() + _c:add_to_deck() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + table.insert(G.playing_cards, _c) + G.hand:emplace(_c) + playing_card_joker_effects({ _c }) + return true + end, + })) + return { message = localize("cry_sus_ex") } + end + end + end, + cry_credits = { + idea = { + "Math" + }, + art = { + "Jevonn" + }, + code = { + "Math" + } + }, +} +local fspinner = { + object_type = "Joker", + name = "cry-fspinner", + key = "fspinner", + pos = { x = 4, y = 0 }, + config = { extra = { chips = 0, chip_mod = 6 } }, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.chips, center.ability.extra.chip_mod } } + end, + rarity = 1, + cost = 6, + order = 77, + blueprint_compat = true, + perishable_compat = false, + atlas = "fspinner", + calculate = function(self, card, context) + if context.before and not context.blueprint then + local play_more_than = (G.GAME.hands[context.scoring_name].played or 0) + for k, v in pairs(G.GAME.hands) do + if k ~= context.scoring_name and v.played >= play_more_than and v.visible then + card.ability.extra.chips = card.ability.extra.chips + card.ability.extra.chip_mod + return { + message = localize("k_upgrade_ex"), + card = card, + } + end + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.chips) > to_big(0)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + chip_mod = card.ability.extra.chips, + } + end + end, + atlas = "atlasone", + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local luigi = { + object_type = "Joker", + name = "cry-luigi", + key = "luigi", + pos = { x = 0, y = 3 }, + soul_pos = { x = 1, y = 3 }, + config = { extra = { x_chips = 3 } }, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.x_chips } } + end, + rarity = 4, + cost = 20, + order = 86, + blueprint_compat = true, + calculate = function(self, card, context) + if context.other_joker and context.other_joker.ability.set == "Joker" then + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ type = "variable", key = "a_xchips", vars = { card.ability.extra.x_chips } }), + colour = G.C.CHIPS, + Xchip_mod = card.ability.extra.x_chips, + } + end + end, + atlas = "atlasthree", + cry_credits = { + idea = { + "Auto Watto" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Auto Watto" + } + }, +} +local waluigi = { + object_type = "Joker", + name = "cry-Waluigi", + key = "waluigi", + pos = { x = 0, y = 3 }, + soul_pos = { x = 1, y = 3 }, + config = { extra = { Xmult = 2.5 } }, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.Xmult } } + end, + rarity = 4, + cost = 20, + order = 87, + blueprint_compat = true, + calculate = function(self, card, context) + if context.other_joker and context.other_joker.ability.set == "Joker" then + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.Xmult } }), + Xmult_mod = card.ability.extra.Xmult, + } + end + end, + atlas = "atlastwo", + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "HexaCryonic" + }, + code = { + "Math" + } + }, +} +local mario = { + object_type = "Joker", + name = "cry-mario", + key = "mario", + config = { extra = { retriggers = 2 } }, + pos = { x = 4, y = 3 }, + soul_pos = { x = 5, y = 3 }, + rarity = 4, + order = 85, + cost = 20, + blueprint_compat = true, + immutable = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.retriggers } } + end, + atlas = "atlasthree", + calculate = function(self, card, context) + if context.retrigger_joker_check and not context.retrigger_joker and context.other_card ~= self then + return { + message = localize("k_again_ex"), + repetitions = card.ability.extra.retriggers, + card = card, + } + end + end, + cry_credits = { + idea = { + "Auto Watto" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Auto Watto" + } + }, +} +local wario = { + object_type = "Joker", + name = "cry-wario", + key = "wario", + order = 88, + pos = { x = 2, y = 3 }, + soul_pos = { x = 3, y = 3 }, + config = { extra = { money = 3 } }, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.money } } + end, + calculate = function(self, card, context) + if context.post_trigger then + ease_dollars(card.ability.extra.money) + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + (context.blueprint_card or card):juice_up(0.5, 0.5) + return true + end, + })) + end + card_eval_status_text( + context.other_context.blueprint_card or context.other_joker, + "extra", + nil, + nil, + nil, + { message = localize("$") .. card.ability.extra.money, colour = G.C.MONEY } + ) + return nil, true + end + end, + + rarity = 4, + cost = 20, + blueprint_compat = true, + atlas = "atlasthree", + cry_credits = { + idea = { + "Auto Watto" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Auto Watto" + } + }, +} +local krustytheclown = { + object_type = "Joker", + name = "cry-krustytheclown", + key = "krustytheclown", + pos = { x = 3, y = 4 }, + config = { extra = { extra = 0.02, x_mult = 1 } }, + pools = {["Meme"] = true}, + rarity = 2, + order = 31, + cost = 7, + perishable_compat = false, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.extra, center.ability.extra.x_mult } } + end, + atlas = "atlasone", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.x_mult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + if context.cardarea == G.play and context.individual and not context.blueprint then + card.ability.extra.x_mult = card.ability.extra.x_mult + card.ability.extra.extra + return { + extra = { focus = card, message = localize("k_upgrade_ex") }, + card = card, + colour = G.C.MULT, + } + end + end, + cry_credits = { + idea = { + "Izumi" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local blurred = { + object_type = "Joker", + name = "cry-blurred Joker", + key = "blurred", + pos = { x = 4, y = 4 }, + pools = {["Meme"] = true}, + config = { extra = 1 }, + rarity = 1, + cost = 4, + order = 51, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + atlas = "atlastwo", + calculate = function(self, card, context) + if context.setting_blind and not (context.blueprint_card or card).getting_sliced then + G.E_MANAGER:add_event(Event({func = function() + ease_hands_played(card.ability.extra) + card_eval_status_text( + context.blueprint_card or card, + 'extra', + nil, + nil, + nil, + {message = localize{type = 'variable', key = 'a_hands', vars = {card.ability.extra}}} + ) + return true end })) + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local gardenfork = { + object_type = "Joker", + name = "cry-gardenfork", + key = "gardenfork", + pos = { x = 0, y = 1 }, + config = { extra = { money = 7 } }, + rarity = 3, + cost = 7, + order = 66, + blueprint_compat = true, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.money } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.blueprint then + for i = 1, #context.full_hand do + if SMODS.Ranks[context.full_hand[i].base.value].key == "Ace" then + for j = 1, #context.full_hand do + if SMODS.Ranks[context.full_hand[j].base.value].key == "7" then + ease_dollars(card.ability.extra.money) + return { message = "$" .. card.ability.extra.money, colour = G.C.MONEY } + end + end + end + end + end + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "HexaCryonic" + }, + code = { + "Jevonn" + } + }, +} +local lightupthenight = { + object_type = "Joker", + name = "cry-lightupthenight", + key = "lightupthenight", + config = { extra = { xmult = 1.5 } }, + pos = { x = 1, y = 1 }, + atlas = "atlasone", + rarity = 3, + cost = 7, + order = 67, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.xmult } } + end, + calculate = function(self, card, context) + if context.cardarea == G.play and context.individual then + local rank = SMODS.Ranks[context.other_card.base.value].key + if rank == "2" or rank == "7" then + return { + x_mult = card.ability.extra.xmult, + colour = G.C.RED, + card = card, + } + end + end + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "HexaCryonic" + }, + code = { + "Jevonn" + } + }, +} +local nosound = { + object_type = "Joker", + name = "cry-nosound", + key = "nosound", + config = { extra = { retriggers = 3 } }, + pos = { x = 2, y = 1 }, + atlas = "atlasone", + rarity = 3, + order = 68, + cost = 7, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.retriggers } } + end, + calculate = function(self, card, context) + if context.repetition then + if context.cardarea == G.play then + local rank = SMODS.Ranks[context.other_card.base.value].key + if rank == "7" then + return { + message = localize("k_again_ex"), + repetitions = card.ability.extra.retriggers, + card = card, + } + end + end + end + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "HexaCryonic" + }, + code = { + "Jevonn" + } + }, +} +local antennastoheaven = { + object_type = "Joker", + name = "cry-antennastoheaven", + key = "antennastoheaven", + pos = { x = 3, y = 1 }, + config = { extra = { bonus = 0.1, x_chips = 1 } }, + rarity = 3, + cost = 7, + order = 69, + perishable_compat = false, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.bonus, center.ability.extra.x_chips } } + end, + atlas = "atlasone", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.x_chips) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xchips", vars = { number_format(card.ability.extra.x_chips) } }), + Xchip_mod = card.ability.extra.x_chips, + colour = G.C.CHIPS, + } + end + if context.cardarea == G.play and context.individual and not context.blueprint then + local rank = SMODS.Ranks[context.other_card.base.value].key + if rank == "4" or rank == "7" then + card.ability.extra.x_chips = card.ability.extra.x_chips + card.ability.extra.bonus + return { + extra = { focus = card, message = localize("k_upgrade_ex") }, + card = card, + colour = G.C.CHIPS, + } + end + end + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "HexaCryonic" + }, + code = { + "Jevonn" + } + }, +} +local hunger = { + object_type = "Joker", + name = "cry-hunger", + key = "hunger", + config = { extra = { money = 3 } }, + pos = { x = 3, y = 0 }, + rarity = 2, + cost = 6, + order = 80, + blueprint_compat = true, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.money } } + end, + calculate = function(self, card, context) --This didn't work for Jevonn for some reason but it works for me :joker: + if context.using_consumeable then --shush + ease_dollars(card.ability.extra.money) + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { message = "$" .. card.ability.extra.money, colour = G.C.MONEY, } + ) + end + end, + cry_credits = { + idea = { + "Mjiojio" + }, + art = { + "AlexZGreat" + }, + code = { + "Jevonn" + } + }, +} +local weegaming = { + object_type = "Joker", + name = "cry-weegaming", + key = "weegaming", + order = 62, + config = { extra = { retriggers = 2 } }, + pos = { x = 3, y = 4 }, + atlas = "atlastwo", + rarity = 1, + cost = 5, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.retriggers } } + end, + calculate = function(self, card, context) + if context.repetition then + if context.cardarea == G.play then + local rank = SMODS.Ranks[context.other_card.base.value].key + if rank == "2" then + return { + message = localize("k_again_ex"), + repetitions = card.ability.extra.retriggers, + card = card, + } + end + end + end + end, + cry_credits = { + idea = { + "Gold" + }, + art = { + "Mystic Misclick" + }, + code = { + "Jevonn" + } + }, +} +local redbloon = { + object_type = "Joker", + name = "cry-redbloon", + key = "redbloon", + config = { extra = { money = 20, rounds_remaining = 2 } }, + pos = { x = 5, y = 1 }, + immutable = true, + rarity = 1, + cost = 4, + order = 97, + blueprint_compat = false, + eternal_compat = false, + perishable_compat = false, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.money, center.ability.extra.rounds_remaining } } + end, + calculate = function(self, card, context) + if + context.end_of_round + and not context.blueprint + and not context.individual + and not context.repetition + and not context.retrigger_joker + then + card.ability.extra.rounds_remaining = card.ability.extra.rounds_remaining - 1 + if card.ability.extra.rounds_remaining > 0 then + return { + message = { localize("cry_minus_round") }, + colour = G.C.FILTER, + } + else + ease_dollars(card.ability.extra.money) + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = "$" .. card.ability.extra.money, + colour = G.C.MONEY, + } + end + end + end, + cry_credits = { + idea = { + "Roguefort Cookie" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local apjoker = { + object_type = "Joker", + name = "cry-AP Joker", + key = "apjoker", + pos = { x = 2, y = 0 }, + config = { extra = { x_mult = 4 } }, + rarity = 2, + cost = 6, + order = 37, + blueprint_compat = true, + perishable_compat = false, + atlas = "atlasone", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.x_mult } } + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and G.GAME.blind.boss and not context.before and not context.after then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "HexaCryonic" + }, + code = { + "Jevonn" + } + }, +} +local maze = { + object_type = "Joker", + name = "cry-maze", + key = "maze", + pos = { x = 1, y = 1 }, + rarity = 1, + cost = 1, + order = 61, + immutable = true, + atlas = "atlastwo", + calculate = function(self, card, context) + if context.after and not context.blueprint and not context.retrigger_joker then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.GAME.current_round.hands_played = 0 + G.GAME.current_round.discards_used = 0 + return true + end, + })) + return true + end + if context.discard and not context.blueprint and not context.retrigger_joker then + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.GAME.current_round.hands_played = 0 + G.GAME.current_round.discards_used = 0 + return true + end, + })) + return true + end + end, + add_to_deck = function(self, card, from_debuff) + G.GAME.current_round.hands_played = 0 + G.GAME.current_round.discards_used = 0 + end, + cry_credits = { + idea = { + "zy-b-org" + }, + art = { + "Math" + }, + code = { + "Jevonn" + } + }, +} +--Fixed Jank for the most part. Other modded jokers may still be jank depending on how they are implemented +--funny side effect of this fix causes trading card and dna to juice up like craaazy lol +local panopticon = { + object_type = "Joker", + name = "cry-panopticon", + key = "panopticon", + pos = { x = 1, y = 4 }, + config = { + extra = {}, + }, + rarity = 1, + order = 47, + cost = 1, + immutable = true, + atlas = "atlastwo", + calculate = function(self, card, context) + if context.before and not context.blueprint and not context.retrigger_joker then + if not G.GAME.cry_panop_juggle then + G.GAME.cry_panop_juggle = G.GAME.current_round.hands_left + end + G.GAME.current_round.hands_left = 0 + end + if context.after and not context.blueprint and not context.retrigger_joker then + if G.GAME.cry_panop_juggle then + G.GAME.current_round.hands_left = G.GAME.cry_panop_juggle + G.GAME.cry_panop_juggle = nil + end + end + end, + cry_credits = { + idea = { + "Samario" + }, + art = { + "Samario" + }, + code = { + "Samario", "Toneblock" + } + }, +} +local magnet = { + object_type = "Joker", + name = "cry-magnet", + key = "magnet", + pos = { x = 4, y = 0 }, + config = { extra = { money = 2, Xmoney = 5, slots = 4 } }, + rarity = 1, + cost = 6, + order = 96, + blueprint_compat = false, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.money, center.ability.extra.Xmoney, center.ability.extra.slots } } + end, + atlas = "atlastwo", + calc_dollar_bonus = function(self, card) + if #G.jokers.cards <= card.ability.extra.slots then + return card.ability.extra.money * card.ability.extra.Xmoney + else + return card.ability.extra.money + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local unjust_dagger = { + object_type = "Joker", + name = "cry-Unjust Dagger", + key = "unjust_dagger", + pos = { x = 3, y = 0 }, + config = { extra = { x_mult = 1 } }, + rarity = 2, + cost = 8, + order = 102, + perishable_compat = false, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.x_mult } } + end, + atlas = "atlasone", + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (card.ability.extra.x_mult > 1) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + local my_pos = nil + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + my_pos = i + break + end + end + if + context.setting_blind + and not (context.blueprint_card or self).getting_sliced + and my_pos + and G.jokers.cards[my_pos - 1] + and not G.jokers.cards[my_pos - 1].ability.eternal + and not G.jokers.cards[my_pos - 1].getting_sliced + then + local sliced_card = G.jokers.cards[my_pos - 1] + sliced_card.getting_sliced = true + if sliced_card.config.center.rarity == "cry_exotic" then + check_for_unlock({ type = "what_have_you_done" }) + end + G.GAME.joker_buffer = G.GAME.joker_buffer - 1 + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.joker_buffer = 0 + card.ability.extra.x_mult = card.ability.extra.x_mult + sliced_card.sell_cost * 0.2 + card:juice_up(0.8, 0.8) + sliced_card:start_dissolve({ HEX("57ecab") }, nil, 1.6) + play_sound("slice1", 0.96 + math.random() * 0.08) + return true + end, + })) + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { card.ability.extra.x_mult + 0.2 * sliced_card.sell_cost }, + }), + colour = G.C.RED, + no_juice = true, + } + ) + return nil, true + end + end, + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Mystic Misclick" + }, + code = { + "Mystic Misclick" + } + }, +} +local monkey_dagger = { + object_type = "Joker", + name = "cry-Monkey Dagger", + key = "monkey_dagger", + pos = { x = 4, y = 3 }, + config = { extra = { chips = 0 } }, + rarity = 2, + cost = 6, + order = 49, + perishable_compat = false, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.chips } } + end, + atlas = "atlastwo", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.chips) > to_big(0)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + chip_mod = card.ability.extra.chips, + } + end + local my_pos = nil + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + my_pos = i + break + end + end + if + context.setting_blind + and not (context.blueprint_card or self).getting_sliced + and my_pos + and G.jokers.cards[my_pos - 1] + and not G.jokers.cards[my_pos - 1].ability.eternal + and not G.jokers.cards[my_pos - 1].getting_sliced + then + local sliced_card = G.jokers.cards[my_pos - 1] + sliced_card.getting_sliced = true + if sliced_card.config.center.rarity == "cry_exotic" then + check_for_unlock({ type = "what_have_you_done" }) + end + G.GAME.joker_buffer = G.GAME.joker_buffer - 1 + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.joker_buffer = 0 + card.ability.extra.chips = card.ability.extra.chips + sliced_card.sell_cost * 10 + card:juice_up(0.8, 0.8) + sliced_card:start_dissolve({ HEX("57ecab") }, nil, 1.6) + play_sound("slice1", 0.96 + math.random() * 0.08) + return true + end, + })) + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = localize({ + type = "variable", + key = "a_chips", + vars = { card.ability.extra.chips + 10 * sliced_card.sell_cost }, + }), + colour = G.C.CHIPS, + no_juice = true, + } + ) + return nil, true + end + end, + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Mystic Misclick" + }, + code = { + "Mystic Misclick" + } + }, +} +local pirate_dagger = { + object_type = "Joker", + name = "cry-Pirate Dagger", + key = "pirate_dagger", + pos = { x = 3, y = 3 }, + config = { extra = { x_chips = 1 } }, + rarity = 2, + cost = 8, + order = 103, + perishable_compat = false, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.x_chips } } + end, + atlas = "atlastwo", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (card.ability.extra.x_chips > 1) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xchips", vars = { card.ability.extra.x_chips } }), + Xchip_mod = card.ability.extra.x_chips, + } + end + local my_pos = nil + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + my_pos = i + break + end + end + if + context.setting_blind + and not (context.blueprint_card or self).getting_sliced + and my_pos + and G.jokers.cards[my_pos + 1] + and not G.jokers.cards[my_pos + 1].ability.eternal + and not G.jokers.cards[my_pos + 1].getting_sliced + then + local sliced_card = G.jokers.cards[my_pos + 1] + sliced_card.getting_sliced = true + if sliced_card.config.center.rarity == "cry_exotic" then + check_for_unlock({ type = "what_have_you_done" }) + end + G.GAME.joker_buffer = G.GAME.joker_buffer - 1 + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.joker_buffer = 0 + card.ability.extra.x_chips = card.ability.extra.x_chips + sliced_card.sell_cost * 0.25 + card:juice_up(0.8, 0.8) + sliced_card:start_dissolve({ HEX("57ecab") }, nil, 1.6) + play_sound("slice1", 0.96 + math.random() * 0.08) + return true + end, + })) + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = localize({ + type = "variable", + key = "a_xchips", + vars = { card.ability.extra.x_chips + 0.25 * sliced_card.sell_cost }, + }), + colour = G.C.CHIPS, + no_juice = true, + } + ) + return nil, true + end + end, + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Mystic Misclick" + }, + code = { + "Mystic Misclick" + } + }, +} +local mondrian = { + object_type = "Joker", + name = "cry-mondrian", + key = "mondrian", + pos = { x = 5, y = 3 }, + config = { extra = { extra = 0.25, x_mult = 1 } }, + rarity = 2, + cost = 7, + order = 44, + perishable_compat = false, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.extra, center.ability.extra.x_mult } } + end, + atlas = "atlastwo", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.x_mult) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + if + context.end_of_round + and G.GAME.current_round.discards_used == 0 + and not context.blueprint + and not context.individual + and not context.repetition + then + card.ability.extra.x_mult = card.ability.extra.x_mult + card.ability.extra.extra + return { + message = localize("k_upgrade_ex"), + card = card, + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local sapling = { + object_type = "Joker", + name = "cry-sapling", + key = "sapling", + pos = { x = 3, y = 2 }, + config = { extra = { score = 0, req = 18, check = nil } }, + immutable = true, + rarity = 2, + cost = 6, + order = 42, + blueprint_compat = false, + eternal_compat = false, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.score, center.ability.extra.req } } + end, + atlas = "atlasone", + calculate = function(self, card, context) + if + context.individual + and context.cardarea == G.play + and not context.blueprint + and not context.retrigger_joker + then + if context.other_card.ability.effect ~= "Base" then + card.ability.extra.score = card.ability.extra.score + 1 + if card.ability.extra.score >= card.ability.extra.req and not card.ability.extra.check then + card.ability.extra.check = true --Prevents violent juice up spam when playing enchanced cards while already active + local eval = function(card) return not card.REMOVED end + juice_card_until(card, eval, true) + end + end + elseif + context.selling_self + and not context.blueprint + and not context.retrigger_joker + then + if card.ability.extra.score >= card.ability.extra.req then + card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize('k_plus_joker'), colour = G.C.RARITY["cry_epic"]}) + local card = create_card("Joker", G.jokers, nil, cry_enable_epics and 'cry_epic' or 1, nil, nil, nil, "cry_sapling") + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + return nil, true + else + card_eval_status_text(card, 'extra', nil, nil, nil, {message = localize("k_nope_ex"), colour = G.C.RARITY["cry_epic"]}) + end + end + end, + cry_credits = { + idea = { + "Mjiojio" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local spaceglobe = { + object_type = "Joker", + name = "cry-spaceglobe", + key = "spaceglobe", + pos = { x = 1, y = 4 }, + config = { extra = { x_chips = 1, Xchipmod = 0.2, type = "High Card" } }, + rarity = 3, + cost = 8, + order = 73, + blueprint_compat = true, + perishable_compat = false, + loc_vars = function(self, info_queue, center) + return { + vars = { + center.ability.extra.x_chips, + center.ability.extra.Xchipmod, + localize(center.ability.extra.type, "poker_hands"), + }, + } + end, + atlas = "atlasone", + set_ability = function(self, card, initial, delay_sprites) + local _poker_hands = {} + for k, v in pairs(G.GAME.hands) do + if v.visible then _poker_hands[#_poker_hands+1] = k end + end + card.ability.extra.type = pseudorandom_element(_poker_hands, pseudoseed('cry_space_globe')) + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.blueprint then + if context.scoring_name == card.ability.extra.type then + G.E_MANAGER:add_event(Event({ + func = function() + local _type = {} + for k, v in pairs(G.GAME.hands) do + if v.visible and k ~= card.ability.extra.type then + _type[#_type + 1] = k + end + end + card.ability.extra.type = pseudorandom_element(_type, pseudoseed("cry_space_globe")) + return true + end, + })) + card.ability.extra.x_chips = card.ability.extra.x_chips + card.ability.extra.Xchipmod + return { + message = localize("k_upgrade_ex"), + card = card, + colour = G.C.CHIPS, + } + end + end + if + context.cardarea == G.jokers + and (to_big(card.ability.extra.x_chips) > to_big(1)) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xchips", vars = { number_format(card.ability.extra.x_chips) } }), + Xchip_mod = card.ability.extra.x_chips, + colour = G.C.CHIPS, + } + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local happy = { + object_type = "Joker", + name = "cry-happy", + key = "happy", + pos = { x = 2, y = 1 }, + rarity = 1, + cost = 2, + order = 63, + immutable = true, + blueprint_compat = true, + eternal_compat = false, + atlas = "atlastwo", + calculate = function(self, card, context) + if + context.selling_self + and #G.jokers.cards + G.GAME.joker_buffer <= G.jokers.config.card_limit + and not context.retrigger_joker + then + local sellcreatejoker = 1 + G.GAME.joker_buffer = G.GAME.joker_buffer + sellcreatejoker + G.E_MANAGER:add_event(Event({ + func = function() + for i = 1, sellcreatejoker do + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, nil, "happy") + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + G.GAME.joker_buffer = 0 + end + return true + end, + })) + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { message = localize("k_plus_joker"), colour = G.C.BLUE } + ) + return nil, true + end + if + context.end_of_round + and not context.individual + and not context.repetition + and #G.jokers.cards + G.GAME.joker_buffer < G.jokers.config.card_limit + and not context.retrigger_joker + then + local roundcreatejoker = math.min(1, G.jokers.config.card_limit - (#G.jokers.cards + G.GAME.joker_buffer)) + G.GAME.joker_buffer = G.GAME.joker_buffer + roundcreatejoker + G.E_MANAGER:add_event(Event({ + func = function() + if roundcreatejoker > 0 then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, nil, "happy") + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + G.GAME.joker_buffer = 0 + end + return true + end, + })) + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { message = localize("k_plus_joker"), colour = G.C.BLUE } + ) + return nil, true + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local meteor = { + object_type = "Joker", + name = "cry-meteor", + key = "meteor", + pos = { x = 0, y = 2 }, + config = { extra = { chips = 75 } }, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.foil) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_foil + end + return { vars = { center.ability.extra.chips } } + end, + rarity = 1, + cost = 4, + order = 38, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.other_joker + and context.other_joker.edition + and context.other_joker.edition.foil == true + and card ~= context.other_joker + then + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.extra.chips } }), + chip_mod = card.ability.extra.chips, + } + end + if context.individual and context.cardarea == G.play then + if context.other_card.edition and context.other_card.edition.foil == true then + return { + chips = card.ability.extra.chips, + colour = G.C.CHIPS, + card = card, + } + end + end + if + context.individual + and context.cardarea == G.hand + and context.other_card.edition + and context.other_card.edition.foil == true + and not context.end_of_round + then + if context.other_card.debuff then + return { + message = localize("k_debuffed"), + colour = G.C.RED, + card = card, + } + else + return { + chips = card.ability.extra.chips, --this doesn't exist yet :pensive: if only... + card = card + } + end + end + end, + atlas = "atlastwo", + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Jevonn" + } + }, +} +local exoplanet = { + object_type = "Joker", + name = "cry-exoplanet", + key = "exoplanet", + pos = { x = 1, y = 2 }, + config = { extra = { mult = 15 } }, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.holo) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_holo + end + return { vars = { center.ability.extra.mult } } + end, + rarity = 1, + order = 39, + cost = 3, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.other_joker + and context.other_joker.edition + and context.other_joker.edition.holo == true + and card ~= context.other_joker + then + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.extra.mult } }), + mult_mod = card.ability.extra.mult, + } + end + if context.individual and context.cardarea == G.play then + if context.other_card.edition and context.other_card.edition.holo == true then + return { + mult = card.ability.extra.mult, + colour = G.C.MULT, + card = card, + } + end + end + if + context.individual + and context.cardarea == G.hand + and context.other_card.edition + and context.other_card.edition.holo == true + and not context.end_of_round + then + if context.other_card.debuff then + return { + message = localize("k_debuffed"), + colour = G.C.RED, + card = card, + } + else + return { + h_mult = card.ability.extra.mult, + card = card + } + end + end + end, + atlas = "atlastwo", + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Jevonn" + } + }, +} +local stardust = { + object_type = "Joker", + name = "cry-stardust", + key = "stardust", + pos = { x = 2, y = 2 }, + config = { extra = { xmult = 2 } }, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.polychrome) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_polychrome + end + return { vars = { center.ability.extra.xmult } } + end, + rarity = 1, + cost = 2, + order = 40, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.other_joker + and context.other_joker.edition + and context.other_joker.edition.polychrome == true + and card ~= context.other_joker + then + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.xmult } }), + Xmult_mod = card.ability.extra.xmult, + } + end + if context.individual and context.cardarea == G.play then + if context.other_card.edition and context.other_card.edition.polychrome == true then + return { + x_mult = card.ability.extra.xmult, + colour = G.C.MULT, + card = card, + } + end + end + if + context.individual + and context.cardarea == G.hand + and context.other_card.edition + and context.other_card.edition.polychrome == true + and not context.end_of_round + then + if context.other_card.debuff then + return { + message = localize("k_debuffed"), + colour = G.C.RED, + card = card, + } + else + return { + x_mult = card.ability.extra.xmult, + card = card + } + end + end + end, + atlas = "atlastwo", + cry_credits = { + idea = { + "Mystic Misclick" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Jevonn" + } + }, +} +function rnjoker_randomize(card) + card.ability.abilities = {} + card.ability.extra = {} + card.ability.extra.value = {} + card.ability.extra.value_mod = {} + card.ability.extra.cond_value = {} + local values = {} + local contexts = { + "open_booster", + "buying_card", + "selling_self", + "selling_card", + "reroll_shop", + "ending_shop", + "skip_blind", + "skipping_booster", + "playing_card_added", + "first_hand_drawn", + "setting_blind", + "remove_playing_cards", + "using_consumeable", + "debuffed_hand", + "pre_discard", + "discard", + "end_of_round", + "individual_play", + "individual_hand_score", + "individual_hand_end", + "repetition_play", + "repetition_hand", + "other_joker", + "before", + "after", + "joker_main", + } + local stats = { + plus_mult = 2 + pseudorandom("rnj_mult1") * 28, + plus_chips = 4 + pseudorandom("rnj_chips1") * 196, + x_mult = 1 + pseudorandom("rnj_mult2") * 3, + x_chips = 1 + pseudorandom("rnj_chips2") * 3, + h_size = 1 + math.floor(pseudorandom("rnj_h_size") * 3), + money = 1 + math.floor(pseudorandom("rnj_money") * 5), + } + local actions = { + make_joker = 1, + make_tarot = 1 + math.min(2, math.floor(pseudorandom("rnj_tarot") * 2)), + make_planet = 1 + math.min(2, math.floor(pseudorandom("rnj_planet") * 2)), + make_spectral = 1, + add_dollars = 1 + math.min(4, math.floor(pseudorandom("rnj_dollars") * 5)), + } + local context = pseudorandom_element(contexts, pseudoseed("rnj_context")) + values.context = context + if context == "other_joker" or context == "joker_main" then + stats.h_size = nil + stats.money = nil + end + local stat_val, stat = pseudorandom_element(stats, pseudoseed("rnj_stat")) + local act_val, act = pseudorandom_element(actions, pseudoseed("rnj_stat")) + local scale = (pseudorandom("rnj_scale") > 0.5) + local is_stat = (pseudorandom("rnj_stat") > 0.5) + if context == "other_joker" or context == "joker_main" then + is_stat = true + scale = false + end + if + ((stat == "h_size") or (stat == "money")) + and (context == "individual_play" or context == "individual_hand_score" or context == "individual_hand_end") + and is_stat + then + scale = true + end + if context == "selling_self" then + is_stat = false + scale = false + end + if is_stat then + values.value = stat_val or 0 + values.stat = stat + if + scale + or ( + (context ~= "joker_main") + and (context ~= "other_joker") + and (context ~= "individual_play") + and (context ~= "individual_hand_score") + ) + then + values.value = ((stat == "x_mult") or (stat == "x_chips")) and 1 or 0 + scale = true + if stat == "plus_mult" then + values.scale_value = pseudorandom("rnj_scaling") * 10 + elseif stat == "plus_chips" then + values.scale_value = pseudorandom("rnj_scaling") * 50 + elseif stat == "h_size" then + values.scale_value = 1 + elseif stat == "money" then + values.scale_value = pseudorandom("rnj_scaling") * 4 + else + values.scale_value = pseudorandom("rnj_scaling") + end + end + else + scale = false + values.value = act_val + values.act = act + end + if pseudorandom("rnj_stat") < 0.8 then + local conds = {} + if context == "buying_card" then + conds = { + "buy_common", + "buy_uncommon", + "tarot", + "planet", + "spectral", + "odds", + } + elseif context == "selling_card" then + conds = { + "tarot", + "planet", + "spectral", + "joker", + "odds", + } + elseif context == "playing_card_added" then + conds = { + "suit", + "rank", + "face", + "odds", + } + elseif context == "setting_blind" then + conds = { + "boss", + "non_boss", + "small", + "big", + "odds", + } + elseif context == "remove_playing_cards" then + conds = { + "suit", + "rank", + "face", + "odds", + } + elseif context == "using_consumeable" then + conds = { + "tarot", + "planet", + "spectral", + "odds", + } + elseif context == "pre_discard" then + conds = { + "first_discard", + "last_discard", + "odds", + } + elseif context == "discard" then + conds = { + "suit", + "rank", + "face", + "odds", + } + elseif context == "individual_play" then + conds = { + "suit", + "rank", + "face", + "odds", + } + elseif context == "individual_hand_score" then + conds = { + "suit", + "rank", + "face", + "odds", + } + elseif context == "individual_hand_end" then + conds = { + "suit", + "rank", + "face", + "odds", + } + elseif context == "repetition_play" then + conds = { + "suit", + "rank", + "face", + "odds", + } + elseif context == "repetition_hand" then + conds = { + "suit", + "rank", + "face", + "odds", + } + elseif context == "other_joker" then + conds = { + "uncommon", + "rare", + "odds", + } + elseif context == "before" then + conds = { + "first", + "last", + "poker_hand", + "odds", + } + elseif context == "after" then + conds = { + "first", + "last", + "poker_hand", + "odds", + } + elseif context == "joker_main" then + conds = { + "first", + "last", + "poker_hand", + "or_more", + "or_less", + "odds", + } + elseif context == "cry_payout" then + conds = { + "hands_left", + "discards_left", + } + end + if #conds > 0 then + local cond = pseudorandom_element(conds, pseudoseed("rnj_stat")) + values.cond = cond + if cond == "poker_hand" then + local none, key = pseudorandom_element(G.GAME.hands, pseudoseed("rnj_poker-hand")) + values.cond_value = localize(key, "poker_hands") + values.poker_hand = key + end + if cond == "suit" then + local suit = pseudorandom_element(SMODS.Suits, pseudoseed("rnj_suit")) + values.cond_value = localize(suit.key, "suits_singular") + values.suit = suit.key + values.color = G.C.SUITS[suit.key] + if values.color == nil then + values.color = G.C.IMPORTANT + end + end + if cond == "rank" then + local rank = pseudorandom_element(SMODS.Ranks, pseudoseed("rnj_rank")) + values.cond_value = localize(rank.key, "ranks") + values.rank = rank.id + end + if (cond == "or_more") or (cond == "or_less") then + values.cond_value = math.min(5, math.floor(pseudorandom("rnj_cards") * 6)) + end + if (cond == "hands_left") or (cond == "discards_left") then + values.cond_value = math.min(3, math.floor(pseudorandom("rnj_cards") * 4)) + end + if cond == "odds" then + values.cond_value = 2 + math.min(3, math.floor(pseudorandom("rnj_cards") * 4)) + end + end + end + local loc_txt = "" + local extra_lines = { "" } + if (context ~= "repetition_play") and (context ~= "repetition_hand") then + if values.stat then + for i, j in ipairs(G.localization.misc.rnj_loc_txts.stats[values.stat]) do + if scale and (i == 1) then + loc_txt = loc_txt .. "Gains " + end + loc_txt = loc_txt .. j + end + end + if values.act then + for i, j in ipairs(G.localization.misc.rnj_loc_txts.actions[values.act]) do + loc_txt = loc_txt .. j + end + end + else + scale = false + values.stat = nil + values.act = nil + values.value = nil + values.scale_value = nil + end + loc_txt = loc_txt .. " " + if values.context then + for i, j in ipairs(G.localization.misc.rnj_loc_txts.contexts[values.context]) do + loc_txt = loc_txt .. j + end + end + if values.context ~= "joker_main" then + loc_txt = loc_txt .. " " + end + if values.cond then + for i, j in ipairs(G.localization.misc.rnj_loc_txts.conds[values.cond]) do + loc_txt = loc_txt .. j + end + end + if scale then + for i, j in ipairs(G.localization.misc.rnj_loc_txts.stats_inactive[values.stat]) do + table.insert(extra_lines, j) + end + end + if values.act and (values.act ~= "add_dollars") then + table.insert(extra_lines, "{C:inactive}(Must have room){}") + end + local accum = 0 + local lines = { "Randomize abilities each {C:attention}Ante{}" } + local in_brace = false + local cuur_str = "" + for i = 1, string.len(loc_txt) do + local char = string.sub(loc_txt, i, i) + if char == "{" then + in_brace = true + cuur_str = cuur_str .. char + elseif char == "}" then + in_brace = false + cuur_str = cuur_str .. char + elseif char == " " and (accum >= 25) then + table.insert(lines, cuur_str) + cuur_str = "" + accum = 0 + else + if not in_brace then + accum = accum + 1 + end + cuur_str = cuur_str .. char + end + end + if string.len(cuur_str) > 0 then + table.insert(lines, cuur_str) + end + if #extra_lines > 0 then + for i, j in ipairs(extra_lines) do + table.insert(lines, j) + end + end + values.loc_txt = lines + card.ability.extra = {} + if values.value then + values.value = math.floor(values.value * 100) / 100 + card.ability.extra.value = values.value + end + if values.scale_value then + values.scale_value = math.floor(values.scale_value * 100) / 100 + card.ability.extra.value_mod = values.scale_value + end + if values.cond_value then + card.ability.extra.cond_value = values.cond_value + end + if values.color then + card.ability.extra.color = values.color + end + local text_parsed = {} + for _, line in ipairs(values.loc_txt) do + text_parsed[#text_parsed + 1] = loc_parse_string(line) + end + values.text_parsed = text_parsed + card.ability.abilities = { values } +end +function localalize_with_direct(loc_target, args, misc_cat) + if loc_target then + for _, lines in + ipairs( + args.type == "unlocks" and loc_target.unlock_parsed + or args.type == "name" and loc_target.name_parsed + or (args.type == "text" or args.type == "tutorial" or args.type == "quips") and loc_target + or loc_target.text_parsed + ) + do + local final_line = {} + for _, part in ipairs(lines) do + local assembled_string = "" + for _, subpart in ipairs(part.strings) do + assembled_string = assembled_string + .. ( + type(subpart) == "string" and subpart + or format_ui_value(args.vars[tonumber(subpart[1])]) + or "ERROR" + ) + end + local desc_scale = G.LANG.font.DESCSCALE + if G.F_MOBILE_UI then + desc_scale = desc_scale * 1.5 + end + if args.type == "name" then + final_line[#final_line + 1] = { + n = G.UIT.O, + config = { + object = DynaText({ + string = { assembled_string }, + colours = { + (part.control.V and args.vars.colours[tonumber(part.control.V)]) + or (part.control.C and loc_colour(part.control.C)) + or G.C.UI.TEXT_LIGHT, + }, + bump = true, + silent = true, + pop_in = 0, + pop_in_rate = 4, + maxw = 5, + shadow = true, + y_offset = -0.6, + spacing = math.max(0, 0.32 * (17 - #assembled_string)), + scale = (0.55 - 0.004 * #assembled_string) + * (part.control.s and tonumber(part.control.s) or 1), + }), + }, + } + elseif part.control.E then + local _float, _silent, _pop_in, _bump, _spacing = nil, true, nil, nil, nil + if part.control.E == "1" then + _float = true + _silent = true + _pop_in = 0 + elseif part.control.E == "2" then + _bump = true + _spacing = 1 + end + final_line[#final_line + 1] = { + n = G.UIT.O, + config = { + object = DynaText({ + string = { assembled_string }, + colours = { + part.control.V and args.vars.colours[tonumber(part.control.V)] + or loc_colour(part.control.C or nil), + }, + float = _float, + silent = _silent, + pop_in = _pop_in, + bump = _bump, + spacing = _spacing, + scale = 0.32 * (part.control.s and tonumber(part.control.s) or 1) * desc_scale, + }), + }, + } + elseif part.control.X then + final_line[#final_line + 1] = { + n = G.UIT.C, + config = { + align = "m", + colour = loc_colour(part.control.X), + r = 0.05, + padding = 0.03, + res = 0.15, + }, + nodes = { + { + n = G.UIT.T, + config = { + text = assembled_string, + colour = loc_colour(part.control.C or nil), + scale = 0.32 * (part.control.s and tonumber(part.control.s) or 1) * desc_scale, + }, + }, + }, + } + else + final_line[#final_line + 1] = { + n = G.UIT.T, + config = { + detailed_tooltip = part.control.T + and (G.P_CENTERS[part.control.T] or G.P_TAGS[part.control.T]) + or nil, + text = assembled_string, + shadow = args.shadow, + colour = part.control.V and args.vars.colours[tonumber(part.control.V)] + or loc_colour(part.control.C or nil, args.default_col), + scale = 0.32 * (part.control.s and tonumber(part.control.s) or 1) * desc_scale, + }, + } + end + end + if args.type == "name" or args.type == "text" then + return final_line + end + args.nodes[#args.nodes + 1] = final_line + end + end +end +local rnjoker = { + object_type = "Joker", + name = "cry-rnjoker Joker", --:balatrojoker: + key = "rnjoker", + pos = { x = 5, y = 4 }, + config = {}, + order = 59, + loc_vars = function(self, info_queue, card) + local vars = { + vars = { + (card.ability.extra and card.ability.extra.value_mod and card.ability.extra.value) or 0, + (card.ability.extra and card.ability.extra.value and card.ability.extra.value_mod) + or (card.ability.extra and card.ability.extra.value) + or 0, + card.ability.extra and card.ability.extra.cond_value or 0, + G.GAME and G.GAME.probabilities.normal or 1, + }, + } + if card.ability.extra and card.ability.extra.color then + vars.vars.colours = { card.ability.extra.color } + end + return vars + end, + rarity = 2, + cost = 6, + blueprint_compat = true, + set_ability = function(self, card, initial, delay_sprites) + card.ability.abilities = {} + rnjoker_randomize(card) + end, + calculate = function(self, card, context) + if card.ability and card.ability.abilities then + for i, j in ipairs(card.ability.abilities) do + local j_context = j.context + local indiv = false + if j_context ~= "cry_payout" then + local valid_context = not not context[j.context] + if j.scale_value and context.blueprint then + valid_context = false + end + if (j_context == "playing_card_added") and card.getting_sliced then + valid_context = false + end + if (j_context == "setting_blind") and card.getting_sliced then + valid_context = false + end + if (j_context == "setting_blind") and card.getting_sliced then + valid_context = false + end + if (j_context == "before") and (card.area ~= G.jokers) then + valid_context = false + end + if (j_context == "after") and (card.area ~= G.jokers) then + valid_context = false + end + if (j_context == "joker_main") and (card.area ~= G.jokers) then + valid_context = false + end + if (j_context == "end_of_round") and (context.repetition or context.individual) then + valid_context = false + end + if + (j_context == "individual_play") + and context.individual + and not context.repetition + and (context.cardarea == G.play) + then + valid_context = true + indiv = true + end + if + (j_context == "individual_hand_score") + and context.individual + and not context.repetition + and (context.cardarea == G.hand) + and not context.end_of_round + then + valid_context = true + indiv = true + end + if + (j_context == "individual_hand_end") + and context.individual + and not context.repetition + and (context.cardarea == G.hand and context.end_of_round) + then + valid_context = true + indiv = true + end + if (j_context == "repetition_play") and context.repetition and (context.cardarea == G.play) then + valid_context = true + end + if (j_context == "repetition_hand") and context.repetition and (context.cardarea == G.hand) then + valid_context = true + end + if valid_context then + local cond_passed = false + local times_passed = 1 + if j.cond then + if j.cond == "buy_common" then + if + context.card + and context.card.ability + and (context.card.ability.set == "Joker") + and (context.card.config.center.rarity == 1) + then + cond_passed = true + end + elseif j.cond == "buy_uncommon" then + if + context.card + and context.card.ability + and (context.card.ability.set == "Joker") + and (context.card.config.center.rarity == 2) + then + cond_passed = true + end + elseif j.cond == "tarot" then + local card = context.card or context.consumeable + if card and card.ability and (card.ability.set == "Tarot") then + cond_passed = true + end + elseif j.cond == "planet" then + local card = context.card or context.consumeable + if card and card.ability and (card.ability.set == "Planet") then + cond_passed = true + end + elseif j.cond == "spectral" then + local card = context.card or context.consumeable + if card and card.ability and (card.ability.set == "Spectral") then + cond_passed = true + end + elseif j.cond == "joker" then + if context.card and context.card.ability and (context.card.ability.set == "Joker") then + cond_passed = true + end + elseif j.cond == "suit" then + times_passed = 0 + local cards = context.cards + if cards == nil then + cards = context.removed + end + if cards == nil then + cards = { context.other_card } + end + for i2, j2 in ipairs(cards) do + if j2:is_suit(j.suit) then + cond_passed = true + times_passed = times_passed + 1 + end + end + elseif j.cond == "rank" then + times_passed = 0 + local cards = context.cards + if cards == nil then + cards = context.removed + end + if cards == nil then + cards = { context.other_card } + end + for i2, j2 in ipairs(cards) do + if j2:get_id() == j.rank then + cond_passed = true + times_passed = times_passed + 1 + end + end + elseif j.cond == "face" then + times_passed = 0 + local cards = context.cards + if cards == nil then + cards = context.removed + end + if cards == nil then + cards = { context.other_card } + end + for i2, j2 in ipairs(cards) do + if j2:is_face() then + cond_passed = true + times_passed = times_passed + 1 + end + end + elseif j.cond == "boss" then + if context.blind.boss and not (context.blind.config and context.blind.config.bonus) then + cond_passed = true + end + elseif j.cond == "non_boss" then + if context.blind and not G.GAME.blind.boss then + cond_passed = true + end + elseif j.cond == "small" then + if context.blind == G.P_BLINDS.bl_small then + cond_passed = true + end + elseif j.cond == "big" then + if context.blind == G.P_BLINDS.bl_big then + cond_passed = true + end + elseif j.cond == "first" then + if G.GAME.current_round.hands_played == 0 then + cond_passed = true + end + elseif j.cond == "last" then + if G.GAME.current_round.hands_left == 0 then + cond_passed = true + end + elseif j.cond == "common" then + if context.other_joker.config.center.rarity == 1 then + cond_passed = true + end + elseif j.cond == "uncommon" then + if context.other_joker.config.center.rarity == 2 then + cond_passed = true + end + elseif j.cond == "rare" then + if context.other_joker.config.center.rarity == 3 then + cond_passed = true + end + elseif j.cond == "poker_hand" then + if context.poker_hands~= nil and next(context.poker_hands[j.poker_hand]) then + cond_passed = true + end + elseif j.cond == "or_more" then + if #context.full_hand >= j.cond_value then + cond_passed = true + end + elseif j.cond == "or_less" then + if #context.full_hand <= j.cond_value then + cond_passed = true + end + elseif j.cond == "hands_left" then + if G.GAME.current_round.hands_left == j.cond_value then + cond_passed = true + end + elseif j.cond == "discards_left" then + if G.GAME.current_round.discards_left == j.cond_value then + cond_passed = true + end + elseif j.cond == "first_discard" then + if G.GAME.current_round.discards_used <= 0 then + cond_passed = true + end + elseif j.cond == "last_discard" then + if G.GAME.current_round.discards_left <= 1 then + cond_passed = true + end + elseif j.cond == "odds" then + if + pseudorandom("rnj") + < ((G.GAME and G.GAME.probabilities.normal or 1) / card.ability.extra.cond_value) + then + cond_passed = true + end + end + else + cond_passed = true + end + if cond_passed then + if j.context == "other_joker" then + local stats = { + plus_mult = "a_mult", + plus_chips = "a_chips", + x_mult = "a_xmult", + x_chips = "a_xchips", + } + local mods = { + plus_chips = "chip_mod", + plus_mult = "mult_mod", + x_mult = "Xmult_mod", + x_chips = "Xchip_mod", + } + local table = {} + table.message = + localize({ type = "variable", key = stats[j.stat], vars = { + card.ability.extra.value, + } }) + table[mods[j.stat]] = card.ability.extra.value + table.card = card + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + return table + elseif (j.context == "repetition_play") or (j.context == "repetition_hand") then + return { + message = localize("k_again_ex"), + repetitions = 1, + card = card, + } + elseif j.scale_value then + card.ability.extra.value = card.ability.extra.value + + (card.ability.extra.value_mod * times_passed) + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex") } + ) + if j.stat == "h_size" then + G.hand:change_size(card.ability.extra.value_mod) + end + elseif j.act then + local j_mod = 0 + if j.context == "selling_self" and (card.ability.set == "Joker") then + j_mod = 1 + end + if j.context == "selling_card" and (context.card.ability.set == "Joker") then + j_mod = 1 + end + local c_mod = 0 + if j.context == "selling_self" and card.ability.consumeable then + c_mod = 1 + end + if j.context == "selling_card" and card.ability.consumeable then + c_mod = 1 + end + if j.act == "make_joker" then + local amount = card.ability.extra.value * times_passed + if + (G.jokers.config.card_limit + j_mod - #G.jokers.cards - G.GAME.joker_buffer) + < amount + then + amount = G.jokers.config.card_limit + + j_mod + - #G.jokers.cards + - G.GAME.joker_buffer + end + if amount > 0 then + G.GAME.joker_buffer = G.GAME.joker_buffer + amount + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.0, + func = function() + for i = 1, amount do + if G.jokers.config.card_limit + j_mod > #G.jokers.cards then + local card = create_card( + "Joker", + G.jokers, + nil, + nil, + nil, + nil, + nil, + "rnj" + ) + card:add_to_deck() + G.jokers:emplace(card) + else + break + end + end + G.GAME.joker_buffer = 0 + return true + end, + })) + end + elseif j.act == "make_tarot" then + local amount = card.ability.extra.value * times_passed + if + ( + G.consumeables.config.card_limit + + c_mod + - #G.consumeables.cards + - G.GAME.consumeable_buffer + ) < amount + then + amount = G.consumeables.config.card_limit + + c_mod + - #G.consumeables.cards + - G.GAME.consumeable_buffer + end + if amount > 0 then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + amount + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.0, + func = function() + for i = 1, amount do + if + G.consumeables.config.card_limit + c_mod > #G.consumeables.cards + then + local card = create_card( + "Tarot", + G.consumeables, + nil, + nil, + nil, + nil, + nil, + "rnj" + ) + card:add_to_deck() + G.consumeables:emplace(card) + else + break + end + end + G.GAME.consumeable_buffer = 0 + return true + end, + })) + end + elseif j.act == "make_planet" then + local amount = card.ability.extra.value * times_passed + if + ( + G.consumeables.config.card_limit + + c_mod + - #G.consumeables.cards + - G.GAME.consumeable_buffer + ) < amount + then + amount = G.consumeables.config.card_limit + + c_mod + - #G.consumeables.cards + - G.GAME.consumeable_buffer + end + if amount > 0 then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + amount + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.0, + func = function() + for i = 1, amount do + if + G.consumeables.config.card_limit + c_mod > #G.consumeables.cards + then + local card = create_card( + "Planet", + G.consumeables, + nil, + nil, + nil, + nil, + nil, + "rnj" + ) + card:add_to_deck() + G.consumeables:emplace(card) + else + break + end + end + G.GAME.consumeable_buffer = 0 + return true + end, + })) + end + elseif j.act == "make_spectral" then + local amount = card.ability.extra.value * times_passed + if + ( + G.consumeables.config.card_limit + + c_mod + - #G.consumeables.cards + - G.GAME.consumeable_buffer + ) < amount + then + amount = G.consumeables.config.card_limit + + c_mod + - #G.consumeables.cards + - G.GAME.consumeable_buffer + end + if amount > 0 then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + amount + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.0, + func = function() + for i = 1, amount do + if + G.consumeables.config.card_limit + c_mod > #G.consumeables.cards + then + local card = create_card( + "Spectral", + G.consumeables, + nil, + nil, + nil, + nil, + nil, + "rnj" + ) + card:add_to_deck() + G.consumeables:emplace(card) + else + break + end + end + G.GAME.consumeable_buffer = 0 + return true + end, + })) + end + elseif j.act == "add_dollars" then + ease_dollars(card.ability.extra.value) + return { + message = localize("$") .. card.ability.extra.value, + colour = G.C.MONEY, + card = card, + } + end + end + end + end + if j.stat and context.individual and indiv then + local cond_passed = false + if j.cond == "suit" then + if context.other_card:is_suit(j.suit) then + cond_passed = true + end + elseif j.cond == "rank" then + if context.other_card:get_id() == j.rank then + cond_passed = true + end + elseif j.cond == "face" then + if context.other_card:is_face() then + cond_passed = true + end + elseif j.cond == "odds" then + if + pseudorandom("rnj") + < ((G.GAME and G.GAME.probabilities.normal or 1) / card.ability.extra.cond_value) + then + cond_passed = true + end + end + if not j.cond then + cond_passed = true + end + if cond_passed then + if (context.cardarea == G.hand) and context.other_card.debuff then + return { + message = localize("k_debuffed"), + colour = G.C.RED, + card = card, + } + end + if j.scale_value then + card.ability.extra.value = card.ability.extra.value + card.ability.extra.value_mod + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex") } + ) + if j.stat == "h_size" then + G.hand:change_size(card.ability.extra.value_mod) + end + else + local stats = { + plus_mult = "mult", + plus_chips = "chips", + } + if context.cardarea == G.hand then + local stats = { + plus_mult = "h_mult", + plus_chips = "h_chips", + } + end + local stat = stats[j.stat] or j.stat + local colors = { + plus_mult = G.C.RED, + plus_chips = G.C.BLUE, + x_mult = G.C.RED, + x_chips = G.C.BLUE, + } + local table = { + card = card, + } + table.colour = colors[j.stat] + table[stat] = card.ability.extra.value + return table + end + end + end + if context.joker_main and j.stat and (j.stat ~= "h_size") and (j.stat ~= "money") then + local cond_passed = false + if j_context ~= "joker_main" then + cond_passed = true + end + if j.cond == "first" then + if G.GAME.current_round.hands_played == 0 then + cond_passed = true + end + elseif j.cond == "last" then + if G.GAME.current_round.hands_left == 0 then + cond_passed = true + end + elseif j.cond == "poker_hand" then + if context.poker_hands~= nil and next(context.poker_hands[j.poker_hand]) then + cond_passed = true + end + elseif j.cond == "or_more" then + if #context.full_hand >= j.cond_value then + cond_passed = true + end + elseif j.cond == "or_less" then + if #context.full_hand <= j.cond_value then + cond_passed = true + end + elseif j.cond == "odds" then + if + pseudorandom("rnj") + < ((G.GAME and G.GAME.probabilities.normal or 1) / card.ability.extra.cond_value) + then + cond_passed = true + end + end + if not j.cond then + cond_passed = true + end + if cond_passed then + local stats = { + plus_mult = "a_mult", + plus_chips = "a_chips", + x_mult = "a_xmult", + x_chips = "a_xchips", + } + local mods = { + plus_mult = "mult_mod", + plus_chips = "chip_mod", + x_mult = "Xmult_mod", + x_chips = "Xchip_mod", + } + local table = {} + table.message = localize({ + type = "variable", + key = stats[j.stat], + vars = { card.ability.extra.value }, + }) + table[mods[j.stat]] = card.ability.extra.value + return table + end + end + end + end + end + if + not context.individual + and not context.repetition + and not card.debuff + and context.end_of_round + and not context.blueprint + and G.GAME.blind.boss + and not (G.GAME.blind.config and G.GAME.blind.config.bonus) + then + local hand_size = 0 + if card.ability and card.ability.abilities then + for i, j in ipairs(card.ability.abilities) do + if j.stat == "h_size" then + hand_size = hand_size + card.ability.extra.value + end + end + end + G.hand:change_size(-hand_size) + rnjoker_randomize(card) + return { + message = localize("k_reset"), + colour = G.C.RED, + } + end + end, + add_to_deck = function(self, card, from_debuff) + local hand_size = 0 + if card.ability and card.ability.abilities then + for i, j in ipairs(card.ability.abilities) do + if j.stat == "h_size" then + hand_size = hand_size + card.ability.extra.value + end + end + end + G.hand:change_size(hand_size) + end, + remove_from_deck = function(self, card, from_debuff) + local hand_size = 0 + if card.ability and card.ability.abilities then + for i, j in ipairs(card.ability.abilities) do + if j.stat == "h_size" then + hand_size = hand_size + card.ability.extra.value + end + end + end + G.hand:change_size(-hand_size) + end, + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + local len = ( + card.ability + and card.ability.abilities + and card.ability.abilities[1].loc_txt + and #card.ability.abilities[1].loc_txt + ) or 0 + local target = { + type = "descriptions", + key = self.key, + set = self.set, + nodes = desc_nodes, + vars = specific_vars or {}, + } + if self.loc_vars and type(self.loc_vars) == "function" then + res = self:loc_vars(info_queue, card) or {} + target.vars = res.vars or target.vars + target.key = res.key or target.key + end + local new_loc = { text = {} } + if + card.ability + and card.ability.abilities + and card.ability.abilities[1].loc_txt + and #card.ability.abilities[1].loc_txt + then + for i, j in ipairs(card.ability.abilities[1].loc_txt) do + table.insert(new_loc.text, j) + end + new_loc.text_parsed = card.ability.abilities[1].text_parsed + end + if not full_UI_table.name then + full_UI_table.name = + localize({ type = "name", set = self.set, key = target.key or self.key, nodes = full_UI_table.name }) + end + if specific_vars and specific_vars.debuffed then + target = { + type = "other", + key = "debuffed_" .. (specific_vars.playing_card and "playing_card" or "default"), + nodes = desc_nodes, + } + localize(target) + else + localalize_with_direct(new_loc, target) + end + end, + calc_dollar_bonus = function(self, card) + if card.ability and card.ability.abilities then + for i, j in ipairs(card.ability.abilities) do + if j.stat == "money" then + if card.ability.extra.value > 0 then + return card.ability.extra.value + end + end + end + end + end, + atlas = "atlastwo", + cry_credits = { + idea = { + "Mathguy" + }, + art = { + "Mathguy" + }, + code = { + "Mathguy" + } + }, +} +local duos = { + object_type = "Joker", + name = "cry-duos", + key = "duos", + order = 90, + pos = { x = 0, y = 0 }, + config = { Xmult = 2.5, type = "Two Pair" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands~= nil and next(context.poker_hands["Two Pair"]) or context.poker_hands~= nil and next(context.poker_hands["Full House"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + cry_credits = { + idea = { + "Mathguy" + }, + art = { + "Mathguy" + }, + code = { + "Mathguy" + } + }, +} +local home = { + object_type = "Joker", + name = "cry-home", + key = "home", + order = 91, + pos = { x = 2, y = 0 }, + config = { Xmult = 3.5, type = "Full House" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands~= nil and next(context.poker_hands["Full House"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + cry_credits = { + idea = { + "Mathguy" + }, + art = { + "Mathguy" + }, + code = { + "Mathguy" + } + }, +} +local nuts = { + object_type = "Joker", + name = "cry-nuts", + key = "nuts", + order = 92, + pos = { x = 1, y = 0 }, + config = { Xmult = 5, type = "Straight Flush" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands ~= nil and next(context.poker_hands["Straight Flush"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + cry_credits = { + idea = { + "Mathguy" + }, + art = { + "Mathguy" + }, + code = { + "Mathguy" + } + }, +} +local quintet = { + object_type = "Joker", + name = "cry-quintet", + key = "quintet", + order = 93, + pos = { x = 3, y = 0 }, + config = { Xmult = 5, type = "Five of a Kind" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands~= nil and next(context.poker_hands["Five of a Kind"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + in_pool = function(self) + if G.GAME.hands["Five of a Kind"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Mathguy" + }, + art = { + "Mathguy" + }, + code = { + "Mathguy" + } + }, +} +local unity = { + object_type = "Joker", + name = "cry-unity", + key = "unity", + order = 94, + pos = { x = 4, y = 0 }, + config = { Xmult = 9, type = "Flush House" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands~= nil and next(context.poker_hands["Flush House"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + in_pool = function(self) + if G.GAME.hands["Flush House"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Mathguy" + }, + art = { + "Mathguy" + }, + code = { + "Mathguy" + } + }, +} +local swarm = { + object_type = "Joker", + name = "cry-swarm", + key = "swarm", + order = 95, + pos = { x = 5, y = 0 }, + config = { Xmult = 9, type = "Flush Five" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands~= nil and next(context.poker_hands["Flush Five"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + in_pool = function(self) + if G.GAME.hands["Flush Five"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Mathguy" + }, + art = { + "Mathguy" + }, + code = { + "Mathguy" + } + }, +} +local stronghold = { + object_type = "Joker", + name = "cry-stronghold", + key = "stronghold", + order = 119, + pos = { x = 8, y = 4 }, + config = { Xmult = 5, type = "cry_Bulwark" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize("cry_hand_bulwark") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands~= nil and next(context.poker_hands["cry_Bulwark"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + in_pool = function(self) + if G.GAME.hands["cry_Bulwark"].played > 0 then + return true + end + return false + end, +} +local wtf = { + object_type = "Joker", + name = "cry-wtf", + key = "wtf", + order = 120, + pos = { x = 7, y = 1 }, + config = { Xmult = 10, type = "cry_Clusterfuck" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize("cry_hand_clusterfuck") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands~= nil and next(context.poker_hands["cry_Clusterfuck"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + in_pool = function(self) + if G.GAME.hands["cry_Clusterfuck"].played > 0 then + return true + end + return false + end, +} +local clash = { + object_type = "Joker", + name = "cry-clash", + key = "clash", + order = 121, + pos = { x = 8, y = 1 }, + config = { Xmult = 12, type = "cry_UltPair" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize("cry_hand_ultpair") } } + end, + atlas = "atlasthree", + rarity = 3, + cost = 8, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (to_big(card.ability.x_mult) > to_big(1)) + and not context.before + and not context.after + then + if context.poker_hands~= nil and next(context.poker_hands["cry_UltPair"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end + end, + in_pool = function(self) + if G.GAME.hands["cry_UltPair"].played > 0 then + return true + end + return false + end, +} +local filler = { + object_type = "Joker", + name = "cry-filler", + key = "filler", + pos = { x = 0, y = 1 }, + pools = {["Meme"] = true}, + config = { Xmult = 1.00000000000001, type = "High Card" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.x_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 3, + order = 89, + cost = 1, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["High Card"]) then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + colour = G.C.RED, + Xmult_mod = card.ability.x_mult, + } + end + end, + cry_credits = { + idea = { + "Mathguy" + }, + art = { + "Mathguy" + }, + code = { + "Mathguy" + } + }, +} +local giggly = { + object_type = "Joker", + name = "cry-Giggly Joker", + key = "giggly", + effect = "Cry Type Mult", + pos = { x = 0, y = 5 }, + config = { t_mult = 4, type = "High Card" }, + order = 16, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 1, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["High Card"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local nutty = { + object_type = "Joker", + name = "cry-Nutty Joker", + key = "nutty", + effect = "Cry Type Mult", + pos = { x = 1, y = 5 }, + order = 17, + config = { t_mult = 19, type = "Four of a Kind" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Four of a Kind"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local manic = { + object_type = "Joker", + name = "cry-Manic Joker", + key = "manic", + effect = "Cry Type Mult", + pos = { x = 2, y = 5 }, + order = 18, + config = { t_mult = 22, type = "Straight Flush" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Straight Flush"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local silly = { + object_type = "Joker", + name = "cry-Silly Joker", + key = "silly", + pos = { x = 3, y = 5 }, + effect = "Cry Type Mult", + order = 19, + config = { t_mult = 16, type = "Full House" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Full House"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local delirious = { + object_type = "Joker", + name = "cry-Delirious Joker", + key = "delirious", + effect = "Cry Type Mult", + pos = { x = 4, y = 5 }, + order = 20, + config = { t_mult = 22, type = "Five of a Kind" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Five of a Kind"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + in_pool = function(self) + if G.GAME.hands["Five of a Kind"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local wacky = { + object_type = "Joker", + name = "cry-Wacky Joker", + key = "wacky", + pos = { x = 5, y = 5 }, + order = 21, + config = { t_mult = 30, type = "Flush House" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + effect = "Cry Type Mult", + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Flush House"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + in_pool = function(self) + if G.GAME.hands["Flush House"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local kooky = { + object_type = "Joker", + name = "cry-Kooky Joker", + key = "kooky", + pos = { x = 6, y = 5 }, + order = 22, + config = { t_mult = 30, type = "Flush Five" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + effect = "Cry Type Mult", + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Flush Five"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + in_pool = function(self) + if G.GAME.hands["Flush Five"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} + +local bonkers = { + object_type = "Joker", + name = "cry-Bonkers Joker", + key = "bonkers", + pos = { x = 8, y = 5 }, + order = 113, + config = { t_mult = 20, type = "cry_Bulwark" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize("cry_hand_bulwark") } } + end, + atlas = "atlasthree", + rarity = 1, + effect = "Cry Type Mult", + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["cry_Bulwark"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + in_pool = function(self) + if G.GAME.hands["cry_Bulwark"].played > 0 then + return true + end + return false + end, +} + +local fuckedup = { + object_type = "Joker", + name = "cry-Fucked-Up Joker", + key = "fuckedup", + pos = { x = 7, y = 2 }, + order = 114, + config = { t_mult = 37, type = "cry_Clusterfuck" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize("cry_hand_clusterfuck") } } + end, + atlas = "atlasthree", + rarity = 1, + effect = "Cry Type Mult", + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["cry_Clusterfuck"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + in_pool = function(self) + if G.GAME.hands["cry_Clusterfuck"].played > 0 then + return true + end + return false + end, +} + +local foolhardy = { + object_type = "Joker", + name = "cry-Foolhardy Joker", + key = "foolhardy", + pos = { x = 8, y = 2 }, + order = 115, + config = { t_mult = 42, type = "cry_UltPair" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_mult, localize("cry_hand_ultpair") } } + end, + atlas = "atlasthree", + rarity = 1, + effect = "Cry Type Mult", + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["cry_UltPair"]) then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.t_mult } }), + colour = G.C.RED, + mult_mod = card.ability.t_mult, + } + end + end, + in_pool = function(self) + if G.GAME.hands["cry_UltPair"].played > 0 then + return true + end + return false + end, +} + +local dubious = { + object_type = "Joker", + name = "cry-Dubious Joker", + key = "dubious", + pos = { x = 0, y = 6 }, + order = 24, + config = { t_chips = 20, type = "High Card" }, + effect = "Cry Type Chips", + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 1, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["High Card"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local shrewd = { + object_type = "Joker", + name = "cry-Shrewd Joker", + key = "shrewd", + pos = { x = 1, y = 6 }, + order = 25, + effect = "Cry Type Chips", + config = { t_chips = 150, type = "Four of a Kind" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Four of a Kind"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local tricksy = { + object_type = "Joker", + name = "cry-Tricksy Joker", + key = "tricksy", + effect = "Cry Type Chips", + order = 26, + pos = { x = 2, y = 6 }, + config = { t_chips = 170, type = "Straight Flush" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Straight Flush"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local foxy = { + object_type = "Joker", + name = "cry-Foxy Joker", + key = "foxy", + pos = { x = 3, y = 6 }, + order = 27, + effect = "Cry Type Chips", + config = { t_chips = 130, type = "Full House" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Full House"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local savvy = { + object_type = "Joker", + name = "cry-Savvy Joker", + key = "savvy", + pos = { x = 4, y = 6 }, + effect = "Cry Type Chips", + order = 28, + config = { t_chips = 170, type = "Five of a Kind" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Five of a Kind"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + in_pool = function(self) + if G.GAME.hands["Five of a Kind"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local subtle = { + object_type = "Joker", + name = "cry-Subtle Joker", + key = "subtle", + pos = { x = 5, y = 6 }, + effect = "Cry Type Chips", + order = 29, + config = { t_chips = 240, type = "Flush House" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Flush House"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + in_pool = function(self) + if G.GAME.hands["Flush House"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local discreet = { + object_type = "Joker", + name = "cry-Discreet Joker", + key = "discreet", + pos = { x = 6, y = 6 }, + effect = "Cry Type Chips", + order = 30, + config = { t_chips = 240, type = "Flush Five" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize(card.ability.type, "poker_hands") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["Flush Five"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + in_pool = function(self) + if G.GAME.hands["Flush Five"].played > 0 then + return true + end + return false + end, + cry_credits = { + idea = { + "Luigicat11" + }, + art = { + "Luigicat11" + }, + code = { + "Math" + } + }, +} +local adroit = { + object_type = "Joker", + name = "cry-Adroit Joker", + key = "adroit", + pos = { x = 7, y = 4 }, + effect = "Cry Type Chips", + order = 116, + config = { t_chips = 170, type = "cry_Bulwark" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize("cry_hand_bulwark") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["cry_Bulwark"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + in_pool = function(self) + if G.GAME.hands["cry_Bulwark"].played > 0 then + return true + end + return false + end, +} +local penetrating = { + object_type = "Joker", + name = "cry-Penetrating Joker", + key = "penetrating", + pos = { x = 7, y = 3 }, + effect = "Cry Type Chips", + order = 117, + config = { t_chips = 270, type = "cry_Clusterfuck" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize("cry_hand_clusterfuck") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["cry_Clusterfuck"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + in_pool = function(self) + if G.GAME.hands["cry_Clusterfuck"].played > 0 then + return true + end + return false + end, +} +local treacherous = { + object_type = "Joker", + name = "cry-Treacherous Joker", + key = "treacherous", + pos = { x = 8, y = 3 }, + effect = "Cry Type Chips", + order = 118, + config = { t_chips = 300, type = "cry_UltPair" }, + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.t_chips, localize("cry_hand_ultpair") } } + end, + atlas = "atlasthree", + rarity = 1, + cost = 4, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after and context.poker_hands and next(context.poker_hands["cry_UltPair"]) then + return { + message = localize({ type = "variable", key = "a_chips", vars = { card.ability.t_chips } }), + colour = G.C.BLUE, + chip_mod = card.ability.t_chips, + } + end + end, + in_pool = function(self) + if G.GAME.hands["cry_UltPair"].played > 0 then + return true + end + return false + end, +} +local coin = { + object_type = "Joker", + name = "cry-coin", + key = "coin", + pos = { x = 0, y = 2 }, + config = { extra = { money = 1 } }, + rarity = 1, + order = 53, + cost = 5, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.money, center.ability.extra.money * 10 } } + end, + atlas = "atlasthree", + calculate = function(self, card, context) + if context.selling_card and context.card.ability.set == "Joker" then + local option = pseudorandom(pseudoseed("coin"), card.ability.extra.money, card.ability.extra.money * 10) + ease_dollars(option) + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { message = localize("$") .. option, colour = G.C.MONEY, delay = 0.45 } + ) + return nil, true + end + end, + cry_credits = { + idea = { + "Squiddy" + }, + art = { + "Timetoexplode" + }, + code = { + "Jevonn" + } + }, +} +local wheelhope = { + object_type = "Joker", + name = "cry-wheelhope", + key = "wheelhope", + pos = { x = 1, y = 1 }, + config = { extra = { extra = 0.5, x_mult = 1 } }, + rarity = 2, + cost = 5, + order = 74, + perishable_compat = false, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = G.P_CENTERS.c_wheel_of_fortune + return { vars = { center.ability.extra.extra, center.ability.extra.x_mult } } + end, + atlas = "atlasthree", + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (card.ability.extra.x_mult > 1) + and not context.before + and not context.after + then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }), + Xmult_mod = card.ability.extra.x_mult, + } + end + if context.consumeable then + if + context.consumeable.ability.name == "The Wheel of Fortune" + and not context.consumeable.cry_wheel_success + then + card.ability.extra.x_mult = card.ability.extra.x_mult + card.ability.extra.extra + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.extra.x_mult } }) } + ) + return nil, true + end + end + end, + cry_credits = { + idea = { + "Linus Goof Balls" + }, + art = { + "Linus Good Balls" + }, + code = { + "Toneblock" + } + }, +} +local oldblueprint = { + object_type = "Joker", + name = "cry-oldblueprint", + key = "oldblueprint", + pos = { x = 2, y = 1 }, + config = { extra = { odds = 4 } }, + rarity = 1, + cost = 6, + order = 83, + loc_vars = function(self, info_queue, center) + return { vars = { "" .. (G.GAME and G.GAME.probabilities.normal or 1), center.ability.extra.odds } } + end, + blueprint_compat = true, + eternal_compat = false, + atlas = "atlasthree", + calculate = function(self, card, context) + if + context.end_of_round2 + and not context.individual + and not context.repetition + and not context.blueprint + and not context.retrigger_joker + then + if pseudorandom("oldblueprint") < G.GAME.probabilities.normal / card.ability.extra.odds then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + if G.P_CENTERS["j_blueprint"].unlocked then G.GAME.oldbpfactor = (G.GAME.oldbpfactor or 1)*3 end + return true + end, + })) + return true + end, + })) + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize("k_extinct_ex"), colour = G.C.FILTER } + ) + else + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize("k_safe_ex"), colour = G.C.FILTER } + ) + end + end + local other_joker = nil + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + other_joker = G.jokers.cards[i + 1] + end + end + if other_joker and other_joker ~= self then + context.blueprint = (context.blueprint and (context.blueprint + 1)) or 1 + context.blueprint_card = context.blueprint_card or card + + if context.blueprint > #G.jokers.cards + 1 then + return + end + + local other_joker_ret, trig = other_joker:calculate_joker(context) + if other_joker_ret or trig then + if not other_joker_ret then + other_joker_ret = {} + end + other_joker_ret.card = context.blueprint_card or card + other_joker_ret.colour = G.C.BLUE + other_joker_ret.no_callback = true + return other_joker_ret + end + end + end, + cry_credits = { + idea = { + "Linus Goof Balls" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Math" + } + }, +} +local night = { + object_type = "Joker", + name = "cry-night", + key = "night", + config = { extra = { mult = 3 } }, + pos = { x = 3, y = 1 }, + rarity = 3, + cost = 6, + order = 41, + eternal_compat = false, + blueprint_compat = true, + atlas = "atlasthree", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.mult } } + end, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and not context.before + and not context.after + and G.GAME.current_round.hands_left == 0 + then + if card.ability.extra.mult > 1 then + return { + message = localize{type='variable',key='a_powmult',vars={card.ability.extra.mult}}, + Emult_mod = card.ability.extra.mult, + colour = G.C.DARK_EDITION, + } + end + elseif context.cardarea == G.jokers and context.after and not context.blueprint and not context.retrigger_joker then + if G.GAME.current_round.hands_left <= 0 then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("k_extinct_ex"), + colour = G.C.FILTER, + } + elseif G.GAME.current_round.hands_left <= 1 then + local eval = function(card) return G.GAME.current_round.hands_left <= 1 and not G.RESET_JIGGLES end + juice_card_until(card, eval, true) + end + elseif context.first_hand_drawn and not context.blueprint and not context.retrigger_joker then + if next(find_joker('cry-panopticon')) then + local eval = function(card) return G.GAME.current_round.hands_played == 0 and not G.RESET_JIGGLES end + juice_card_until(card, eval, true) + end + end + end, + cry_credits = { + idea = { + "Linus Goof Balls" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Jevonn" + } + }, +} +local busdriver = { + object_type = "Joker", + name = "cry-busdriver", + key = "busdriver", + config = { extra = { mult = 50, odds = 4 } }, + pos = { x = 5, y = 1 }, + immutable = true, + rarity = 2, + cost = 7, + order = 46, + atlas = "atlasthree", + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { + vars = { + "" .. ((G.GAME and G.GAME.probabilities.normal or 1) * 3), + center.ability.extra.mult, + center.ability.extra.odds, + }, + } + end, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and (card.ability.extra.mult > 0) + and not context.before + and not context.after + then + if pseudorandom("busdriver") < G.GAME.probabilities.normal / card.ability.extra.odds * 3 then + return { + message = localize({ type = "variable", key = "a_mult", vars = { card.ability.extra.mult } }), + mult_mod = card.ability.extra.mult, + colour = G.C.MULT, + } + else + return { + message = localize({ type = "variable", key = "a_mult_minus", vars = { card.ability.extra.mult } }), + mult_mod = (card.ability.extra.mult * -1), + colour = G.C.MULT, + } + end + end + end, + cry_credits = { + idea = { + "Linus Goof Balls" + }, + art = { + "Linus Goof Balls" + }, + code = { + "Jevonn" + } + }, +} +local translucent = { + object_type = "Joker", + name = "cry-translucent Joker", + key = "translucent", + pos = { x = 5, y = 2 }, + rarity = 1, + cost = 4, + order = 52, + immutable = true, + eternal_compat = false, + atlas = "atlasthree", + calculate = function(self, card, context) + if context.selling_self and not (context.retrigger_joker or context.blueprint) then + local jokers = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] ~= card and not G.jokers.cards[i].debuff then + jokers[#jokers + 1] = G.jokers.cards[i] + end + end + if #jokers > 0 then + if #G.jokers.cards <= G.jokers.config.card_limit then + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_duplicated_ex") }) + local chosen_joker = pseudorandom_element(jokers, pseudoseed("trans")) + local _card = + copy_card(chosen_joker, nil, nil, nil, chosen_joker.edition and chosen_joker.edition.negative) + _card:add_to_deck() + _card:set_banana(true) + _card.ability.perishable = true -- Done manually to bypass perish compat + _card.ability.perish_tally = G.GAME.perishable_rounds + G.jokers:emplace(_card) + return nil, true + else + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_no_room_ex") }) + end + else + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_no_other_jokers") }) + end + end + end, + cry_credits = { + idea = { + "SDM_0" + }, + art = { + "SDM_0" + }, + code = { + "SDM_0" + } + }, +} +local morse = { + object_type = "Joker", + name = "cry-morse", + key = "morse", + pos = { x = 5, y = 1 }, + config = { extra = { bonus = 2, money = 1 } }, + rarity = 1, + cost = 5, + order = 57, + perishable_compat = false, + blueprint_compat = false, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.bonus, center.ability.extra.money } } + end, + atlas = "atlastwo", + calculate = function(self, card, context) + if context.selling_card and context.card.edition and not context.blueprint then + card.ability.extra.money = card.ability.extra.money + card.ability.extra.bonus + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("k_upgrade_ex"), + colour = G.C.MONEY, + }), + } + end + end, + calc_dollar_bonus = function(self, card) + if card.ability.extra.money > 0 then + return card.ability.extra.money + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local membershipcard = { + object_type = "Joker", + name = "cry-membershipcard", + key = "membershipcard", + config = { extra = { Xmult_mod = 0.1 } }, + pos = { x = 6, y = 2 }, + soul_pos = { x = 6, y = 1 }, + rarity = 4, + cost = 20, + order = 35, + blueprint_compat = true, + atlas = "atlasthree", + loc_vars = function(self, info_queue, card) + return { vars = { card.ability.extra.Xmult_mod, card.ability.extra.Xmult_mod * GLOBAL_cry_member_count } } + end, + calculate = function(self, card, context) + if + context.cardarea == G.jokers + and not context.before + and not context.after + and card.ability.extra.Xmult_mod * GLOBAL_cry_member_count > 1 + then + return { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { card.ability.extra.Xmult_mod * GLOBAL_cry_member_count }, + }), + Xmult_mod = card.ability.extra.Xmult_mod * GLOBAL_cry_member_count, + } + end + end, + cry_credits = { + idea = { + "Toneblock" + }, + art = { + "HexaCryonic" + }, + code = { + "Toneblock" + } + }, +} +local kscope = { + object_type = "Joker", + name = "cry-kscope", + key = "kscope", + pos = { x = 3, y = 4 }, + rarity = 3, + cost = 7, + order = 55, + atlas = "atlasthree", + immutable = true, + calculate = function(self, card, context) + if context.end_of_round and G.GAME.blind.boss and not context.individual and not context.repetition then + local eligiblejokers = {} + for k, v in pairs(G.jokers.cards) do + if v.ability.set == "Joker" and not v.edition and v ~= card then + table.insert(eligiblejokers, v) + end + end + if #eligiblejokers > 0 then + --you just lost the game + local eligible_card = + pseudorandom_element(eligiblejokers, pseudoseed("nevergonnagiveyouupnevergonnaletyoudown")) + local edition = { polychrome = true } + eligible_card:set_edition(edition, true) + check_for_unlock({ type = "have_edition" }) + end + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local cryptidmoment = { + object_type = "Joker", + name = "cry_cryptidmoment", + key = "cryptidmoment", + pos = { x = 6, y = 0 }, + config = { extra = { money = 1 } }, + loc_vars = function(self, info_queue, center) + return { vars = { math.max(1, math.floor(center.ability.extra.money)) } } + end, + rarity = 1, + cost = 4, + order = 65, + eternal_compat = false, + atlas = "atlasthree", + calculate = function(self, card, context) + if context.selling_self and not context.blueprint then + for k, v in ipairs(G.jokers.cards) do + if v.set_cost then + v.ability.extra_value = (v.ability.extra_value or 0) + + math.max(1, math.floor(card.ability.extra.money)) + v:set_cost() + end + end + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_val_up"), colour = G.C.MONEY }) + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Yamper" + }, + code = { + "Jevonn" + } + }, +} +local flipside = { + object_type = "Joker", + name = "cry-Flip Side", + key = "flip_side", + pos = { x = 1, y = 0 }, + rarity = 2, + cost = 7, + order = 107, + atlas = "placeholders", + no_dbl = true, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_double_sided + end, + add_to_deck = function(self, card, from_debuff) + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].edition and G.jokers.cards[i].edition.cry_double_sided then + G.jokers.cards[i]:init_dbl_side() + G.jokers.cards[i]:remove_from_deck(true) + G.jokers.cards[i].dbl_side:add_to_deck(true) + end + end + end, + remove_from_deck = function(self, card, from_debuff) + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].edition and G.jokers.cards[i].edition.cry_double_sided then + G.jokers.cards[i]:init_dbl_side() + G.jokers.cards[i]:add_to_deck(true) + G.jokers.cards[i].dbl_side:remove_from_deck(true) + end + end + end, + calculate = function(self, card, context) + if context.retrigger_joker_check and not context.retrigger_joker and context.other_card ~= self then + if context.other_context and context.other_context.dbl_side then + return { + message = localize("k_again_ex"), + repetitions = 1, + card = card, + } + else + return nil, true + end + end + end, + cry_credits = { + idea = { + "Axolotus" + }, + art = { + ":(" + }, + code = { + "Math" + } + }, +} +local oldinvisible = { + object_type = "Joker", + name = "cry-Old Invisible Joker", + key = "oldinvisible", + pos = { x = 4, y = 4 }, + config = { extra = 0 }, + rarity = 4, + cost = 20, + order = 78, + atlas = "atlasthree", + immutable = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + calculate = function(self, card, context) + if context.selling_card and context.card.ability.set == "Joker" + and not context.blueprint and not context.retrigger_joker then + if card.ability.extra >= 3 then + card.ability.extra = 0 + local eligibleJokers = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.name ~= card.ability.name and G.jokers.cards[i] ~= context.card then + eligibleJokers[#eligibleJokers + 1] = G.jokers.cards[i] + end + end + if #eligibleJokers > 0 then + G.E_MANAGER:add_event(Event({ + func = function() + local card = copy_card(pseudorandom_element(eligibleJokers, pseudoseed("cry_oldinvis")), nil) + card:add_to_deck() + G.jokers:emplace(card) + return true + end, + })) + card_eval_status_text( + context.blueprint_card or card, + "extra", + nil, + nil, + nil, + { message = localize("k_duplicated_ex") } + ) + return nil, true + else + card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, {message = localize('k_no_other_jokers')}) + end + return + else + card.ability.extra = card.ability.extra + 1 + if card.ability.extra == 3 then + local eval = function(card) return (card.ability.extra == 3) end + juice_card_until(card, eval, true) + end + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = card.ability.extra .. "/4", + colour = G.C.FILTER, + }), + } + end + end + end, + cry_credits = { + idea = { + "LocalThunk" + }, + art = { + "LocalThunk" + }, + code = { + "Jevonn" + } + }, +} +local fractal = { + object_type = "Joker", + name = "cry-FractalFingers", + key = "fractal", + pos = { x = 6, y = 4 }, + config = { extra = 2 }, + rarity = 3, + cost = 7, + order = 76, + atlas = "atlasthree", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + add_to_deck = function(self, card, from_debuff) + card.ability.extra = math.floor(card.ability.extra) + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit + card.ability.extra + end, + remove_from_deck = function(self, card, from_debuff) + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit - card.ability.extra + if G.hand.config.highlighted_limit < 5 then G.hand.config.highlighted_limit = 5 end + G.hand:unhighlight_all() + end, + cry_credits = { + idea = { + "HexaCryonic" + }, + art = { + "HexaCryonic" + }, + code = { + "HexaCryonic" + } + }, +} +local universe = { + cry_credits = { + idea = {"Mystic Misclick"}, + art = {"spire_winder"}, + code = {"spire_winder"} + }, + object_type = "Joker", + name = "cry-universe", + key = "universe", + pos = { x = 8, y = 0 }, + atlas = "atlasthree", + config = { extra = { emult = 1.2 } }, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.cry_astral) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_astral + end + return { vars = { center.ability.extra.emult } } + end, + rarity = 3, + cost = 6, + order = 121, + blueprint_compat = true, + calculate = function(self, card, context) + if + context.other_joker + and context.other_joker.edition + and context.other_joker.edition.cry_astral == true + and card ~= context.other_joker + then + if not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end, + })) + end + return { + message = localize({ type = "variable", key = "a_powmult", vars = { card.ability.extra.emult } }), + Emult_mod = card.ability.extra.emult, + colour = G.C.DARK_EDITION + } + end + if context.individual and context.cardarea == G.play then + if context.other_card.edition and context.other_card.edition.cry_astral == true then + return { + e_mult = card.ability.extra.emult, + colour = G.C.DARK_EDITION, + card = card + } + end + end + if + context.individual + and context.cardarea == G.hand + and context.other_card.edition + and context.other_card.edition.cry_astral == true + and not context.end_of_round + then + if context.other_card.debuff then + return { + message = localize("k_debuffed"), + colour = G.C.RED, + card = card, + } + else + return { + e_mult = card.ability.extra.emult, + colour = G.C.DARK_EDITION, + card = card + } + end + end + end, +} +local astral_bottle = { + cry_credits = { + idea = {"AlexZGreat"}, + art = {"spire_winder"}, + code = {"spire_winder"} + }, + object_type = "Joker", + name = "cry-astral_bottle", + key = "astral_bottle", + pos = { x = 7, y = 0 }, + atlas = "atlasthree", + rarity = 2, + cost = 6, + order = 122, + blueprint_compat = false, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.cry_astral) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_astral + end + end, + calculate = function(self, card, context) + if context.selling_self and not context.retrigger_joker and not context.blueprint then + local jokers = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] ~= card and not G.jokers.cards[i].debuff and not G.jokers.cards[i].edition then + jokers[#jokers + 1] = G.jokers.cards[i] + end + end + if #jokers > 0 then + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_duplicated_ex") }) + local chosen_joker = pseudorandom_element(jokers, pseudoseed("trans")) + chosen_joker:set_edition{cry_astral = true} + chosen_joker.ability.perishable = true -- Done manually to bypass perish compat + chosen_joker.ability.perish_tally = G.GAME.perishable_rounds + return nil, true + else + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_no_other_jokers") }) + end + end + end, +} +local kidnap = { + object_type = "Joker", + name = "cry-kidnap", + key = "kidnap", + order = 23, + pos = { x = 1, y = 2 }, + config = { + extra = { money = 1, money_mod = 3 }, + jolly = { t_mult = 8, type = "Pair" }, + zany = { t_mult = 12, type = "Three of a Kind" }, + mad = { t_mult = 10, type = "Two Pair" }, + crazy = { t_mult = 12, type = "Straight" }, + droll = { t_mult = 10, type = "Flush" }, + }, + rarity = 1, + cost = 4, + blueprint_compat = false, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_jolly", + specific_vars = { self.config.jolly.t_mult, localize(self.config.jolly.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_zany", + specific_vars = { self.config.zany.t_mult, localize(self.config.zany.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_mad", + specific_vars = { self.config.mad.t_mult, localize(self.config.mad.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_crazy", + specific_vars = { self.config.crazy.t_mult, localize(self.config.crazy.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_droll", + specific_vars = { self.config.droll.t_mult, localize(self.config.droll.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_sly", + specific_vars = { 50, localize(self.config.jolly.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_wily", + specific_vars = { 100, localize(self.config.zany.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_clever", + specific_vars = { 80, localize(self.config.mad.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_devious", + specific_vars = { 100, localize(self.config.crazy.type, "poker_hands") }, + } + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_crafty", + specific_vars = { 80, localize(self.config.droll.type, "poker_hands") }, + } + return { vars = { center.ability.extra.money_mod, center.ability.extra.money } } + end, + atlas = "atlasone", + calculate = function(self, card, context) + if + context.selling_card + and ( + ( + context.card.ability.name == "Sly Joker" + or context.card.ability.name == "Wily Joker" + or context.card.ability.name == "Clever Joker" + or context.card.ability.name == "Devious Joker" + or context.card.ability.name == "Crafty Joker" + ) + or context.card.ability.effect == "Type Mult" + or context.card.ability.effect == "Cry Type Mult" + or context.card.ability.effect == "Cry Type Chips" + --[[ + Other developers can add effect == "Boost Kidnapping" + to their joker config if they want it to boost kidnapping when sold + ]]-- + or context.card.ability.effect == "Boost Kidnapping" + or context.card:is_jolly() + ) + and not context.blueprint + then + card.ability.extra.money = card.ability.extra.money + card.ability.extra.money_mod + return { + card_eval_status_text(card, "extra", nil, nil, nil, { + message = localize("k_upgrade_ex"), + colour = G.C.MONEY, + }), + } + end + end, + calc_dollar_bonus = function(self, card) + if card.ability.extra.money > 0 then + return card.ability.extra.money + end + end, + cry_credits = { + idea = { + "Jevonn" + }, + art = { + "Jevonn" + }, + code = { + "Jevonn" + } + }, +} +local exposed = { + object_type = "Joker", + name = "cry-Exposed", + key = "exposed", + pos = { x = 0, y = 5 }, + config = { extra = 2 }, + rarity = 3, + cost = 8, + order = 123, + atlas = "atlastwo", + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + update = function(self, card, dt) + if G.deck and not (card.area and card.area.config.collection) then + for i, v in pairs (G.deck.cards) do + if v:is_face() then + v:set_debuff(true) + end + end + end + if G.hand and not (card.area and card.area.config.collection) then + for i, v in pairs (G.hand.cards) do + if v:is_face() then + v:set_debuff(true) + end + end + end + end, + calculate = function(self, card, context) + if context.repetition and context.cardarea == G.play then + if not context.other_card:is_face() then + return { + message = localize('k_again_ex'), + repetitions = card.ability.extra, + card = card + } + end + end + end, +} +local mask = { + object_type = "Joker", + name = "cry-Mask", + key = "mask", + pos = { x = 1, y = 5 }, + config = { extra = 3 }, + rarity = 3, + cost = 7, + atlas = "atlastwo", + order = 124, + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + update = function(self, card, dt) + if G.deck and not (card.area and card.area.config.collection) then + for i, v in pairs (G.deck.cards) do + if not v:is_face() then + v:set_debuff(true) + end + end + end + if G.hand and not (card.area and card.area.config.collection) then + for i, v in pairs (G.hand.cards) do + if not v:is_face() then + v:set_debuff(true) + end + end + end + end, + calculate = function(self, card, context) + if context.repetition and context.cardarea == G.play then + if context.other_card:is_face() then + return { + message = localize('k_again_ex'), + repetitions = card.ability.extra, + card = card + } + end + end + end, +} +local tropical_smoothie = { + object_type = "Joker", + name = "cry-Tropical Smoothie", + key = "tropical_smoothie", + pos = { x = 2, y = 5 }, + config = {}, + rarity = 3, + cost = 5, + order = 125, + atlas = "atlastwo", + immutable = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + calculate = function(self, card, context) + if context.selling_self then + local check = false + for i, v in pairs (G.jokers.cards) do + if not Card.no(v, "immutable", true) then + cry_with_deck_effects(G.jokers.cards[1], function(card) + cry_misprintize(v, { min = 1.5, max = 1.5}, nil, true) + end) + check = true + end + end + if check then + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { message = localize("k_upgrade_ex"), colour = G.C.GREEN } + ) + end + end + end, +} +local necromancer = { + object_type = "Joker", + name = "cry-Necromancer", + key = "necromancer", + pos = { x = 3, y = 5 }, + config = {}, + rarity = 2, + cost = 5, + atlas = "atlastwo", + order = 126, + immutable = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + calculate = function(self, card, context) + if context.selling_card and context.card.sell_cost > 0 and context.card.config.center.set == 'Joker' and G.GAME.jokers_sold then + local card = create_card('Joker', G.jokers, nil, nil, nil, nil, G.GAME.jokers_sold[pseudorandom('cry_necromancer', 1, #G.GAME.jokers_sold)]) + card.sell_cost = 0 + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + end + end, + cry_credits = { + idea = { + "Pyrocreep" + }, + art = { + "Pyrocreep" + }, + code = { + "Foegro" + } + }, +} +local oil_lamp = { --You want it? It's yours my friend + object_type = "Joker", + name = "cry-Oil-Lamp", + key = "oil_lamp", + pos = { x = 4, y = 5 }, + config = { extra = { increase = 1.2 } }, + rarity = 3, + cost = 10, + order = 127, + atlas = "atlastwo", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.increase } } + end, + calculate = function(self, card, context) + if context.end_of_round and not context.repetition and not context.individual then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + if i < #G.jokers.cards then + if not Card.no(G.jokers.cards[i+1], "immutable", true) then + cry_with_deck_effects(G.jokers.cards[i+1], function(cards) + cry_misprintize(cards, { min = card.ability.extra.increase, max = card.ability.extra.increase }, nil, true) + end) + end + end + end + end + end + end, + cry_credits = { + idea = { + "AlexZGreat" + }, + art = { + "AlexZGreat" + }, + code = { + "Foegro" + } + }, +} +local tax_fraud = { + object_type = "Joker", + name = "cry-Tax-Fraud", + key = "tax_fraud", + pos = { x = 2, y = 0 }, + config = { extra = { money = 6 } }, + rarity = 3, + cost = 10, + order = 128, + atlas = "placeholders", + in_pool = function(self) + if G.jokers then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.rental then return true end + end + end + return false + end, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.money } } + end, + calc_dollar_bonus = function(self, card) + local rentals = 0 + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.rental then rentals = rentals+1 end + end + return rentals*card.ability.extra.money + end, + cry_credits = { + idea = { + "DoNotSus" + }, + code = { + "Foegro" + } + }, +} +local pity_prize = { + object_type = "Joker", + name = "cry-Pity-Prize", + key = "pity_prize", + pos = { x = 5, y = 5 }, + config = { }, + rarity = 1, + cost = 4, + atlas = "atlastwo", + order = 129, + loc_vars = function(self, info_queue, center) + return { vars = { } } + end, + calculate = function(self, card, context) + if context.skipping_booster then + local tag + repeat + tag = Tag(get_next_tag_key("cry_pity_prize")) + until tag.name ~= "Boss Tag" and tag.name ~= "Gambler's Tag" and tag.name ~= "Empowered Tag" --I saw pickle not generating boss tags because it apparently causes issues, so I did the same here + if tag.name == "Orbital Tag" then + local _poker_hands = {} + for k, v in pairs(G.GAME.hands) do + if v.visible then + _poker_hands[#_poker_hands + 1] = k + end + end + tag.ability.orbital_hand = pseudorandom_element(_poker_hands, pseudoseed("cry_pity_prize")) + end + add_tag(tag) + end + end, + cry_credits = { + idea = { + "Pyrocreep" + }, + art = { + "Pyrocreep" + }, + code = { + "Foegro" + } + }, +} +local miscitems = { + jimball_sprite, + dropshot, + happyhouse, + maximized, + potofjokes, + queensgambit, + wee_fib, + compound_interest, + whip, + pickle, + triplet_rhythm, + booster, + chili_pepper, + lucky_joker, + cursor, + cube, + big_cube, + nice, + sus, + chad, + jimball, + luigi, + waluigi, + mario, + wario, + eternalflame, + seal_the_deal, + fspinner, + krustytheclown, + blurred, + gardenfork, + lightupthenight, + nosound, + antennastoheaven, + hunger, + weegaming, + redbloon, + apjoker, + maze, + panopticon, + magnet, + unjust_dagger, + monkey_dagger, + pirate_dagger, + mondrian, + sapling, + spaceglobe, + happy, + meteor, + exoplanet, + stardust, + rnjoker, + filler, + duos, + home, + nuts, + quintet, + unity, + swarm, + coin, + wheelhope, + night, + busdriver, + oldblueprint, + morse, + membershipcard, + kscope, + cryptidmoment, + oldinvisible, + fractal, + giggly, + nutty, + manic, + silly, + delirious, + wacky, + kooky, + dubious, + shrewd, + tricksy, + foxy, + savvy, + subtle, + discreet, + kidnap, + exposed, + mask, + tropical_smoothie, + necromancer, + oil_lamp, + tax_fraud, + pity_prize, +} +if Cryptid.enabled["Misc."] then + miscitems[#miscitems+1] = flipside + miscitems[#miscitems+1] = universe + miscitems[#miscitems+1] = astral_bottle + miscitems[#miscitems+1] = stronghold + miscitems[#miscitems+1] = wtf + miscitems[#miscitems+1] = clash + miscitems[#miscitems+1] = adroit + miscitems[#miscitems+1] = penetrating + miscitems[#miscitems+1] = treacherous + miscitems[#miscitems+1] = bonkers + miscitems[#miscitems+1] = fuckedup + miscitems[#miscitems+1] = foolhardy +end +if Cryptid.enabled["More Stakes"] then + miscitems[#miscitems+1] = translucent +end +return { + name = "Misc. Jokers", + init = function() + cry_enable_jokers = true + --Dropshot Patches + local gigo = Game.init_game_object + function Game:init_game_object() + local g = gigo(self) + g.current_round.cry_dropshot_card = { suit = "Spades" } + return g + end + local rcc = reset_castle_card + function reset_castle_card() + rcc() + if not G.GAME.current_round.cry_dropshot_card then + G.GAME.current_round.cry_dropshot_card = {} + end + G.GAME.current_round.cry_dropshot_card.suit = "Spades" + local valid_castle_cards = {} + for k, v in ipairs(G.playing_cards) do + if v.ability.effect ~= "Stone Card" then + valid_castle_cards[#valid_castle_cards + 1] = v + end + end + if valid_castle_cards[1] then + local castle_card = + pseudorandom_element(valid_castle_cards, pseudoseed("cry_dro" .. G.GAME.round_resets.ante)) + if not G.GAME.current_round.cry_dropshot_card then + G.GAME.current_round.cry_dropshot_card = {} + end + G.GAME.current_round.cry_dropshot_card.suit = castle_card.base.suit + end + end + + --Maximized Patches + local cgi_ref = Card.get_id + override_maximized = false + function Card:get_id() + local id = cgi_ref(self) + if id == nil then + id = 10 + end + if next(find_joker("cry-Maximized")) and not override_maximized then + if id >= 2 and id <= 10 then + id = 10 + end + if id >= 11 and id <= 13 or next(find_joker("Pareidolia")) then + id = 13 + end + end + return id + end + --Fix issues with View Deck and Maximized + local gui_vd = G.UIDEF.view_deck + function G.UIDEF.view_deck(unplayed_only) + override_maximized = true + local ret_value = gui_vd(unplayed_only) + override_maximized = false + return ret_value + end + + --Cube Patches + local sc = Card.set_cost + function Card:set_cost() + sc(self) + if self.ability.name == "cry-Cube" then + self.cost = -27 + end + if self.ability.name == "cry-Big Cube" then + self.cost = 27 + end + end + --Jimball Patches + local upd = Game.update + cry_jimball_dt = 0 + function Game:update(dt) + upd(self, dt) + cry_jimball_dt = cry_jimball_dt + dt + if G.P_CENTERS and G.P_CENTERS.j_cry_jimball and cry_jimball_dt > 0.1 then + cry_jimball_dt = 0 + local obj = G.P_CENTERS.j_cry_jimball + if obj.pos.x == 5 and obj.pos.y == 6 then + obj.pos.x = 0 + obj.pos.y = 0 + elseif obj.pos.x < 8 then + obj.pos.x = obj.pos.x + 1 + elseif obj.pos.y < 6 then + obj.pos.x = 0 + obj.pos.y = obj.pos.y + 1 + end + end + end + end, + items = miscitems, +} diff --git a/Cryptid/Items/Planets.lua b/Cryptid/Items/Planets.lua new file mode 100644 index 0000000..d763e7b --- /dev/null +++ b/Cryptid/Items/Planets.lua @@ -0,0 +1,860 @@ +local timantti = { + object_type = "Consumable", + set = "Planet", + name = "cry-Timantti", + key = "Timantti", + pos = { x = 0, y = 2 }, + config = { hand_types = { "High Card", "Pair", "Two Pair" } }, + cost = 4, + aurinko = true, + atlas = "atlasnotjokers", + order = 8, + can_use = function(self, card) + return true + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["High Card"].level or 1 + local leveltwo = G.GAME.hands["Pair"].level or 1 + local levelthree = G.GAME.hands["Two Pair"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + local planetcolourtwo = G.C.HAND_LEVELS[math.min(leveltwo, 7)] + local planetcolourthree = G.C.HAND_LEVELS[math.min(levelthree, 7)] + if levelone == 1 or leveltwo == 1 or levelthree == 1 then --Level 1 colour is white (The background), so this sets it to black + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + if leveltwo == 1 then + planetcolourtwo = G.C.UI.TEXT_DARK + end + if levelthree == 1 then + planetcolourthree = G.C.UI.TEXT_DARK + end + end + return { + vars = { + localize("High Card", "poker_hands"), + localize("Pair", "poker_hands"), + localize("Two Pair", "poker_hands"), + G.GAME.hands["High Card"].level, + G.GAME.hands["Pair"].level, + G.GAME.hands["Two Pair"].level, + colours = { planetcolourone, planetcolourtwo, planetcolourthree }, + }, + } + end, + use = function(self, card, area, copier) + suit_level_up(self, card, area, copier) + end, + bulk_use = function(self, card, area, copier, number) + suit_level_up(self, card, area, copier, number) + end, + calculate = function(self, card, context) + if + G.GAME.used_vouchers.v_observatory + and ( + context.scoring_name == "High Card" + or context.scoring_name == "Pair" + or context.scoring_name == "Two Pair" + ) + then + local value = G.P_CENTERS.v_observatory.config.extra + return { + message = localize({ type = "variable", key = "a_xmult", vars = { value } }), + Xmult_mod = value, + } + end + end, +} +local klubi = { + object_type = "Consumable", + set = "Planet", + name = "cry-Klubi", + key = "Klubi", + pos = { x = 1, y = 2 }, + config = { hand_types = { "Three of a Kind", "Straight", "Flush" } }, + cost = 4, + aurinko = true, + atlas = "atlasnotjokers", + order = 9, + can_use = function(self, card) + return true + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["Three of a Kind"].level or 1 + local leveltwo = G.GAME.hands["Straight"].level or 1 + local levelthree = G.GAME.hands["Flush"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + local planetcolourtwo = G.C.HAND_LEVELS[math.min(leveltwo, 7)] + local planetcolourthree = G.C.HAND_LEVELS[math.min(levelthree, 7)] + if levelone == 1 or leveltwo == 1 or levelthree == 1 then --Level 1 colour is white (The background), so this sets it to black + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + if leveltwo == 1 then + planetcolourtwo = G.C.UI.TEXT_DARK + end + if levelthree == 1 then + planetcolourthree = G.C.UI.TEXT_DARK + end + end + return { + vars = { + localize("Three of a Kind", "poker_hands"), + localize("Straight", "poker_hands"), + localize("Flush", "poker_hands"), + G.GAME.hands["Three of a Kind"].level, + G.GAME.hands["Straight"].level, + G.GAME.hands["Flush"].level, + colours = { planetcolourone, planetcolourtwo, planetcolourthree }, + }, + } + end, + use = function(self, card, area, copier) + suit_level_up(self, card, area, copier) + end, + bulk_use = function(self, card, area, copier, number) + suit_level_up(self, card, area, copier, number) + end, + calculate = function(self, card, context) + if + G.GAME.used_vouchers.v_observatory + and ( + context.scoring_name == "Three of a Kind" + or context.scoring_name == "Straight" + or context.scoring_name == "Flush" + ) + then + local value = G.P_CENTERS.v_observatory.config.extra + return { + message = localize({ type = "variable", key = "a_xmult", vars = { value } }), + Xmult_mod = value, + } + end + end, +} +local sydan = { + object_type = "Consumable", + set = "Planet", + name = "cry-Sydan", + key = "Sydan", + pos = { x = 2, y = 2 }, + config = { hand_types = { "Full House", "Four of a Kind", "Straight Flush" } }, + cost = 4, + aurinko = true, + atlas = "atlasnotjokers", + order = 10, + can_use = function(self, card) + return true + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["Full House"].level or 1 + local leveltwo = G.GAME.hands["Four of a Kind"].level or 1 + local levelthree = G.GAME.hands["Straight Flush"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + local planetcolourtwo = G.C.HAND_LEVELS[math.min(leveltwo, 7)] + local planetcolourthree = G.C.HAND_LEVELS[math.min(levelthree, 7)] + if levelone == 1 or leveltwo == 1 or levelthree == 1 then --Level 1 colour is white (The background), so this sets it to black + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + if leveltwo == 1 then + planetcolourtwo = G.C.UI.TEXT_DARK + end + if levelthree == 1 then + planetcolourthree = G.C.UI.TEXT_DARK + end + end + return { + vars = { + localize("Full House", "poker_hands"), + localize("Four of a Kind", "poker_hands"), + localize("Straight Flush", "poker_hands"), + G.GAME.hands["Full House"].level, + G.GAME.hands["Four of a Kind"].level, + G.GAME.hands["Straight Flush"].level, + colours = { planetcolourone, planetcolourtwo, planetcolourthree }, + }, + } + end, + use = function(self, card, area, copier) + suit_level_up(self, card, area, copier) + end, + bulk_use = function(self, card, area, copier, number) + suit_level_up(self, card, area, copier, number) + end, + calculate = function(self, card, context) + if + G.GAME.used_vouchers.v_observatory + and ( + context.scoring_name == "Full House" + or context.scoring_name == "Four of a Kind" + or context.scoring_name == "Straight Flush" + ) + then + local value = G.P_CENTERS.v_observatory.config.extra + return { + message = localize({ type = "variable", key = "a_xmult", vars = { value } }), + Xmult_mod = value, + } + end + end, +} +local lapio = { + object_type = "Consumable", + set = "Planet", + name = "cry-Lapio", + key = "Lapio", + pos = { x = 3, y = 2 }, + config = { hand_types = { "Five of a Kind", "Flush House", "Flush Five" }, softlock = true }, + cost = 4, + aurinko = true, + atlas = "atlasnotjokers", + order = 11, + can_use = function(self, card) + return true + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["Five of a Kind"].level or 1 + local leveltwo = G.GAME.hands["Flush House"].level or 1 + local levelthree = G.GAME.hands["Flush Five"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + local planetcolourtwo = G.C.HAND_LEVELS[math.min(leveltwo, 7)] + local planetcolourthree = G.C.HAND_LEVELS[math.min(levelthree, 7)] + if levelone == 1 or leveltwo == 1 or levelthree == 1 then --Level 1 colour is white (The background), so this sets it to black + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + if leveltwo == 1 then + planetcolourtwo = G.C.UI.TEXT_DARK + end + if levelthree == 1 then + planetcolourthree = G.C.UI.TEXT_DARK + end + end + return { + vars = { + localize("Five of a Kind", "poker_hands"), + localize("Flush House", "poker_hands"), + localize("Flush Five", "poker_hands"), + G.GAME.hands["Five of a Kind"].level, + G.GAME.hands["Flush House"].level, + G.GAME.hands["Flush Five"].level, + colours = { planetcolourone, planetcolourtwo, planetcolourthree }, + }, + } + end, + use = function(self, card, area, copier) + suit_level_up(self, card, area, copier) + end, + bulk_use = function(self, card, area, copier, number) + suit_level_up(self, card, area, copier, number) + end, + calculate = function(self, card, context) + if + G.GAME.used_vouchers.v_observatory + and ( + context.scoring_name == "Five of a Kind" + or context.scoring_name == "Flush House" + or context.scoring_name == "Flush Five" + ) + then + local value = G.P_CENTERS.v_observatory.config.extra + return { + message = localize({ type = "variable", key = "a_xmult", vars = { value } }), + Xmult_mod = value, + } + end + end, +} +local kaikki = { + object_type = "Consumable", + set = "Planet", + name = "cry-Kaikki", + key = "Kaikki", + pos = { x = 3, y = 5 }, + config = { hand_types = { "cry_Bulwark", "cry_Clusterfuck", "cry_UltPair" }, softlock = true }, + cost = 4, + aurinko = true, + atlas = "atlasnotjokers", + order = 12, + can_use = function(self, card) + return true + end, + loc_vars = function(self, info_queue, center) + local levelone = G.GAME.hands["cry_Bulwark"].level or 1 + local leveltwo = G.GAME.hands["cry_Clusterfuck"].level or 1 + local levelthree = G.GAME.hands["cry_UltPair"].level or 1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + local planetcolourtwo = G.C.HAND_LEVELS[math.min(leveltwo, 7)] + local planetcolourthree = G.C.HAND_LEVELS[math.min(levelthree, 7)] + if levelone == 1 or leveltwo == 1 or levelthree == 1 then --Level 1 colour is white (The background), so this sets it to black + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + if leveltwo == 1 then + planetcolourtwo = G.C.UI.TEXT_DARK + end + if levelthree == 1 then + planetcolourthree = G.C.UI.TEXT_DARK + end + end + return { + vars = { + localize("cry_hand_bulwark"), + localize("cry_hand_clusterfuck"), + localize("cry_hand_ultpair"), + G.GAME.hands["cry_Bulwark"].level, + G.GAME.hands["cry_Clusterfuck"].level, + G.GAME.hands["cry_UltPair"].level, + colours = { planetcolourone, planetcolourtwo, planetcolourthree }, + }, + } + end, + use = function(self, card, area, copier) + suit_level_up(self, card, area, copier) + end, + bulk_use = function(self, card, area, copier, number) + suit_level_up(self, card, area, copier, number) + end, + calculate = function(self, card, context) + if + G.GAME.used_vouchers.v_observatory + and ( + context.scoring_name == "cry_Bulwark" + or context.scoring_name == "cry_Clusterfuck" + or context.scoring_name == "cry_UltPair" + ) + then + local value = G.P_CENTERS.v_observatory.config.extra + return { + message = localize({ type = "variable", key = "a_xmult", vars = { value } }), + Xmult_mod = value, + } + end + end, +} +local planetlua = { + object_type = "Consumable", + set = "Planet", + name = "cry-planetlua", + key = "planetlua", + config = { extra = { odds = 5 } }, + pos = { x = 4, y = 2 }, + cost = 4, + aurinko = true, + atlas = "atlasnotjokers", + order = 1, + loc_vars = function(self, info_queue, center) + return { vars = { "" .. (G.GAME and G.GAME.probabilities.normal or 1), self.config.extra.odds } } + end, + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + if pseudorandom("planetlua") < G.GAME.probabilities.normal / card.ability.extra.odds then --Code "borrowed" from black hole + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { handname = localize("k_all_hands"), chips = "...", mult = "...", level = "" } + ) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = true + return true + end, + })) + update_hand_text({ delay = 0 }, { mult = "+", StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.9, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 0 }, { chips = "+", StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.9, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = nil + return true + end, + })) + update_hand_text({ sound = "button", volume = 0.7, pitch = 0.9, delay = 0 }, { level = "+1" }) + delay(1.3) + for k, v in pairs(G.GAME.hands) do + level_up_hand(used_consumable, k, true) + end + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) + else + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() --"borrowed" from Wheel Of Fortune + attention_text({ + text = localize("k_nope_ex"), + scale = 1.3, + hold = 1.4, + major = used_consumable, + backdrop_colour = G.C.SECONDARY_SET.Planet, + align = ( + G.STATE == G.STATES.TAROT_PACK + or G.STATE == G.STATES.SPECTRAL_PACK + or G.STATE == G.STATES.SMODS_BOOSTER_OPENED + ) + and "tm" + or "cm", + offset = { + x = 0, + y = ( + G.STATE == G.STATES.TAROT_PACK + or G.STATE == G.STATES.SPECTRAL_PACK + or G.STATE == G.STATES.SMODS_BOOSTER_OPENED + ) + and -0.2 + or 0, + }, + silent = true, + }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.06 * G.SETTINGS.GAMESPEED, + blockable = false, + blocking = false, + func = function() + play_sound("tarot2", 0.76, 0.4) + return true + end, + })) + play_sound("tarot2", 1, 0.4) + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + end + end, + bulk_use = function(self, card, area, copier, number) + local used_consumable = copier or card + local quota = 0 + if card.ability.cry_rigged then + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { handname = localize("k_all_hands"), chips = "...", mult = "...", level = "" } + ) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = true + return true + end, + })) + update_hand_text({ delay = 0 }, { mult = "+", StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.9, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 0 }, { chips = "+", StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.9, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = nil + return true + end, + })) + update_hand_text({ sound = "button", volume = 0.7, pitch = 0.9, delay = 0 }, { level = "+" .. number }) + delay(1.3) + for k, v in pairs(G.GAME.hands) do + level_up_hand(card, k, true, number) + end + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) + else + for i = 1, number do + quota = quota + + (pseudorandom("planetlua") < G.GAME.probabilities.normal / card.ability.extra.odds and 1 or 0) + end + if quota > 0 then + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { handname = localize("k_all_hands"), chips = "...", mult = "...", level = "" } + ) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = true + return true + end, + })) + update_hand_text({ delay = 0 }, { mult = "+", StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.9, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + return true + end, + })) + update_hand_text({ delay = 0 }, { chips = "+", StatusText = true }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.9, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = nil + return true + end, + })) + update_hand_text({ sound = "button", volume = 0.7, pitch = 0.9, delay = 0 }, { level = "+" .. quota }) + delay(1.3) + for k, v in pairs(G.GAME.hands) do + level_up_hand(card, k, true, quota) + end + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) + else + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + attention_text({ + text = localize("k_nope_ex"), + scale = 1.3, + hold = 1.4, + major = used_consumable, + backdrop_colour = G.C.SECONDARY_SET.Planet, + align = ( + G.STATE == G.STATES.TAROT_PACK + or G.STATE == G.STATES.SPECTRAL_PACK + or G.STATE == G.STATES.SMODS_BOOSTER_OPENED + ) + and "tm" + or "cm", + offset = { + x = 0, + y = ( + G.STATE == G.STATES.TAROT_PACK + or G.STATE == G.STATES.SPECTRAL_PACK + or G.STATE == G.STATES.SMODS_BOOSTER_OPENED + ) + and -0.2 + or 0, + }, + silent = true, + }) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.06 * G.SETTINGS.GAMESPEED, + blockable = false, + blocking = false, + func = function() + play_sound("tarot2", 0.76, 0.4) + return true + end, + })) + play_sound("tarot2", 1, 0.4) + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + end + end + end, + calculate = function(self, card, context) --Observatory effect: (G.GAME.probabilities.normal) in (odds) chance for (G.P_CENTERS.v_observatory.config.extra) Mult + if + G.GAME.used_vouchers.v_observatory + and (pseudorandom("nstar") < G.GAME.probabilities.normal / card.ability.extra.odds) + then + local value = G.P_CENTERS.v_observatory.config.extra + return { + message = localize({ type = "variable", key = "a_xmult", vars = { value } }), + Xmult_mod = value, + } + end + end, +} +local nstar = { + object_type = "Consumable", + set = "Planet", + name = "cry-nstar", + key = "nstar", + pos = { x = 4, y = 1 }, + cost = 4, + aurinko = true, + atlas = "atlasnotjokers", + order = 6, + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("k_planet_q"), get_type_colour(self or card.config, card), nil, 1.2) + end, + can_use = function(self, card) + return true + end, + loc_vars = function(self, info_queue, center) + return { vars = { (G.GAME and G.GAME.neutronstarsusedinthisrun or 0) } } + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + --Get amount of Neutron stars use this run or set to 0 if nil + G.GAME.neutronstarsusedinthisrun = G.GAME.neutronstarsusedinthisrun or 0 + + --Add +1 to amount of neutron stars used this run + G.GAME.neutronstarsusedinthisrun = G.GAME.neutronstarsusedinthisrun + 1 + local neutronhand = neutronstarrandomhand() --Random poker hand + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { + handname = localize(neutronhand, "poker_hands"), + chips = G.GAME.hands[neutronhand].chips, + mult = G.GAME.hands[neutronhand].mult, + level = G.GAME.hands[neutronhand].level, + } + ) + --level up once for each neutron star used this run + level_up_hand(used_consumable, neutronhand, nil, G.GAME.neutronstarsusedinthisrun) + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) + end, + bulk_use = function(self, card, area, copier, number) + local used_consumable = copier or card + G.GAME.neutronstarsusedinthisrun = G.GAME.neutronstarsusedinthisrun or 0 + + local handstolv = {} + local neutronhand = "n/a" + for i = 1, number do + G.GAME.neutronstarsusedinthisrun = G.GAME.neutronstarsusedinthisrun + 1 + neutronhand = neutronstarrandomhand() + handstolv[neutronhand] = (handstolv[neutronhand] or 0) + G.GAME.neutronstarsusedinthisrun + end + for k, v in pairs(handstolv) do + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { + handname = localize(k, "poker_hands"), + chips = G.GAME.hands[k].chips, + mult = G.GAME.hands[k].mult, + level = G.GAME.hands[k].level, + } + ) + card_eval_status_text( + used_consumable, + "extra", + nil, + nil, + nil, + { message = "+" .. tostring(v), colour = G.C.BLUE } + ) + level_up_hand(used_consumable, k, nil, v) + end + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) + G.E_MANAGER:add_event( + Event({ + trigger = "after", + func = function() + handstolv = nil + return true + end, + }) + ) + end, + calculate = function(self, card, context) --Observatory effect: X0.04 mult for each neutron star used this run + if G.GAME.used_vouchers.v_observatory and G.GAME.neutronstarsusedinthisrun ~= nil then + return { + message = localize({ + type = "variable", + key = "a_xmult", + vars = { 1 + (0.04 * G.GAME.neutronstarsusedinthisrun) }, + }), + Xmult_mod = 1 + (0.04 * G.GAME.neutronstarsusedinthisrun), + } + end + end, +} +local sunplanet = { + object_type = "Consumable", + set = "Planet", + name = "cry-sunplanet", + key = "sunplanet", + pos = { x = 5, y = 2 }, + cost = 4, + aurinko = true, + atlas = "atlasnotjokers", + order = 7, + set_card_type_badge = function(self, card, badges) + badges[1] = create_badge(localize("cry_p_star"), get_type_colour(self or card.config, card), nil, 1.2) + end, + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local sunlevel = (G.GAME.sunnumber and G.GAME.sunnumber or 0)+1 + delay(0.4) + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize('cry_asc_hands'),chips = '...', mult = '...', level=sunlevel}) + delay(1.0) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function() + play_sound('tarot1') + ease_colour(G.C.UI_CHIPS, copy_table(G.C.GOLD), 0.1) + ease_colour(G.C.UI_MULT, copy_table(G.C.GOLD), 0.1) + cry_pulse_flame(0.01, sunlevel) + used_consumable:juice_up(0.8, 0.5) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + blocking = false, + delay = 1.2, + func = (function() + ease_colour(G.C.UI_CHIPS, G.C.BLUE, 1) + ease_colour(G.C.UI_MULT, G.C.RED, 1) + return true + end) + })) + return true end })) + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.9, delay = 0}, {level=sunlevel+1}) + delay(2.6) + G.GAME.sunnumber = G.GAME.sunnumber ~= nil and G.GAME.sunnumber + 1 or 1 + update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''}) + end, + bulk_use = function(self, card, area, copier, number) + local used_consumable = copier or card + local sunlevel = (G.GAME.sunnumber and G.GAME.sunnumber or 0)+1 + delay(0.4) + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize('cry_asc_hands'),chips = '...', mult = '...', level=sunlevel}) + delay(1.0) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function() + play_sound('tarot1') + ease_colour(G.C.UI_CHIPS, copy_table(G.C.GOLD), 0.1) + ease_colour(G.C.UI_MULT, copy_table(G.C.GOLD), 0.1) + cry_pulse_flame(0.01, (sunlevel-1)+number) + used_consumable:juice_up(0.8, 0.5) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + blocking = false, + delay = 1.2, + func = (function() + ease_colour(G.C.UI_CHIPS, G.C.BLUE, 1) + ease_colour(G.C.UI_MULT, G.C.RED, 1) + return true + end) + })) + return true end })) + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.9, delay = 0}, {level=sunlevel+number}) + delay(2.6) + G.GAME.sunnumber = G.GAME.sunnumber ~= nil and G.GAME.sunnumber + number or number + update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''}) + end, + calculate = function(self, card, context) --Observatory effect: X1.5 mult if hand is an ascended hand + if G.GAME.used_vouchers.v_observatory and G.GAME.current_round.current_hand.cry_asc_num ~= 0 then + local value = G.P_CENTERS.v_observatory.config.extra + return { + message = localize({ type = "variable", key = "a_xmult", vars = { value } }), + Xmult_mod = value, + } + end + end, + loc_vars = function(self, info_queue, center) + local levelone = (G.GAME.sunnumber and G.GAME.sunnumber or 0)+1 + local planetcolourone = G.C.HAND_LEVELS[math.min(levelone, 7)] + if levelone == 1 then + planetcolourone = G.C.UI.TEXT_DARK + end + return { + vars = { + (G.GAME.sunnumber and G.GAME.sunnumber or 0)+1, + ((G.GAME.sunnumber and G.GAME.sunnumber or 0)/20) + 1.25, + colours = { planetcolourone }, + }, + } + end, + in_pool = function(self) + if G.GAME.cry_asc_played and G.GAME.cry_asc_played > 0 then + return true + end + return false + end, +} +function suit_level_up(center, card, area, copier, number) + local used_consumable = copier or card + for _, v in pairs(card.config.center.config.hand_types) do + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { + handname = localize(v, "poker_hands"), + chips = G.GAME.hands[v].chips, + mult = G.GAME.hands[v].mult, + level = G.GAME.hands[v].level, + } + ) + level_up_hand(used_consumable, v, nil, number) + end + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) +end +function neutronstarrandomhand(ignore, seed, allowhidden) + --From JenLib's get_random_hand + local chosen_hand + ignore = ignore or {} + seed = seed or "randomhand" + if type(ignore) ~= "table" then + ignore = { ignore } + end + while true do + chosen_hand = pseudorandom_element(G.handlist, pseudoseed(seed)) + if G.GAME.hands[chosen_hand].visible or allowhidden then + local safe = true + for _, v in pairs(ignore) do + if v == chosen_hand then + safe = false + end + end + if safe then + break + end + end + end + return chosen_hand +end +local planet_cards = { planetlua, nstar, timantti, klubi, sydan, lapio, sunplanet } +if Cryptid.enabled["Misc."] then + planet_cards[#planet_cards + 1] = kaikki +end +if not (SMODS.Mods["jen"] or {}).can_load then +end +return { name = "Planets", init = function() end, items = planet_cards } diff --git a/Cryptid/Items/Sleeves.lua b/Cryptid/Items/Sleeves.lua new file mode 100644 index 0000000..a098887 --- /dev/null +++ b/Cryptid/Items/Sleeves.lua @@ -0,0 +1,372 @@ +if CardSleeves then + local atlasSleeves = SMODS.Atlas({ + object_type = "Atlas", + key = "atlasSleeves", + path = "atlasSleeves.png", + px = 71, + py = 95, + }) + + local encodedsleeve = CardSleeves.Sleeve({ + key = "encoded_sleeve", + name = "Encoded Sleeve", + atlas = "atlasSleeves", + pos = { x = 1, y = 0 }, + config = {}, + unlocked = true, + unlock_condition = { deck = "Encoded Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + + trigger_effect = function(self, args) end, + apply = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + -- Adding a before spawning becuase jen banned copy_paste + if G.P_CENTERS["j_cry_CodeJoker"] and (G.GAME.banned_keys and not G.GAME.banned_keys["j_cry_CodeJoker"]) then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_CodeJoker") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + end + if G.P_CENTERS["j_cry_copypaste"] and (G.GAME.banned_keys and not G.GAME.banned_keys["j_cry_copypaste"]) then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_copypaste") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + end + return true + end + end, + })) + + --DOWNSIDE: + + G.GAME.joker_rate = 0 + G.GAME.planet_rate = 0 + G.GAME.tarot_rate = 0 + G.GAME.code_rate = 1e100 + end, + }) + + local equilibriumsleeve = CardSleeves.Sleeve({ + key = "equilibrium_sleeve", + name = "Balanced Sleeve", + atlas = "atlasSleeves", + pos = { x = 2, y = 0 }, + config = { vouchers = { "v_overstock_norm", "v_overstock_plus" }, cry_equilibrium = true }, + unlocked = true, + unlock_condition = { deck = "Deck of Equilibrium", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + + trigger_effect = function(self, args) end, + apply = function(self) + change_shop_size(2) + G.GAME.modifiers.cry_equilibrium = true + end, + }) + + local misprintsleeve = CardSleeves.Sleeve({ + key = "misprint_sleeve", + name = "Misprinted Sleeve", + atlas = "atlasSleeves", + pos = { x = 3, y = 0 }, + config = { cry_misprint_min = 0.1, cry_misprint_max = 10 }, + unlocked = true, + unlock_condition = { deck = "Misprint Deck", stake = 1 }, + trigger_effect = function(self, args) + if args.context.create_card then + cry_misprintize( + args.context.card, + { min = 0.1 * (G.GAME.modifiers.cry_misprint_min or 1), max = 10 + * (G.GAME.modifiers.cry_misprint_max or 1) } + ) + end + end, + apply = function(self) + G.GAME.modifiers.cry_misprint_min = self.config.cry_misprint_min + G.GAME.modifiers.cry_misprint_max = self.config.cry_misprint_max + end, + }) + + local infinitesleeve = CardSleeves.Sleeve({ + key = "infinite_sleeve", + name = "Unlimited Sleeve", + atlas = "atlasSleeves", + pos = { x = 4, y = 0 }, + config = { cry_highlight_limit = 1e20, hand_size = 1 }, + unlocked = true, + unlock_condition = { deck = "Infinite Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + trigger_effect = function(self, args) end, + apply = function(self) + G.GAME.modifiers.cry_highlight_limit = self.config.cry_highlight_limit + end, + }) + + local conveyorsleeve = CardSleeves.Sleeve({ + key = "conveyor_sleeve", + name = "Conveyor Sleeve", + atlas = "atlasSleeves", + pos = { x = 5, y = 0 }, + config = { cry_conveyor = true }, + unlocked = true, + unlock_condition = { deck = "Conveyor Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + trigger_effect = function(self, args) end, + apply = function(self) + G.GAME.modifiers.cry_conveyor = true + end, + }) + + local CCDsleeve = CardSleeves.Sleeve({ + key = "ccd_sleeve", + name = "CCD Sleeve", + atlas = "atlasSleeves", + pos = { x = 6, y = 0 }, + config = { cry_conveyor = true }, + unlocked = true, + unlock_condition = { deck = "CCD Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + trigger_effect = function(self, args) end, + apply = function(self) + G.GAME.modifiers.cry_ccd = true + end, + }) + + local wormholesleeve = CardSleeves.Sleeve({ + key = "wormhole_sleeve", + name = "Wormhole Sleeve", + atlas = "atlasSleeves", + pos = { x = 0, y = 0 }, + config = { cry_wormhole = true, cry_negative_rate = 20, joker_slot = -2 }, + unlocked = true, + unlock_condition = { deck = "Wormhole Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + apply = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + local card = + create_card("Joker", G.jokers, nil, "cry_exotic", nil, nil, nil, "cry_wormholesleeve") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + return true + end + end, + })) + G.GAME.modifiers.cry_negative_rate = (G.GAME.modifiers.cry_negative_rate or 1) + * self.config.cry_negative_rate + G.GAME.starting_params.joker_slots = G.GAME.starting_params.joker_slots + self.config.joker_slot + end, + }) + + local redeemedsleeve = CardSleeves.Sleeve({ + key = "redeemed_sleeve", + name = "Redeemed Sleeve", + atlas = "atlasSleeves", + pos = { x = 7, y = 0 }, + config = { cry_negative_rate = 20, joker_slot = -2 }, + unlocked = true, + unlock_condition = { deck = "Redeemed Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + apply = function(self) + G.GAME.modifiers.cry_redeemed = true + end, + }) + + local criticalsleeve = CardSleeves.Sleeve({ + key = "critical_sleeve", + name = "Critical Sleeve", + atlas = "atlasSleeves", + pos = { x = 8, y = 0 }, + config = { cry_crit_rate = 0.25, cry_crit_miss_rate = 0.125 }, + unlocked = true, + unlock_condition = { deck = "Redeemed Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + apply = function(self) end, + trigger_effect = function(self, args) + if args.context == "final_scoring_step" then + local crit_poll = pseudorandom(pseudoseed("cry_critical")) + crit_poll = crit_poll / (G.GAME.probabilities.normal or 1) + if crit_poll < self.config.cry_crit_rate then + args.mult = args.mult ^ 2 + update_hand_text({ delay = 0 }, { mult = args.mult, chips = args.chips }) + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("talisman_emult", 1) + attention_text({ + scale = 1.4, + text = localize("cry_critical_hit_ex"), + hold = 2, + align = "cm", + offset = { x = 0, y = -2.7 }, + major = G.play, + }) + return true + end, + })) + elseif crit_poll < self.config.cry_crit_rate + self.config.cry_crit_miss_rate then + args.mult = args.mult ^ 0.5 + update_hand_text({ delay = 0 }, { mult = args.mult, chips = args.chips }) + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("timpani", 1) + attention_text({ + scale = 1.4, + text = localize("cry_critical_miss_ex"), + hold = 2, + align = "cm", + offset = { x = 0, y = -2.7 }, + major = G.play, + }) + return true + end, + })) + end + delay(0.6) + return args.chips, args.mult + end + end, + }) + local legendarysleeve = CardSleeves.Sleeve({ + key = "legendary_sleeve", + name = "Legendary Sleeve", + atlas = "atlasSleeves", + pos = { x = 1, y = 1 }, + config = { cry_legendary = true, cry_legendary_rate = 0.2 }, + unlocked = true, + unlock_condition = { deck = "Legendary Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + trigger_effect = function(self, args) + if args.context == "eval" and G.GAME.last_blind and G.GAME.last_blind.boss then + if G.jokers then + if #G.jokers.cards < G.jokers.config.card_limit then + local legendary_poll = pseudorandom(pseudoseed("cry_legendary")) + legendary_poll = legendary_poll / (G.GAME.probabilities.normal or 1) + if legendary_poll < self.config.cry_legendary_rate then + local card = create_card("Joker", G.jokers, true, 4, nil, nil, nil, "") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + return true + else + card_eval_status_text( + G.jokers, + "jokers", + nil, + nil, + nil, + { message = localize("k_nope_ex"), colour = G.C.RARITY[4] } + ) + end + else + card_eval_status_text( + G.jokers, + "jokers", + nil, + nil, + nil, + { message = localize("k_no_room_ex"), colour = G.C.RARITY[4] } + ) + end + end + end + end, + apply = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + local card = create_card("Joker", G.jokers, true, 4, nil, nil, nil, "") + card:add_to_deck() + card:start_materialize() + G.jokers:emplace(card) + return true + end + end, + })) + end, + }) + local spookysleeve = CardSleeves.Sleeve({ + key = "spooky_sleeve", + name = "Spooky Sleeve", + atlas = "atlasSleeves", + pos = { x = 2, y = 1 }, + config = { cry_spooky = true, cry_curse_rate = 0.25 }, + unlocked = true, + unlock_condition = { deck = "Spooky Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + + trigger_effect = function(self, args) end, + apply = function(self) + G.GAME.modifiers.cry_spooky = true + G.GAME.modifiers.cry_curse_rate = self.config.cry_curse_rate or 0.25 + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_chocolate_dice") + card:add_to_deck() + card:start_materialize() + card:set_eternal(true) + G.jokers:emplace(card) + return true + end + end, + })) + end, + }) + local bountifulsleeve = CardSleeves.Sleeve({ + key = "bountiful_sleeve", + name = "Bountiful Sleeve", + atlas = "atlasSleeves", + pos = { x = 0, y = 2 }, + config = { cry_forced_draw_amount = 5 }, + unlocked = true, + unlock_condition = { deck = "Bountiful Deck", stake = 1 }, + loc_vars = function(self) + return { vars = {} } + end, + + trigger_effect = function(self, args) end, + apply = function(self) + G.GAME.modifiers.cry_forced_draw_amount = self.config.cry_forced_draw_amount + end, + }) + local sleeveitems = { atlasSleeves } + if CardSleeves and Cryptid.enabled["Misc. Decks"] then + sleeveitems[#sleeveitems + 1] = encodedsleeve + sleeveitems[#sleeveitems + 1] = equilibriumsleeve + sleeveitems[#sleeveitems + 1] = misprintsleeve + sleeveitems[#sleeveitems + 1] = infinitesleeve + sleeveitems[#sleeveitems + 1] = conveyorsleeve + sleeveitems[#sleeveitems + 1] = CCDsleeve + sleeveitems[#sleeveitems + 1] = wormholesleeve + sleeveitems[#sleeveitems + 1] = redeemedsleeve + sleeveitems[#sleeveitems + 1] = criticalsleeve + sleeveitems[#sleeveitems + 1] = legendarysleeve + sleeveitems[#sleeveitems + 1] = spookysleeve + sleeveitems[#sleeveitems + 1] = bountifulsleeve + end +end +return { name = "Sleeves", init = function() end, items = { sleeveitems } } diff --git a/Cryptid/Items/Spectrals.lua b/Cryptid/Items/Spectrals.lua new file mode 100644 index 0000000..08085e6 --- /dev/null +++ b/Cryptid/Items/Spectrals.lua @@ -0,0 +1,1274 @@ +local white_hole = { + object_type = "Consumable", + set = "Spectral", + name = "cry-White Hole", + key = "white_hole", + pos = { x = 1, y = 4 }, + cost = 4, + order = 40, + atlas = "atlasnotjokers", + hidden = true, --default soul_rate of 0.3% in spectral packs is used + soul_set = "Planet", + can_use = function(self, card) + return true + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + --Get most played hand type (logic yoinked from Telescope) + local _planet, _hand, _tally = nil, nil, -1 + for k, v in ipairs(G.handlist) do + if G.GAME.hands[v].visible and G.GAME.hands[v].played > _tally then + _hand = v + _tally = G.GAME.hands[v].played + end + end + if _hand then + for k, v in pairs(G.P_CENTER_POOLS.Planet) do + if v.config.hand_type == _hand then + _planet = v.key + end + end + end + local removed_levels = 0 + for k, v in ipairs(G.handlist) do + if G.GAME.hands[v].level > 1 then + local this_removed_levels = G.GAME.hands[v].level - 1 + removed_levels = removed_levels + this_removed_levels + level_up_hand(used_consumable, v, true, -this_removed_levels) + end + end + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { + handname = localize(_hand, "poker_hands"), + chips = G.GAME.hands[_hand].chips, + mult = G.GAME.hands[_hand].mult, + level = G.GAME.hands[_hand].level, + } + ) + level_up_hand(used_consumable, _hand, false, 3 * removed_levels) + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) + end, + --Incantation compat + can_stack = true, + can_divide = true, + can_bulk_use = true, + bulk_use = function(self, card, area, copier, number) + local used_consumable = copier or card + --Get most played hand type (logic yoinked from Telescope) + local _planet, _hand, _tally = nil, nil, -1 + for k, v in ipairs(G.handlist) do + if G.GAME.hands[v].visible and G.GAME.hands[v].played > _tally then + _hand = v + _tally = G.GAME.hands[v].played + end + end + if _hand then + for k, v in pairs(G.P_CENTER_POOLS.Planet) do + if v.config.hand_type == _hand then + _planet = v.key + end + end + end + local removed_levels = 0 + for k, v in ipairs(G.handlist) do + if G.GAME.hands[v].level > 1 then + local this_removed_levels = G.GAME.hands[v].level - 1 + removed_levels = removed_levels + this_removed_levels + level_up_hand(used_consumable, v, true, -this_removed_levels) + end + end + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { + handname = localize(_hand, "poker_hands"), + chips = G.GAME.hands[_hand].chips, + mult = G.GAME.hands[_hand].mult, + level = G.GAME.hands[_hand].level, + } + ) + level_up_hand(used_consumable, _hand, false, removed_levels * 3 ^ number) + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) + end, +} +local vacuum = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Vacuum", + key = "vacuum", + pos = { x = 3, y = 1 }, + config = { extra = 4 }, + cost = 4, + order = 2, + atlas = "atlasnotjokers", + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra } } + end, + can_use = function(self, card) + return #G.hand.cards > 0 + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local earnings = 0 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + for i = 1, #G.hand.cards do + local percent = 1.15 - (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.cards[i]:flip() + play_sound("card1", percent) + G.hand.cards[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.hand.cards do + local CARD = G.hand.cards[i] + if CARD.config.center ~= G.P_CENTERS.c_base then + earnings = earnings + 1 + end + if CARD.edition then + earnings = earnings + 1 + end + if CARD.seal then + earnings = earnings + 1 + end + local percent = 0.85 + (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + CARD:set_ability(G.P_CENTERS.c_base, true, nil) + CARD:set_edition(nil, true) + CARD:set_seal(nil, true) + play_sound("tarot2", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + ease_dollars(earnings * card.ability.extra) + end, +} +local hammerspace = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Hammerspace", + key = "hammerspace", + pos = { x = 4, y = 3 }, + config = {}, + cost = 4, + order = 3, + atlas = "atlasnotjokers", + can_use = function(self, card) + return #G.hand.cards > 0 + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + for i = 1, #G.hand.cards do + local percent = 1.15 - (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.cards[i]:flip() + play_sound("card1", percent) + G.hand.cards[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.hand.cards do + local CARD = G.hand.cards[i] + local percent = 0.85 + (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + CARD:set_ability(get_random_consumable("cry_hammerspace", nil, "c_cry_hammerspace", nil, true)) + play_sound("tarot2", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + end, +} +local lock = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Lock", + key = "lock", + pos = { x = 0, y = 1 }, + config = {}, + cost = 4, + order = 1, + atlas = "atlasnotjokers", + can_use = function(self, card) + return #G.jokers.cards > 0 + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local target = #G.jokers.cards == 1 and G.jokers.cards[1] or G.jokers.cards[math.random(#G.jokers.cards)] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + for i = 1, #G.jokers.cards do + local percent = 1.15 - (i - 0.999) / (#G.jokers.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.jokers.cards[i]:flip() + play_sound("card1", percent) + G.jokers.cards[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.jokers.cards do + local CARD = G.jokers.cards[i] + local percent = 0.85 + (i - 0.999) / (#G.jokers.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + CARD.ability.perishable = nil + CARD.pinned = nil + CARD:set_rental(nil) + if not CARD.sob then + CARD:set_eternal(nil) + end + CARD.ability.banana = nil + if Cryptid.enabled["Spooky"] then + CARD.ability.cry_possessed = nil + SMODS.Stickers.cry_flickering:apply(CARD, nil) + end + play_sound("card1", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot2") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + play_sound("card1", 0.9) + target:flip() + return true + end, + })) + delay(0.2) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + func = function() + play_sound("gold_seal", 1.2, 0.4) + target:juice_up(0.3, 0.3) + return true + end, + })) + delay(0.2) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + play_sound("card1", 1.1) + target:flip() + target:set_eternal(true) + return true + end, + })) + end, +} +local trade = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Trade", + key = "trade", + pos = { x = 2, y = 1 }, + config = {}, + cost = 4, + order = 4, + atlas = "atlasnotjokers", + can_use = function(self, card) + local usable_count = 0 + for _, v in pairs(G.GAME.used_vouchers) do + if v then + usable_count = usable_count + 1 + end + end + if G.GAME.voucher_sticker_index and G.GAME.voucher_sticker_index.eternal then + for _, v in pairs(G.GAME.voucher_sticker_index.eternal) do + if v then + usable_count = usable_count - 1 + end + end + end + if usable_count > 0 then + return true + else + return false + end + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local usable_vouchers = {} + for k, _ in pairs(G.GAME.used_vouchers) do + local can_use = true + for kk, __ in pairs(G.GAME.used_vouchers) do + local v = G.P_CENTERS[kk] + if v.requires then + for _, vv in pairs(v.requires) do + if vv == k then + can_use = false + break + end + end + end + if + G.GAME.voucher_sticker_index + and G.GAME.voucher_sticker_index.eternal + and G.GAME.voucher_sticker_index.eternal[v.name] + then + can_use = false + end + end + if can_use then + usable_vouchers[#usable_vouchers + 1] = k + end + end + local unredeemed_voucher = pseudorandom_element(usable_vouchers, pseudoseed("cry_trade")) + --redeem extra voucher code based on Betmma's Vouchers + local area + if G.STATE == G.STATES.HAND_PLAYED then + if not G.redeemed_vouchers_during_hand then + G.redeemed_vouchers_during_hand = + CardArea(G.play.T.x, G.play.T.y, G.play.T.w, G.play.T.h, { type = "play", card_limit = 5 }) + end + area = G.redeemed_vouchers_during_hand + else + area = G.play + end + local card = create_card("Voucher", area, nil, nil, nil, nil, unredeemed_voucher) + + if G.GAME.voucher_edition_index[card.ability.name] then + local edition = cry_edition_to_table(G.GAME.voucher_edition_index[card.ability.name]) + if edition then + card:set_edition(edition, true, true) + end + end + if G.GAME.voucher_sticker_index.eternal[card.ability.name] then + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.voucher_sticker_index.perishable[card.ability.name] then + card:set_perishable(true) + card.ability.perish_tally = G.GAME.voucher_sticker_index.perishable[card.ability.name] + card.ability.perishable = true + if G.GAME.voucher_sticker_index.perishable[card.ability.name] == 0 then + card.debuff = true + end + end + if G.GAME.voucher_sticker_index.rental[card.ability.name] then + card:set_rental(true) + card.ability.rental = true + end + if G.GAME.voucher_sticker_index.pinned[card.ability.name] then + card.pinned = true + end + if G.GAME.voucher_sticker_index.banana[card.ability.name] then + card.ability.banana = true + end + card:start_materialize() + area:emplace(card) + card.cost = 0 + card.shop_voucher = false + local current_round_voucher = G.GAME.current_round.voucher + card:unredeem() + G.GAME.current_round.voucher = current_round_voucher + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + func = function() + card:start_dissolve() + return true + end, + })) + for i = 1, 2 do + local area + if G.STATE == G.STATES.HAND_PLAYED then + if not G.redeemed_vouchers_during_hand then + G.redeemed_vouchers_during_hand = + CardArea(G.play.T.x, G.play.T.y, G.play.T.w, G.play.T.h, { type = "play", card_limit = 5 }) + end + area = G.redeemed_vouchers_during_hand + else + area = G.play + end + local _pool = get_current_pool("Voucher", nil, nil, nil, true) + local center = pseudorandom_element(_pool, pseudoseed("cry_trade_redeem")) + local it = 1 + while center == "UNAVAILABLE" do + it = it + 1 + center = pseudorandom_element(_pool, pseudoseed("cry_trade_redeem_resample" .. it)) + end + local card = create_card("Voucher", area, nil, nil, nil, nil, center) + card:start_materialize() + area:emplace(card) + card.cost = 0 + card.shop_voucher = false + local current_round_voucher = G.GAME.current_round.voucher + card:redeem() + G.GAME.current_round.voucher = current_round_voucher + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0, + func = function() + card:start_dissolve() + return true + end, + })) + end + end, +} +local analog = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Analog", + key = "analog", + pos = { x = 3, y = 0 }, + config = { copies = 2, ante = 1 }, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.copies, center.ability.ante } } + end, + cost = 4, + order = 7, + atlas = "atlasnotjokers", + can_use = function(self, card) + return #G.jokers.cards > 0 + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local deletable_jokers = {} + for k, v in pairs(G.jokers.cards) do + if not v.ability.eternal then + deletable_jokers[#deletable_jokers + 1] = v + end + end + local chosen_joker = pseudorandom_element(G.jokers.cards, pseudoseed("cry_analog_choice")) + local _first_dissolve = nil + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.75, + func = function() + for k, v in pairs(deletable_jokers) do + if v ~= chosen_joker then + v:start_dissolve(nil, _first_dissolve) + _first_dissolve = true + end + end + return true + end, + })) + for i = 1, card.ability.copies do + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.4, + func = function() + local card = copy_card(chosen_joker) + card:start_materialize() + card:add_to_deck() + G.jokers:emplace(card) + return true + end, + })) + end + ease_ante(card.ability.ante) + end, +} +local summoning = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Summoning", + key = "summoning", + pos = { x = 3, y = 4 }, + cost = 4, + order = 5, + atlas = "atlasnotjokers", + can_use = function(self, card) + return #G.jokers.cards > 0 + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local deletable_jokers = {} + for k, v in pairs(G.jokers.cards) do + if not v.ability.eternal then + deletable_jokers[#deletable_jokers + 1] = v + end + end + local chosen_joker = pseudorandom_element(G.jokers.cards, pseudoseed("cry_summoning")) + local _first_dissolve = nil + G.E_MANAGER:add_event(Event({ + trigger = "before", + delay = 0.75, + func = function() + for k, v in pairs(deletable_jokers) do + if v == chosen_joker then + v:start_dissolve(nil, _first_dissolve) + _first_dissolve = true + end + end + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("timpani") + local card = create_card("Joker", G.jokers, nil, "cry_epic", nil, nil, nil, "cry_summoning") + card:add_to_deck() + G.jokers:emplace(card) + card:juice_up(0.3, 0.5) + return true + end, + })) + delay(0.6) + end, +} +local replica = { + object_type = "Consumable", + set = "Spectral", + name = "cry-Replica", + key = "replica", + pos = { x = 1, y = 1 }, + config = {}, + cost = 4, + order = 6, + atlas = "atlasnotjokers", + can_use = function(self, card) + return #G.hand.cards > 0 + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local chosen_card = pseudorandom_element(G.hand.cards, pseudoseed("cry_replica_choice")) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + for i = 1, #G.hand.cards do + local percent = 1.15 - (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.cards[i]:flip() + play_sound("card1", percent) + G.hand.cards[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + for i = 1, #G.hand.cards do + if not G.hand.cards[i].ability.eternal then + G.E_MANAGER:add_event(Event({ + func = function() + copy_card(chosen_card, G.hand.cards[i]) + return true + end, + })) + end + end + for i = 1, #G.hand.cards do + local percent = 0.85 + (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.hand.cards[i]:flip() + play_sound("tarot2", percent, 0.6) + G.hand.cards[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.5) + end, +} +local ritual = { + cry_credits = { + idea = {"Mystic Misclick"}, + art = {"spire_winder"}, + code = {"spire_winder"} + }, + object_type = "Consumable", + set = "Spectral", + name = "cry-Ritual", + key = "ritual", + order = 9, + config = { + max_highlighted = 1, + }, + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.cry_mosaic) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_mosaic + end + if not center.edition or (center.edition and not center.edition.negative) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_negative + end + if not center.edition or (center.edition and not center.edition.cry_astral) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_astral + end + return { vars = { center.ability.max_highlighted } } + end, + cost = 5, + atlas = "atlasnotjokers", + pos = { x = 5, y = 1 }, + use = function(self, card, area, copier) + local used_consumable = copier or card + for i = 1, #G.hand.highlighted do + local highlighted = G.hand.highlighted[i] + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + highlighted:juice_up(0.3, 0.5) + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.1, + func = function() + if highlighted then + local random_result = pseudorandom(pseudoseed("cry-Ritual")) + if random_result >= 5 / 6 then + highlighted:set_edition({cry_astral = true}) + else + if random_result >= 1 / 2 then + highlighted:set_edition({cry_mosaic = true}) + else + highlighted:set_edition({negative = true}) + end + end + end + return true + end, + })) + delay(0.5) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + G.hand:unhighlight_all() + return true + end, + })) + end + end, +} +local adversary = { + cry_credits = { + idea = {"y_not_tony"}, + art = {"Pyrocreep"}, + code = {"spire_winder"} + }, + object_type = "Consumable", + set = "Spectral", + name = "cry-Adversary", + key = "adversary", + pos = { x = 6, y = 1 }, + config = {}, + cost = 4, + order = 10, + atlas = "atlasnotjokers", + loc_vars = function(self, info_queue, center) + if not center.edition or (center.edition and not center.edition.negative) then + info_queue[#info_queue + 1] = G.P_CENTERS.e_negative + end + end, + can_use = function(self, card) + return #G.jokers.cards > 0 + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local target = #G.jokers.cards == 1 and G.jokers.cards[1] or G.jokers.cards[math.random(#G.jokers.cards)] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + for i = 1, #G.jokers.cards do + local percent = 1.15 - (i - 0.999) / (#G.jokers.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + G.jokers.cards[i]:flip() + play_sound("card1", percent) + G.jokers.cards[i]:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + for i = 1, #G.jokers.cards do + local CARD = G.jokers.cards[i] + local percent = 0.85 + (i - 0.999) / (#G.jokers.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + CARD:flip() + if not CARD.edition then CARD:set_edition({negative = true}) end + play_sound("card1", percent) + CARD:juice_up(0.3, 0.3) + return true + end, + })) + end + delay(0.2) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot2") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.cry_shop_joker_price_modifier = G.GAME.cry_shop_joker_price_modifier * 2 + for k, v in pairs(G.I.CARD) do + if v.set_cost then + v:set_cost() + end + end + return true + end, + })) + end, +} +local chambered = { + cry_credits = { + idea = {"y_not_tony"}, + art = {"Pyrocreep"}, + code = {"spire_winder"} + }, + object_type = "Consumable", + set = "Spectral", + name = "cry-Chambered", + key = "chambered", + pos = { x = 5, y = 0 }, + config = { extra = {num_copies = 3}}, + loc_vars = function(self, info_queue, card) + info_queue[#info_queue + 1] = { key = "e_negative_consumable", set = "Edition", config = { extra = 1 } } + return { vars = { card.ability.extra.num_copies } } + end, + cost = 4, + order = 11, + atlas = "atlasnotjokers", + can_use = function(self, card) + local filteredCons = {} + + -- Copy consumables that aren't Chambered + for _, item in ipairs(G.consumeables.cards) do + if item.ability.name ~= "cry-Chambered" then + table.insert(filteredCons, item) + end + end + return #filteredCons > 0 + end, + use = function(self, card, area, copier) + local filteredCons = {} + + -- Copy consumables that aren't Chambered + for _, item in ipairs(G.consumeables.cards) do + if item.ability.name ~= "cry-Chambered" then + table.insert(filteredCons, item) + end + end + target = pseudorandom_element(filteredCons, pseudoseed('chambered')) + for i=1,card.ability.extra.num_copies do + G.E_MANAGER:add_event(Event({ + func = function() + local card_copy = copy_card(target, nil) + if Incantation then + card_copy:setQty(1) + end + card_copy:set_edition({negative = true}, true) + card_copy:add_to_deck() + G.consumeables:emplace(card_copy) + return true + end})) + card_eval_status_text(target, 'extra', nil, nil, nil, {message = localize('k_duplicated_ex'), colour = G.C.SECONDARY_SET.Spectral}) + end + end, +} +local conduit = { + cry_credits = { + idea = {"Knockback1 (Oiiman)"}, + art = {"Knockback1 (Oiiman)"}, + code = {"spire_winder"} + }, + object_type = "Consumable", + set = "Spectral", + name = "cry-conduit", + key = "conduit", + pos = { x = 6, y = 0 }, + config = { }, + cost = 4, + order = 12, + atlas = "atlasnotjokers", + can_use = function(self, card) + local combinedTable = {} + + for _, value in ipairs(G.hand.highlighted) do + if value ~= card then + table.insert(combinedTable, value) + end + end + + for _, value in ipairs(G.jokers.highlighted) do + if value ~= card then + table.insert(combinedTable, value) + end + end + return (#combinedTable == 2) + end, + use = function(self, card, area, copier) + local used_consumable = copier or card + local combinedTable = {} + + for _, value in ipairs(G.hand.highlighted) do + if value ~= card then + table.insert(combinedTable, value) + end + end + + for _, value in ipairs(G.jokers.highlighted) do + if value ~= card then + table.insert(combinedTable, value) + end + end + local highlighted_1 = combinedTable[1] + local highlighted_2 = combinedTable[2] + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + local percent = 1.15 - (1 - 0.999) / (1 - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + highlighted_1:flip() + highlighted_2:flip() + play_sound("card1", percent) + highlighted_1:juice_up(0.3, 0.3) + highlighted_2:juice_up(0.3, 0.3) + return true + end, + })) + delay(0.2) + local percent = 0.85 + (1 - 0.999) / (1 - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.15, + func = function() + local one_edition = highlighted_1.edition + highlighted_1:flip() + highlighted_1:set_edition(highlighted_2.edition) + highlighted_2:flip() + highlighted_2:set_edition(one_edition) + play_sound("card1", percent) + highlighted_1:juice_up(0.3, 0.3) + highlighted_2:juice_up(0.3, 0.3) + return true + end, + })) + delay(0.2) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot2") + used_consumable:juice_up(0.3, 0.5) + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.2, + func = function() + G.hand:unhighlight_all() + G.jokers:unhighlight_all() + return true + end, + })) + end, +} +local spectrals = { + white_hole, + vacuum, + hammerspace, + lock, + trade, + analog, + replica, + adversary, + chambered, + conduit, +} +if Cryptid.enabled["Epic Jokers"] then + spectrals[#spectrals + 1] = summoning +end +if Cryptid.enabled["Misc."] then + spectrals[#spectrals + 1] = ritual +end +return { + name = "Spectrals", + init = function() + --Trade - undo redeeming vouchers + function Card:unredeem() + if self.ability.set == "Voucher" then + stop_use() + if not self.config.center.discovered then + discover_card(self.config.center) + end + + self.states.hover.can = false + if G.GAME.used_vouchers[self.config.center_key] then + G.GAME.used_vouchers[self.config.center_key] = nil + end + G.GAME.cry_owned_vouchers[self.config.center_key] = nil + local top_dynatext = nil + local bot_dynatext = nil + + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + top_dynatext = DynaText({ + string = localize({ + type = "name_text", + set = self.config.center.set, + key = self.config.center.key, + }), + colours = { G.C.RED }, + rotate = 1, + shadow = true, + bump = true, + float = true, + scale = 0.9, + pop_in = 0.6 / G.SPEEDFACTOR, + pop_in_rate = 1.5 * G.SPEEDFACTOR, + }) + bot_dynatext = DynaText({ + string = localize("cry_unredeemed"), + colours = { G.C.RED }, + rotate = 2, + shadow = true, + bump = true, + float = true, + scale = 0.9, + pop_in = 1.4 / G.SPEEDFACTOR, + pop_in_rate = 1.5 * G.SPEEDFACTOR, + pitch_shift = 0.25, + }) + self:juice_up(0.3, 0.5) + play_sound("card1") + play_sound("timpani") + self.children.top_disp = UIBox({ + definition = { + n = G.UIT.ROOT, + config = { align = "tm", r = 0.15, colour = G.C.CLEAR, padding = 0.15 }, + nodes = { + { n = G.UIT.O, config = { object = top_dynatext } }, + }, + }, + config = { align = "tm", offset = { x = 0, y = 0 }, parent = self }, + }) + self.children.bot_disp = UIBox({ + definition = { + n = G.UIT.ROOT, + config = { align = "tm", r = 0.15, colour = G.C.CLEAR, padding = 0.15 }, + nodes = { + { n = G.UIT.O, config = { object = bot_dynatext } }, + }, + }, + config = { align = "bm", offset = { x = 0, y = 0 }, parent = self }, + }) + return true + end, + })) + G.GAME.current_round.voucher = nil + + self:unapply_to_run() + + delay(0.6) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 2.6, + func = function() + top_dynatext:pop_out(4) + bot_dynatext:pop_out(4) + return true + end, + })) + + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.5, + func = function() + self.children.top_disp:remove() + self.children.top_disp = nil + self.children.bot_disp:remove() + self.children.bot_disp = nil + return true + end, + })) + end + end + function Card:unapply_to_run(center) + local center_table = { + name = center and center.name or self and self.ability.name, + extra = self and G.GAME.cry_voucher_centers[self.config.center_key].config.extra, + } + local obj = center or self.config.center + if obj.unredeem and type(obj.unredeem) == "function" then + obj:unredeem(self) + return + end + local is_debuffed = false + if + G.GAME.voucher_sticker_index.perishable[center_table.name] + and G.GAME.voucher_sticker_index.perishable[center_table.name] == 0 + then + is_debuffed = true + end + if G.GAME.voucher_sticker_index.eternal[center_table.name] then + G.GAME.voucher_sticker_index.eternal[center_table.name] = nil + end + if G.GAME.voucher_sticker_index.perishable[center_table.name] then + G.GAME.voucher_sticker_index.perishable[center_table.name] = nil + end + if G.GAME.voucher_sticker_index.rental[center_table.name] then + G.GAME.voucher_sticker_index.rental[center_table.name] = nil + end + if G.GAME.voucher_sticker_index.pinned[center_table.name] then + G.GAME.voucher_sticker_index.pinned[center_table.name] = nil + end + if G.GAME.voucher_sticker_index.banana[center_table.name] then + G.GAME.voucher_sticker_index.banana[center_table.name] = nil + end + + if is_debuffed == false then + if center_table.name == "Overstock" or center_table.name == "Overstock Plus" then + G.E_MANAGER:add_event(Event({ + func = function() + change_shop_size(-center_table.extra) + return true + end, + })) + end + if center_table.name == "Tarot Merchant" or center_table.name == "Tarot Tycoon" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.tarot_rate = G.GAME.tarot_rate / center_table.extra + return true + end, + })) + end + if center_table.name == "Planet Merchant" or center_table.name == "Planet Tycoon" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.planet_rate = G.GAME.planet_rate / center_table.extra + return true + end, + })) + end + if center_table.name == "Hone" or center_table.name == "Glow Up" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.edition_rate = G.GAME.edition_rate / center_table.extra + return true + end, + })) + end + if center_table.name == "Magic Trick" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.playing_card_rate = 0 + return true + end, + })) + end + if center_table.name == "Crystal Ball" then + G.E_MANAGER:add_event(Event({ + func = function() + G.consumeables.config.card_limit = G.consumeables.config.card_limit - center_table.extra + return true + end, + })) + end + if center_table.name == "Clearance Sale" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.discount_percent = 0 + for k, v in pairs(G.I.CARD) do + if v.set_cost then + v:set_cost() + end + end + return true + end, + })) + end + if center_table.name == "Liquidation" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.discount_percent = 25 -- no idea why the below returns nil, so it's hardcoded now + -- G.GAME.discount_percent = G.P_CENTERS.v_clearance_sale.extra + for k, v in pairs(G.I.CARD) do + if v.set_cost then + v:set_cost() + end + end + return true + end, + })) + end + if center_table.name == "Reroll Surplus" or center_table.name == "Reroll Glut" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost + self.ability.extra + G.GAME.current_round.reroll_cost = + math.max(0, G.GAME.current_round.reroll_cost + self.ability.extra) + return true + end, + })) + end + if center_table.name == "Seed Money" then + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.interest_cap = 25 --note: does not account for potential deck effects + return true + end, + })) + end + if center_table.name == "Money Tree" then + G.E_MANAGER:add_event(Event({ + func = function() + if G.GAME.used_vouchers.v_seed_money then + G.GAME.interest_cap = 50 + else + G.GAME.interest_cap = 25 + end + return true + end, + })) + end + if center_table.name == "Grabber" or center_table.name == "Nacho Tong" then + G.GAME.round_resets.hands = G.GAME.round_resets.hands - center_table.extra + ease_hands_played(-center_table.extra) + end + if center_table.name == "Paint Brush" or center_table.name == "Palette" then + G.hand:change_size(-center_table.extra) + end + if center_table.name == "Wasteful" or center_table.name == "Recyclomancy" then + G.GAME.round_resets.discards = G.GAME.round_resets.discards - center_table.extra + ease_discard(-center_table.extra) + end + if center_table.name == "Antimatter" then + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + G.jokers.config.card_limit = G.jokers.config.card_limit - center_table.extra + end + return true + end, + })) + end + if center_table.name == "Hieroglyph" or center_table.name == "Petroglyph" then + ease_ante(center_table.extra) + G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante or G.GAME.round_resets.ante + G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante + center_table.extra + + if center_table.name == "Hieroglyph" then + G.GAME.round_resets.hands = G.GAME.round_resets.hands + center_table.extra + ease_hands_played(center_table.extra) + end + if center_table.name == "Petroglyph" then + G.GAME.round_resets.discards = G.GAME.round_resets.discards + center_table.extra + ease_discard(center_table.extra) + end + end + end + end + end, + items = spectrals, +} diff --git a/Cryptid/Items/Spooky.lua b/Cryptid/Items/Spooky.lua new file mode 100644 index 0000000..2ea2032 --- /dev/null +++ b/Cryptid/Items/Spooky.lua @@ -0,0 +1,1467 @@ +local cotton_candy = { + object_type = "Joker", + key = "cotton_candy", + pos = { x = 2, y = 0 }, + rarity = "cry_candy", + cost = 10, + atlas = "atlasspooky", + order = 130, + blueprint_compat = true, + eternal_compat = false, + perishable_compat = false, + calculate = function(self, card, context) + if context.selling_self and not context.retrigger_joker and not context.blueprint_card then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + if i > 1 then + G.jokers.cards[i-1]:set_edition{negative = true} + end + if i < #G.jokers.cards then + G.jokers.cards[i+1]:set_edition{negative = true} + end + end + end + end + end +} +local wrapped = { + object_type = "Joker", + key = "wrapped", + pos = { x = 5, y = 0 }, + rarity = "cry_candy", + cost = 10, + atlas = "atlasspooky", + eternal_compat = false, + perishable_compat = false, + order = 131, + immune_to_chemach = true, + config = {extra = {rounds = 2}}, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { set = "Other", key = "food_jokers" } + return { vars = { center.ability.extra.rounds } } + end, + calculate = function(self, card, context) + if + context.end_of_round + and not context.blueprint + and not context.individual + and not context.repetition + and not context.retrigger_joker + then + card.ability.extra.rounds = card.ability.extra.rounds - 1 + if card.ability.extra.rounds > 0 then + return { + message = { localize("cry_minus_round") }, + colour = G.C.FILTER, + } + else + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + local card = create_card( + "Joker", + G.jokers, + nil, + nil, + nil, + nil, + Cryptid.get_food("cry_wrapped") + ) + card:add_to_deck() + G.jokers:emplace(card) + return { + message = localize("k_extinct_ex"), + colour = G.C.FILTER, + } + end + end + end, +} +local choco_dice = { + object_type = "Joker", + key = "chocolate_dice", + pos = { x = 1, y = 0 }, + rarity = 3, + cost = 10, + order = 132, + atlas = "atlasspooky", + config = {extra = {roll = 0}}, + immutable = true, + no_dbl = true, + loc_vars = function(self, info_queue, center) + if not center then --tooltip + elseif not center.added_to_deck then + for i = 1, 10 do + info_queue[#info_queue + 1] = { set = "Other", key = "ev_cry_choco"..i } + end + else + SMODS.Events["ev_cry_choco"..center.ability.extra.roll]:loc_vars(info_queue, center) + end + return { vars = { not center and "None" or center.ability.extra.roll == 0 and "None" or center.ability.extra.roll } } + end, + calculate = function(self, card, context) + if context.end_of_round and not context.individual and not context.repetition and not context.blueprint and not context.retrigger_joker and G.GAME.blind.boss then + --todo: check if duplicates of event are already started/finished + SMODS.Events["ev_cry_choco"..card.ability.extra.roll]:finish() + card.ability.extra.roll = roll_dice("cry_choco", 1, 10, {ignore_value = card.ability.extra.roll}) + SMODS.Events["ev_cry_choco"..card.ability.extra.roll]:start() + return { + message = tostring(card.ability.extra.roll), + colour = G.C.GREEN + } + end + end, + remove_from_deck = function(self, card, from_debuff) + if not from_debuff then + SMODS.Events["ev_cry_choco"..card.ability.extra.roll]:finish() + end + + end, +} +local choco_base_event = { + object_type = "Event", + key = "choco0" +} +local choco1 = { + object_type = "Event", + key = "choco1", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { set = "Other", key = self.key } --todo specific_vars + info_queue[#info_queue + 1] = { set = "Other", key = "cry_flickering_desc", specific_vars = {5} } + info_queue[#info_queue + 1] = { set = "Joker", key = "j_cry_ghost", specific_vars = {G.GAME.probabilities.normal or 1,2,6}} + end, + start = function(self) + G.GAME.events[self.key] = true + local areas = {"jokers","deck","hand","play","discard"} + for k, v in pairs(areas) do + for i = 1, #G[v].cards do + if pseudorandom(pseudoseed("cry_choco_possession")) < G.GAME.probabilities.normal / 3 then + SMODS.Stickers.cry_flickering:apply(G[v].cards[i], true) + end + end + end + --create a ghost + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_ghost") + card:add_to_deck() + G.jokers:emplace(card) + end +} +local choco2 = { + object_type = "Event", + key = "choco2", + --everything here is done with lovely patches or hooks, search for ev_cry_choco2 + calculate = function(self, context) + if context.cash_out then + G.GAME.current_round.rerolled = false + end + end +} +local num_potions = 3 --note: must be changed whenever new potion effects are added +local choco3 = { + object_type = "Event", + key = "choco3", + start = function(self) + if not G.GAME.events[self.key] then + G.GAME.events[self.key] = {potions = {}} + end + for i = 1, 3 do + local card = create_card("Unique", G.consumeables, nil, nil, nil, nil, "c_cry_potion") + card:add_to_deck() + card.ability.random_event = pseudorandom(pseudoseed("cry_choco_witch"),1,num_potions) + G.consumeables:emplace(card) + end + end, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { set = "Other", key = self.key } --todo specific_vars + info_queue[#info_queue + 1] = { set = "Unique", key = "c_cry_potion" } -- bugged rn + end, + finish = function(self) + --Reverse all potion effects + if G.GAME.events[self.key].potions[2] then + G.GAME.starting_params.ante_scaling = G.GAME.starting_params.ante_scaling / (1.15^G.GAME.events[self.key].potions[2]) + end + if G.GAME.events[self.key].potions[3] then + G.GAME.round_resets.hands = G.GAME.round_resets.hands + G.GAME.events[self.key].potions[3] + ease_hands_played(G.GAME.events[self.key].potions[3]) + G.GAME.round_resets.discards = G.GAME.round_resets.discards + G.GAME.events[self.key].potions[3] + ease_discard(G.GAME.events[self.key].potions[3]) + end + G.GAME.events[self.key] = nil + end, + calculate = function(self, context) + --bug: if this event finishes and starts, every potion gets instantly destroyed + --bug: crashes if all 3 are used on blind skip + if context.pre_jokers and (context.skip_blind or (context.end_of_round and not context.individual and not context.repetition)) and not context.blueprint and not context.retrigger_joker then + --Detect if a potion has been used + local used_potion = false + for i = 1, num_potions do + if G.GAME.events[self.key].potions[i] then + used_potion = true + break + end + end + if used_potion then + G.E_MANAGER:add_event(Event({ + func = function() + for i = #G.consumeables.cards, 1, -1 do + if G.consumeables.cards[i].config.center.key == "c_cry_potion" then + G.consumeables.cards[i]:start_dissolve() + end + end + return true + end + })) + else + --these animations are still a bit goofy, idk why + G.E_MANAGER:add_event(Event({ + func = function() + for i = #G.consumeables.cards, 1, -1 do + if G.consumeables.cards[i].config.center.key == "c_cry_potion" then + G.consumeables.cards[i].config.center:use(G.consumeables.cards[i],G.consumeables) + G.consumeables.cards[i]:start_dissolve() + end + end + return true + end + })) + end + end + end + --todo: loc_vars potions +} +local potion = { + object_type = "Consumable", + set = "Unique", + key = "potion", + name = "cry-Potion", + pos = { x = 0, y = 1 }, + config = { random_event = 2 }, + cost = 4, + no_doe = true, + no_ccd = true, + order = 1, + immutable = true, + no_dbl = true, + no_grc = true, + atlas = "atlasspooky", + can_use = function(self, card) + return true + end, + in_pool = function() + return false + end, + use = function(self, card, area, copier) + if not (G.GAME.events and G.GAME.events.ev_cry_choco3) then + return + end -- Just in case a potion is found out side of the event + G.GAME.events.ev_cry_choco3.potions[card.ability.random_event] + = (G.GAME.events.ev_cry_choco3.potions[card.ability.random_event] or 0)+1 + --Announce event + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("timpani", 1) + attention_text({ + scale = 1.4, + text = localize("cry_potion"..card.ability.random_event), + hold = 2, + align = "cm", + offset = { x = 0, y = -2.7 }, + major = G.play, + }) + return true + end + })) + if card.ability.random_event == 1 then -- -1 to all hand levels + update_hand_text( + { sound = "button", volume = 0.7, pitch = 0.8, delay = 0.3 }, + { handname = localize("k_all_hands"), chips = "...", mult = "...", level = "" } + ) + update_hand_text({ delay = 0 }, { mult = "-", StatusText = true }) + update_hand_text({ delay = 0 }, { chips = "-", StatusText = true }) + update_hand_text({ sound = "button", volume = 0.7, pitch = 0.9, delay = 0 }, { level = "+1" }) + delay(1.3) + for k, v in pairs(G.GAME.hands) do + level_up_hand(used_consumable, k, true, -1) + end + update_hand_text( + { sound = "button", volume = 0.7, pitch = 1.1, delay = 0 }, + { mult = 0, chips = 0, handname = "", level = "" } + ) + end + if card.ability.random_event == 2 then -- X1.15 blind size + G.GAME.starting_params.ante_scaling = G.GAME.starting_params.ante_scaling * 1.15 + if G.GAME.blind and G.GAME.blind.chips then + G.GAME.blind.chips = G.GAME.blind.chips * 1.15 + end + end + if card.ability.random_event == 3 then -- -1 Hand and Discard + G.GAME.round_resets.hands = G.GAME.round_resets.hands - 1 + ease_hands_played(-1) + G.GAME.round_resets.discards = G.GAME.round_resets.discards - 1 + ease_discard(-1) + end + delay(12/G.SETTINGS.GAMESPEED) + end, +} +local choco4 = { --lunar abyss + object_type = "Event", + key = "choco4", + calculate = function(self, context) + if context.pre_jokers and context.before and not context.repetition and not context.blueprint and not context.retrigger_joker then + for i = 1, #G.play.cards do + if pseudorandom(pseudoseed("cry_choco_lunar")) < G.GAME.probabilities.normal / 4 then + local faces = {} + for _, v in ipairs(SMODS.Rank.obj_buffer) do + local r = SMODS.Ranks[v] + if r.face then table.insert(faces, r) end + end + local _rank = pseudorandom_element(faces, pseudoseed('cry_choco_lunar_create')).card_key + G.play.cards[i]:set_base(G.P_CARDS["C_".._rank]) + end + end + end + if context.post_jokers and context.joker_main and not context.blueprint_card and not context.retrigger_joker then + local faces = 0 + for i = 1, #G.play.cards do + if G.play.cards[i]:is_face() then + faces = faces + 1 + end + end + if faces > 1 then + mult = mult / faces + update_hand_text({ delay = 0 }, { mult = mult, chips = hand_chips }) + end + end + end +} +local choco5 = { --bloodsucker + object_type = "Event", + key = "choco5", + calculate = function(self, context) + if context.pre_jokers and context.before and not context.repetition and not context.blueprint and not context.retrigger_joker then + for k, v in ipairs(context.scoring_hand) do + if v.config.center ~= G.P_CENTERS.c_base and not v.debuff and not v.vampired then + v:set_ability(G.P_CENTERS.c_base, nil, true) + v.vampired = true + G.E_MANAGER:add_event(Event({ + func = function() + v:juice_up() + v.vampired = nil + return true + end + })) + end + end + end + if context.post_jokers and context.destroying_card and not context.blueprint and not context.retrigger_joker then + if context.destroying_card:is_suit("Hearts") or context.destroying_card:is_suit("Diamonds") then + if pseudorandom(pseudoseed("cry_choco_blood")) < G.GAME.probabilities.normal / 3 then + context.destroying_card.will_shatter = true + G.E_MANAGER:add_event(Event({ + func = function() + context.destroying_card:start_dissolve() + return true + end + })) + end + end + end + end +} +local choco6 = { --please take one + object_type = "Event", + key = "choco6", + calculate = function(self, context) + if context.pre_cash then + G.E_MANAGER:add_event(Event({ + func = function() + local key = get_pack('cry_take_one').key + local card = Card(G.play.T.x + G.play.T.w/2 - G.CARD_W*1.27/2, + G.play.T.y + G.play.T.h/2-G.CARD_H*1.27/2, + G.CARD_W*1.27, G.CARD_H*1.27, + G.P_CARDS.empty, G.P_CENTERS[key], + {bypass_discovery_center = true, bypass_discovery_ui = true}) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({config = {ref_table = card}}) + card:start_materialize() + pack_opened = true + return true + end + })) + end + if context.setting_blind then + pack_opened = nil + end + end +} +local choco7 = { + object_type = "Event", + key = "choco7", + start = function(self) + G.GAME.events[self.key] = true + for i = 1, 3 do + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_trick_or_treat") + card:add_to_deck() + G.jokers:emplace(card) + end + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_candy_basket") + card:add_to_deck() + G.jokers:emplace(card) + end, + calculate = function(self, context) + if context.start_shop then + local tag = Tag("tag_cry_rework") + if not tag.ability then + tag.ability = {} + end + tag.ability.rework_key = "j_cry_trick_or_treat" + tag.ability.rework_edition = "e_base" + add_tag(tag) + end + end +} +local choco8 = { + object_type = "Event", + key = "choco8", + calculate = function(self, context) + if context.cash_out then + for i = 1, G.GAME.current_round.hands_left do + local card = create_card("Joker", G.jokers, nil, "cry_candy", nil, nil, nil, "cry_choco8") + card:add_to_deck() + G.jokers:emplace(card) + end + end + end +} +local choco9 = { + object_type = "Event", + key = "choco9", + start = function(self) + G.GAME.events[self.key] = true + ease_dollars(10) --will already be X2 = 20 + end, +} +local ed = ease_dollars +function ease_dollars(mod, instant) + if mod == 0 then return end + if G.GAME.events.ev_cry_choco9 and mod > 0 then + mod = mod * 2 + end + return ed(mod, instant) +end +local choco10 = { --revered antique + object_type = "Event", + key = "choco10" + --everything here is lovely patches or hooks +} +local spy = { + object_type = "Joker", + key = "spy", + pos = { x = 0, y = 0 }, + rarity = 1, + cost = 8, + atlas = "atlasspooky", + config = {x_mult = 0.5, extra = {secret_card = "", revealed = false}}, + immutable = true, + source_gate = "sho", + order = 133, + no_dbl = true, + loc_vars = function(self, info_queue, center) + return { vars = { localize({ type = "name_text", set = "Joker", key = center.ability and center.ability.extra and center.ability.extra.secret_card }), center.ability.x_mult } } + end, + update = function(self, card, front) + if card.ability.extra.secret_card == "" then + secret_card = pseudorandom_element(G.P_CENTER_POOLS.Joker, pseudoseed("cry_spy" .. (card.area and card.area.config.collection and "_collection" or ""))) + card.ability.extra.secret_card = secret_card.key + if not (card.area and card.area.config.collection) then + card.pos = secret_card.pos + card.config.center.rarity = secret_card.rarity + card.cost = secret_card.cost + card:set_sprites(G.P_CENTERS[card.ability.extra.secret_card]) + card.children.center:set_sprite_pos(secret_card.pos) + end + end + if card.area and card.area.config.collection then + card.config.center.rarity = "cry_cursed" + end + end, + add_to_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit + 1 + card.ability.perishable = true + card.ability.perish_tally = G.GAME.perishable_rounds + card.config.center.rarity = "cry_cursed" + card:set_sprites(card.config.center) + card.ability.extra.revealed = true + end, + remove_from_deck = function(self, card, from_debuff) + G.jokers.config.card_limit = G.jokers.config.card_limit - 1 + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after then + return { + message = localize({ type = "variable", key = "a_xmult", vars = { card.ability.x_mult } }), + Xmult_mod = card.ability.x_mult, + colour = G.C.MULT, + } + end + end, + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + if card.ability.extra.revealed or (card.area and card.area.config.collection) then + if card.area and card.area.config.collection then + card:update(0.016) + end + local target = { + type = 'descriptions', + key = self.key, + set = self.set, + nodes = desc_nodes, + vars = + specific_vars or {} + } + local res = {} + if self.loc_vars and type(self.loc_vars) == 'function' then + res = self:loc_vars(info_queue, card) or {} + target.vars = res.vars or target.vars + target.key = res.key or target.key + target.set = res.set or target.set + end + if desc_nodes == full_UI_table.main and not full_UI_table.name then + full_UI_table.name = localize { type = 'name', set = target.set, key = target.key, nodes = full_UI_table.name } + elseif desc_nodes ~= full_UI_table.main and not desc_nodes.name then + desc_nodes.name = localize{type = 'name_text', key = target.key, set = target.set } + end + if specific_vars and specific_vars.debuffed and not res.replace_debuff then + target = { type = 'other', key = 'debuffed_' .. + (specific_vars.playing_card and 'playing_card' or 'default'), nodes = desc_nodes } + end + if res.main_start then + desc_nodes[#desc_nodes + 1] = res.main_start + end + localize(target) + if res.main_end then + desc_nodes[#desc_nodes + 1] = res.main_end + end + else + local secret_card = cry_deep_copy(G.P_CENTERS[card.ability.extra.secret_card]) + secret_card.ability = secret_card.config + local target = { + type = 'descriptions', + key = secret_card.key, + set = secret_card.set, + nodes = desc_nodes, + vars = + specific_vars or {} + } + local res = {} + if secret_card.loc_vars and type(secret_card.loc_vars) == 'function' then + res = secret_card:loc_vars(info_queue, secret_card) or {} + target.vars = res.vars or target.vars + target.key = res.key or target.key + target.set = res.set or target.set + end + if desc_nodes == full_UI_table.main and not full_UI_table.name then + full_UI_table.name = localize { type = 'name', set = target.set, key = target.key, nodes = full_UI_table.name } + elseif desc_nodes ~= full_UI_table.main and not desc_nodes.name then + desc_nodes.name = localize{type = 'name_text', key = target.key, set = target.set } + end + if specific_vars and specific_vars.debuffed and not res.replace_debuff then + target = { type = 'other', key = 'debuffed_' .. + (specific_vars.playing_card and 'playing_card' or 'default'), nodes = desc_nodes } + end + if res.main_start then + desc_nodes[#desc_nodes + 1] = res.main_start + end + localize(target) + if res.main_end then + desc_nodes[#desc_nodes + 1] = res.main_end + end + end + end +} +local flickering = { + object_type = "Sticker", + atlas = "sticker", + pos = { x = 5, y = 4 }, --placeholder + key = "flickering", + badge_colour = HEX("747474"), + loc_vars = function(self, info_queue, card) + return { vars = { 5, card.ability.flick_tally } } + end, + apply = function(self, card, val) + if not card.ability.eternal or G.GAME.modifiers.cry_sticker_sheet then + card.ability[self.key] = val + if card.ability[self.key] then card.ability.flick_tally = 5 end + end + end, + calculate = function(self, card, context) + if card.ability.set == "Joker" then + if context.post_trigger and context.other_joker == card then + card.ability.flick_tally = card.ability.flick_tally - 1 + if card.ability.flick_tally > 0 then + card_eval_status_text( + card, 'extra', nil, nil, nil, + {message = localize{type='variable',key='a_remaining',vars={card.ability.flick_tally}}, + colour = G.C.FILTER, + delay = 0.45}) + else + card.will_shatter = true + G.E_MANAGER:add_event(Event({ + func = function() + card:start_dissolve() + return true + end + })) + end + end + elseif context.from_playing_card and not card.debuff and not context.repetition_only and context.ret then + context.ret.jokers = nil + if next(context.ret) ~= nil then + card.ability.flick_tally = card.ability.flick_tally - 1 + if card.ability.flick_tally > 0 then + card_eval_status_text( + card, 'extra', nil, nil, nil, + { + message = localize{type='variable',key='a_remaining',vars={card.ability.flick_tally}}, + colour = G.C.FILTER, + delay = 0.45 + }) + else + card.will_shatter = true + G.E_MANAGER:add_event(Event({ + func = function() + card:start_dissolve() + return true + end + })) + end + end + end + end, +} +local trick_or_treat = { + object_type = "Joker", + key = "trick_or_treat", + pos = { x = 2, y = 1 }, + rarity = 2, + cost = 5, + order = 134, + atlas = "atlasspooky", + blueprint_compat = true, + eternal_compat = false, + perishable_compat = false, + calculate = function(self, card, context) + if context.selling_self then + if pseudorandom(pseudoseed("cry_trick_or_treat")) < 3/4*G.GAME.probabilities.normal then + for i = 1, 2 do + local card = create_card("Joker", G.jokers, nil, "cry_candy", nil, nil, nil, "cry_trick_candy") + card:add_to_deck() + G.jokers:emplace(card) + end + else + local card = create_card("Joker", G.jokers, nil, "cry_cursed", nil, nil, nil, "cry_trick_curse") + card:add_to_deck() + G.jokers:emplace(card) + end + end + end, + loc_vars = function(self, info_queue, center) + return { vars = { 3 * G.GAME.probabilities.normal, 4 } } + end, +} +local candy_basket = { + object_type = "Joker", + key = "candy_basket", + pos = { x = 4, y = 0 }, + rarity = 2, + cost = 6, + order = 135, + atlas = "atlasspooky", + blueprint_compat = false, + eternal_compat = false, + perishable_compat = false, + config = {extra = {candies = 0, candy_mod = 0.5, candy_boss_mod = 2}}, + calculate = function(self, card, context) + if context.selling_self then + for i = 1, math.min(100, card.ability.extra.candies) do + local card = create_card("Joker", G.jokers, nil, "cry_candy", nil, nil, nil, "cry_candy_basket") + card:add_to_deck() + G.jokers:emplace(card) + end + end + if context.end_of_round and not context.individual and not context.repetition then + candy_pre = math.floor(card.ability.extra.candies) + card.ability.extra.candies = card.ability.extra.candies + card.ability.extra.candy_mod + if G.GAME.blind.boss then + card.ability.extra.candies = card.ability.extra.candies + card.ability.extra.candy_boss_mod + end + if math.floor(card.ability.extra.candies) > candy_pre then + card_eval_status_text(card, "extra", nil, nil, nil, { message = localize("k_upgrade_ex") }) + end + end + end, + loc_vars = function(self, info_queue, center) + return { vars = { math.floor(center.ability.extra.candies), 2*center.ability.extra.candy_mod, center.ability.extra.candy_boss_mod} } + end, +} +local blacklist = { + object_type = "Joker", + key = "blacklist", + pos = { x = 2, y = 2 }, + rarity = "cry_cursed", + cost = 0, + atlas = "atlasspooky", + order = 136, + config = {extra = {blacklist = {}}}, + blueprint_compat = false, + eternal_compat = false, + perishable_compat = false, + no_dbl = true, + calculate = function(self, card, context) + if context.joker_main then + local blacklist = false + for i = 1, #G.play.cards do + if G.play.cards[i]:get_id() == card.ability.extra.blacklist.id then + blacklist = true + break + end + end + for i = 1, #G.hand.cards do + if G.hand.cards[i]:get_id() == card.ability.extra.blacklist.id then + blacklist = true + break + end + end + if blacklist then + hand_chips = to_big(0) + mult = to_big(0) + update_hand_text({ delay = 0 }, { mult = mult, chips = hand_chips }) + return { + message = localize("k_nope_ex"), + colour = G.C.BLACK, + } + else + for i = 1, #G.discard.cards do + if G.discard.cards[i]:get_id() == card.ability.extra.blacklist.id then + blacklist = true + break + end + end + for i = 1, #G.deck.cards do + if G.deck.cards[i]:get_id() == card.ability.extra.blacklist.id then + blacklist = true + break + end + end + if not blacklist then + G.E_MANAGER:add_event(Event({ + func = function() + card:start_dissolve() + return true + end + })) + end + end + end + end, + add_to_deck = function(self, card, from_debuff) + card.ability.extra.blacklist = pseudorandom_element(SMODS.Ranks, pseudoseed("cry_blacklist"..G.GAME.round_resets.ante)) + end, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.blacklist and center.ability.extra.blacklist.key or "Ace" } } + end, +} +local ghost = { + object_type = "Joker", + key = "ghost", + pos = { x = 3, y = 0 }, + config = {extra = {possess_rate = 2, destroy_rate = 6}}, + rarity = "cry_cursed", + cost = 0, + order = 137, + atlas = "atlasspooky", + blueprint_compat = false, + eternal_compat = false, + perishable_compat = false, + no_dbl = true, + calculate = function(self, card, context) + if context.end_of_round and not context.individual and not context.repetition and not context.blueprint and not context.retrigger_joker then + if pseudorandom(pseudoseed("cry_ghost_destroy")) < G.GAME.probabilities.normal/card.ability.extra.destroy_rate then + G.E_MANAGER:add_event(Event({ + func = function() + card:start_dissolve() + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.cry_possessed then + if G.jokers.cards[i].ability.eternal then + G.jokers.cards[i].ability.cry_possessed = nil + else + G.jokers.cards[i]:start_dissolve() + end + end + end + return true + end + })) + return + end + --todo: let multiple ghosts possess multiple jokers + if pseudorandom(pseudoseed("cry_ghost_possess")) < G.GAME.probabilities.normal/card.ability.extra.possess_rate then + for i = 1, #G.jokers.cards do + G.jokers.cards[i].ability.cry_possessed = nil + end + local eligible_cards = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].config.center.key ~= "j_cry_ghost" then + table.insert(eligible_cards, i) + end + end + if #eligible_cards ~= 0 then + G.jokers.cards[pseudorandom_element(eligible_cards,pseudoseed("cry_ghost_possess_choice"))].ability.cry_possessed = true + end + return + end + end + end, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = { set = "Other", key = "cry_possessed"} + return { vars = { G.GAME.probabilities.normal or 1, center.ability.extra.possess_rate, center.ability.extra.destroy_rate } } + end, +} +local possessed = { + object_type = "Sticker", + atlas = "sticker", + pos = { x = 2, y = 2 }, --todo + key = "possessed", + no_sticker_sheet = true, + badge_colour = HEX("aaaaaa"), +} +local spookydeck = { + object_type = "Back", + key = "spooky", + config = { cry_spooky = true, cry_curse_rate = 0.25 }, + pos = { x = 3, y = 1 }, + order = 16, + atlas = "atlasspooky", +} +local candy_dagger = { + object_type = "Joker", + name = "cry-Candy Dagger", + key = "candy_dagger", + pos = { x = 4, y = 2 }, + rarity = 2, + cost = 8, + order = 138, + atlas = "atlasspooky", + blueprint_compat = true, + calculate = function(self, card, context) + local my_pos = nil + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + my_pos = i + break + end + end + if + context.setting_blind + and not (context.blueprint_card or self).getting_sliced + and my_pos + and G.jokers.cards[my_pos + 1] + and not G.jokers.cards[my_pos + 1].ability.eternal + and not G.jokers.cards[my_pos + 1].getting_sliced + then + local sliced_card = G.jokers.cards[my_pos + 1] + sliced_card.getting_sliced = true + if sliced_card.config.center.rarity == "cry_exotic" then + check_for_unlock({ type = "what_have_you_done" }) + end + G.GAME.joker_buffer = G.GAME.joker_buffer - 1 + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.joker_buffer = 0 + card:juice_up(0.8, 0.8) + sliced_card:start_dissolve({ HEX("57ecab") }, nil, 1.6) + play_sound("slice1", 0.96 + math.random() * 0.08) + return true + end, + })) + card_eval_status_text( + card, + "extra", + nil, + nil, + nil, + { + message = localize({ + type = "variable", + key = "a_candy", + vars = { 1 }, + }), + colour = G.C.RARITY["cry_candy"], + no_juice = true, + } + ) + local card = create_card("Joker", G.jokers, nil, "cry_candy", nil, nil, nil, "cry_candy_dagger") + card:add_to_deck() + G.jokers:emplace(card) + return nil, true + end + end, +} +local candy_cane = { + object_type = "Joker", + key = "candy_cane", + pos = { x = 1, y = 1 }, + rarity = "cry_candy", + config = { extra = { rounds = 11, dollars = 4 } }, + cost = 10, + order = 139, + atlas = "atlasspooky", + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.rounds, center.ability.extra.dollars } } + end, + calculate = function(self, card, context) + if context.individual and context.cardarea == G.play then + if not context.other_card.candy_caned then + context.other_card.candy_caned = true + G.E_MANAGER:add_event(Event({ + func = function() + context.other_card.candy_caned = nil + return true + end + })) + else + ease_dollars(card.ability.extra.dollars) + end + end + if + context.end_of_round + and not context.blueprint + and not context.individual + and not context.repetition + and not context.retrigger_joker + then + card.ability.extra.rounds = card.ability.extra.rounds - 1 + if card.ability.extra.rounds > 0 then + return { + message = { localize("cry_minus_round") }, + colour = G.C.FILTER, + } + else + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("k_extinct_ex"), + colour = G.C.FILTER, + } + end + end + end, +} + +local candy_buttons = { + object_type = "Joker", + key = "candy_buttons", + name = "cry-candybuttons", + pos = { x = 1, y = 2 }, + order = 140, + rarity = "cry_candy", + config = { extra = { rerolls = 15 } }, + cost = 10, + atlas = "atlasspooky", + blueprint_compat = true, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.rerolls } } + end, + calculate = function(self, card, context) + if context.reroll_shop and not context.blueprint then + card.ability.extra.rerolls = card.ability.extra.rerolls - 1 + if card.ability.extra.rerolls <= 0 then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("k_extinct_ex"), + colour = G.C.FILTER, + } + end + return nil, true + end + end, + add_to_deck = function(self, card, from_debuff) + calculate_reroll_cost(true) + end, + remove_from_deck = function(self, card, from_debuff) + calculate_reroll_cost(true) + end, +} + +local jawbreaker = { + object_type = "Joker", + key = "jawbreaker", + pos = { x = 3, y = 2 }, + rarity = "cry_candy", + cost = 10, + order = 141, + atlas = "atlasspooky", + blueprint_compat = false, + calculate = function(self, card, context) + if context.end_of_round and not context.individual and not context.repetition and G.GAME.blind.boss and not context.blueprint_card and not context.retrigger_joker then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == card then + if i > 1 then + if not Card.no(G.jokers.cards[i-1], "immune_to_chemach", true) and not Card.no(G.jokers.cards[i-1], "immutable", true) then + cry_with_deck_effects(G.jokers.cards[i-1], function(card) + cry_misprintize(card, { min = 2, max = 2 }, nil, true) + end) + end + end + if i < #G.jokers.cards then + if not Card.no(G.jokers.cards[i+1], "immune_to_chemach", true) and not Card.no(G.jokers.cards[i+1], "immutable", true) then + cry_with_deck_effects(G.jokers.cards[i+1], function(card) + cry_misprintize(card, { min = 2, max = 2 }, nil, true) + end) + end + end + end + end + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("k_extinct_ex"), + colour = G.C.FILTER, + } + end + end, + add_to_deck = function(self, card, from_debuff) + calculate_reroll_cost(true) + end, + remove_from_deck = function(self, card, from_debuff) + calculate_reroll_cost(true) + end, +} +local mellowcreme = { + object_type = "Joker", + key = "mellowcreme", + pos = { x = 0, y = 2 }, + rarity = "cry_candy", + cost = 10, + order = 142, + atlas = "atlasspooky", + config = {extra = {sell_mult = 4}}, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.sell_mult } } + end, + blueprint_compat = true, + calculate = function(self, card, context) + if context.selling_self then + for k, v in ipairs(G.consumeables.cards) do + if v.set_cost then + v.ability.extra_value = (v.ability.extra_value or 0) + (math.max(1, math.floor(v.cost/2)) + (v.ability.extra_value or 0))*(card.ability.extra.sell_mult-1) + v:set_cost() + end + end + end + end, +} +local brittle = { + object_type = "Joker", + key = "brittle", + pos = { x = 5, y = 1 }, + rarity = "cry_candy", + cost = 10, + atlas = "atlasspooky", + order = 143, + config = {extra = {rounds = 9}}, + loc_vars = function(self, info_queue, center) + info_queue[#info_queue + 1] = G.P_CENTERS.m_stone + info_queue[#info_queue + 1] = G.P_CENTERS.m_gold + info_queue[#info_queue + 1] = G.P_CENTERS.m_steel + return { vars = { center.ability.extra.rounds } } + end, + blueprint_compat = true, + calculate = function(self, card, context) + if context.cardarea == G.jokers and context.before and not context.blueprint_card and not context.retrigger_joker then + local _card = context.scoring_hand[#context.scoring_hand] + if not _card.brittled then + card.ability.extra.rounds = card.ability.extra.rounds - 1 + local enhancement = pseudorandom_element({"m_stone", "m_gold", "m_steel"}, pseudoseed("cry_brittle")) + _card.brittled = true + _card:set_ability(G.P_CENTERS[enhancement], nil, true) + G.E_MANAGER:add_event(Event({ + func = function() + _card:juice_up() + _card.brittled = nil + return true + end + })) + if card.ability.extra.rounds > 0 then + return nil, true + else + G.E_MANAGER:add_event(Event({ + func = function() + play_sound("tarot1") + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true + end, + })) + return true + end, + })) + return { + message = localize("k_extinct_ex"), + colour = G.C.FILTER, + } + end + end + end + end +} +local monopoly_money = { + object_type = "Joker", + key = "monopoly_money", + name = "cry-Monopoly", + pos = { x = 4, y = 1 }, + config = {extra = {fail_rate = 4}}, + order = 144, + rarity = "cry_cursed", + cost = 0, + atlas = "atlasspooky", + blueprint_compat = false, + eternal_compat = false, + perishable_compat = false, + no_dbl = true, + calculate = function(self, card, context) + if context.buying_card and not context.blueprint_card and not context.retrigger_joker and not (context.card == card) then + if pseudorandom(pseudoseed("cry_monopoly")) < G.GAME.probabilities.normal/card.ability.extra.fail_rate then + G.E_MANAGER:add_event(Event({ + func = function() + context.card:start_dissolve() + card_eval_status_text(card, 'extra', nil, nil, nil, { + message = localize("k_nope_ex"), + colour = G.C.BLACK, + }) + return true + end + })) + end + return nil, true + end + if context.selling_self and not context.blueprint_card and not context.retrigger_joker then + G.E_MANAGER:add_event(Event({ + func = function() + ease_dollars(math.floor(-0.5*G.GAME.dollars)) + return true + end + })) + return nil, true + end + end, + loc_vars = function(self, info_queue, center) + return { vars = { G.GAME.probabilities.normal or 1, center.ability.extra.fail_rate} } + end, +} +local candy_sticks = { + object_type = "Joker", + key = "candy_sticks", + name = "cry-Candy-Sticks", + pos = { x = 5, y = 2 }, + order = 145, + config = {extra = { boss = {}, hands = 1, clockscore = 0}}, + rarity = "cry_candy", + cost = 3, + atlas = "atlasspooky", + blueprint_compat = false, + eternal_compat = false, + no_dbl = true, + calculate = function(self, card, context) + if context.setting_blind and not self.getting_sliced and not context.blueprint and context.blind.boss then + card.ability.extra.boss = G.GAME.blind:save() + if G.GAME.blind.name == 'The Clock' then + card.ability.extra.clockscore = G.GAME.blind.chips + end + G.E_MANAGER:add_event(Event({func = function() + G.E_MANAGER:add_event(Event({func = function() + G.GAME.blind:disable() + play_sound('timpani') + delay(0.4) + return true end })) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('ph_boss_disabled')}) + return true end })) + end + if context.after and G.GAME.blind:get_type() == 'Boss' then + card.ability.extra.hands = card.ability.extra.hands-1 + end + if ((context.selling_self and G.GAME.blind and G.GAME.blind:get_type() == 'Boss') or card.ability.extra.hands <= 0) and G.GAME.blind.disabled then + G.GAME.blind:load(card.ability.extra.boss) + if not context.selling_self then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true; end})) + return true + end + })) + end + end + if context.end_of_round and G.GAME.blind:get_type() == 'Boss' then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + card.T.r = -0.2 + card:juice_up(0.3, 0.4) + card.states.drag.is = true + card.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(card) + card:remove() + card = nil + return true; end})) + return true + end + })) + end + end, + loc_vars = function(self, info_queue, center) + return { vars = { center.ability.extra.hands} } + end, + cry_credits = { + idea = { + "Squiddy" + }, + art = { + "lolxddj" + }, + code = { + "Foegro" + } + }, +} + +items = { + cotton_candy, + wrapped, + choco_dice, + choco_base_event, + choco1, + choco2, + choco3, + potion, + choco4, + choco5, + choco6, + choco7, + choco8, + choco9, + choco10, + --spy, + flickering, + trick_or_treat, + candy_basket, + blacklist, + ghost, + possessed, + spookydeck, + candy_dagger, + candy_cane, + candy_buttons, + jawbreaker, + mellowcreme, + brittle, + monopoly_money, + candy_sticks, +} +return { name = "Spooky", init = function() + + local sc = Card.set_cost + function Card:set_cost() + sc(self) + if self.config and self.config.center and self.config.center.rarity == "cry_cursed" then + self.sell_cost = 0 + self.sell_cost_label = 0 + end + end + + --Really hacky patch to remove sell button for cursed jokers + local G_UIDEF_use_and_sell_buttons_ref = G.UIDEF.use_and_sell_buttons + function G.UIDEF.use_and_sell_buttons(card) + local m = G_UIDEF_use_and_sell_buttons_ref(card) + if card.area and card.area.config.type == 'joker' and card.config and card.config.center and card.config.center.rarity == "cry_cursed" and card.ability.name ~= "cry-Monopoly" then + table.remove(m.nodes[1].nodes, 1) + end + if card.config and card.config.center and card.config.center.key == "c_cry_potion" then + table.remove(m.nodes[1].nodes, 1) + end + return m + end + + --track if rerolled + local gfrs = G.FUNCS.reroll_shop + G.FUNCS.reroll_shop = function(e) + local ret = gfrs(e) + G.GAME.current_round.rerolled = true + return ret + end + local gfcr = G.FUNCS.can_reroll + G.FUNCS.can_reroll = function(e) + if G.GAME.events.ev_cry_choco2 and G.GAME.current_round.rerolled then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + return + end + return gfcr(e) + end + + --candy gives $3 + local catd = Card.add_to_deck + function Card:add_to_deck(debuff) + if not debuff and self.config.center.rarity == "cry_candy" then + if G.GAME.events.ev_cry_choco7 then + ease_dollars(3) + end + if G.GAME.events.ev_cry_choco8 then + local card = create_card( + "Joker", + G.jokers, + nil, + nil, + nil, + nil, + pseudorandom_element(Cryptid.food, pseudoseed("cry_candy_rain")) + ) + card:add_to_deck() + G.jokers:emplace(card) + end + end + return catd(self, debuff) + end + + --antique can only be bought as last item + local gfcb = G.FUNCS.can_buy + function G.FUNCS.can_buy(e) + if e.config.ref_table and e.config.ref_table.ability and e.config.ref_table.ability.cry_antique then + if not (#G.shop_jokers.cards == 0 and #G.shop_booster.cards == 0 and #G.shop_vouchers.cards == 1) then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + return + end + end + return gfcb(e) + end + + local Backapply_to_runRef = Back.apply_to_run + function Back.apply_to_run(self) + Backapply_to_runRef(self) + if self.effect.config.cry_spooky then + G.GAME.modifiers.cry_spooky = true + G.GAME.modifiers.cry_curse_rate = self.effect.config.cry_curse_rate or 0.25 + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cry_chocolate_dice") + card:add_to_deck() + card:start_materialize() + card:set_eternal(true) + G.jokers:emplace(card) + return true + end + end, + })) + end + end +end, items = items } diff --git a/Cryptid/Items/Stakes.lua b/Cryptid/Items/Stakes.lua new file mode 100644 index 0000000..5f85f27 --- /dev/null +++ b/Cryptid/Items/Stakes.lua @@ -0,0 +1,607 @@ +local pink = { + object_type = "Stake", + name = "cry-Pink Stake", + key = "pink", + pos = { x = 0, y = 0 }, + atlas = "stake", + applied_stakes = { "gold" }, + prefix_config = { applied_stakes = { mod = false } }, + modifiers = function() + G.GAME.modifiers.scaling = (G.GAME.modifiers.scaling or 1) + 1 + end, + order = 9, + colour = HEX("ff5ee6"), +} +local brown = { + object_type = "Stake", + name = "cry-Brown Stake", + key = "brown", + pos = { x = 1, y = 0 }, + atlas = "stake", + applied_stakes = { "cry_pink" }, + modifiers = function() + G.GAME.modifiers.cry_eternal_perishable_compat = true + end, + order = 10, + colour = HEX("883200"), +} +local yellow = { + object_type = "Stake", + name = "cry-Yellow Stake", + key = "yellow", + pos = { x = 2, y = 0 }, + atlas = "stake", + applied_stakes = { "cry_brown" }, + modifiers = function() + G.GAME.modifiers.cry_any_stickers = true + end, + order = 11, + colour = HEX("f7ff1f"), +} +local jade = { + object_type = "Stake", + name = "cry-Jade Stake", + key = "jade", + pos = { x = 3, y = 0 }, + atlas = "stake", + applied_stakes = { "cry_yellow" }, + modifiers = function() + G.GAME.modifiers.flipped_cards = 20 + end, + shiny = true, + order = 12, + colour = HEX("78953c"), +} +local cyan = { + object_type = "Stake", + name = "cry-Cyan Stake", + key = "cyan", + pos = { x = 4, y = 0 }, + atlas = "stake", + applied_stakes = { "cry_jade" }, + modifiers = function() + G.GAME.modifiers.cry_rarer_jokers = true + -- Note that this is not the exact rarity as the old lovely patch might be nerf/buff to the stake + G.GAME.uncommon_mod = 0.8 + G.GAME.rare_mod = 0.8 + end, + order = 13, + colour = HEX("39ffcc"), +} +local gray = { + object_type = "Stake", + name = "cry-Gray Stake", + key = "gray", + pos = { x = 0, y = 1 }, + atlas = "stake", + applied_stakes = { "cry_cyan" }, + modifiers = function() + G.GAME.modifiers.cry_reroll_scaling = 2 + end, + order = 14, + colour = HEX("999999"), +} +local crimson = { + object_type = "Stake", + name = "cry-Crimson Stake", + key = "crimson", + pos = { x = 1, y = 1 }, + atlas = "stake", + applied_stakes = { "cry_gray" }, + modifiers = function() + G.GAME.modifiers.cry_voucher_restock_antes = 2 + end, + order = 15, + colour = HEX("800000"), +} +local diamond = { + object_type = "Stake", + name = "cry-Diamond Stake", + key = "diamond", + pos = { x = 2, y = 1 }, + atlas = "stake", + applied_stakes = { "cry_crimson" }, + modifiers = function() + G.GAME.win_ante = 10 + end, + shiny = true, + order = 16, + colour = HEX("88e5d9"), +} +local amber = { + object_type = "Stake", + name = "cry-Amber Stake", + key = "amber", + pos = { x = 3, y = 1 }, + atlas = "stake", + applied_stakes = { "cry_diamond" }, + modifiers = function() + G.GAME.modifiers.cry_booster_packs = 1 + end, + shiny = true, + order = 17, + colour = HEX("feb900"), +} +local bronze = { + object_type = "Stake", + name = "cry-Bronze Stake", + key = "bronze", + pos = { x = 4, y = 1 }, + atlas = "stake", + applied_stakes = { "cry_amber" }, + modifiers = function() + G.GAME.modifiers.cry_voucher_price_hike = 1.5 + end, + shiny = true, + order = 18, + colour = HEX("d27c37"), +} +local quartz = { + object_type = "Stake", + name = "cry-Quartz Stake", + key = "quartz", + pos = { x = 0, y = 2 }, + atlas = "stake", + applied_stakes = { "cry_bronze" }, + modifiers = function() + G.GAME.modifiers.cry_enable_pinned_in_shop = true + end, + shiny = true, + order = 19, + colour = HEX("e8e8e8"), +} +local ruby = { + object_type = "Stake", + name = "cry-Ruby Stake", + key = "ruby", + pos = { x = 1, y = 2 }, + atlas = "stake", + applied_stakes = { "cry_quartz" }, + modifiers = function() + G.GAME.modifiers.cry_big_boss_rate = 0.3 + end, + shiny = true, + order = 20, + colour = HEX("fc5f55"), +} +local glass = { + object_type = "Stake", + name = "cry-Glass Stake", + key = "glass", + pos = { x = 2, y = 2 }, + atlas = "stake", + applied_stakes = { "cry_ruby" }, + modifiers = function() + G.GAME.modifiers.cry_shatter_rate = 30 + end, + shiny = true, + order = 21, + colour = HEX("ffffff8f"), +} +local sapphire = { + object_type = "Stake", + name = "cry-Sapphire Stake", + key = "sapphire", + pos = { x = 3, y = 2 }, + atlas = "stake", + applied_stakes = { "cry_glass" }, + modifiers = function() + G.GAME.modifiers.cry_ante_tax = 0.25 + G.GAME.modifiers.cry_ante_tax_max = 10 + end, + shiny = true, + order = 22, + colour = HEX("3551fc"), +} +local emerald = { + object_type = "Stake", + name = "cry-Emerald Stake", + key = "emerald", + pos = { x = 4, y = 2 }, + atlas = "stake", + applied_stakes = { "cry_sapphire" }, + modifiers = function() + G.GAME.modifiers.cry_enable_flipped_in_shop = true + end, + shiny = true, + order = 23, + colour = HEX("06fc2c"), +} +local platinum = { + object_type = "Stake", + name = "cry-Platinum Stake", + key = "platinum", + pos = { x = 0, y = 3 }, + atlas = "stake", + applied_stakes = { "cry_emerald" }, + modifiers = function() + G.GAME.modifiers.cry_no_small_blind = true + G.GAME.round_resets.blind_states["Small"] = "Hide" + end, + shiny = true, + order = 24, + colour = HEX("b0f6ff"), +} +--init colors so they have references +G.C.CRY_TWILIGHT = { 0, 0, 0, 0 } +G.C.CRY_VERDANT = { 0, 0, 0, 0 } +G.C.CRY_EMBER = { 0, 0, 0, 0 } +G.C.CRY_DAWN = { 0, 0, 0, 0 } +G.C.CRY_HORIZON = { 0, 0, 0, 0 } +G.C.CRY_BLOSSOM = { 0, 0, 0, 0 } +G.C.CRY_AZURE = { 0, 0, 0, 0 } +G.C.CRY_ASCENDANT = { 0, 0, 0, 0 } +local twilight = { + object_type = "Stake", + name = "cry-Twilight Stake", + key = "twilight", + pos = { x = 1, y = 3 }, + atlas = "stake", + applied_stakes = { "cry_platinum" }, + modifiers = function() + G.GAME.modifiers.enable_banana = true + end, + shiny = true, + order = 25, + colour = G.C.CRY_TWILIGHT, +} +local banana = { + object_type = "Sticker", + badge_colour = HEX("e8c500"), + prefix_config = { key = false }, + key = "banana", + atlas = "sticker", + pos = { x = 5, y = 2 }, + should_apply = false, + loc_vars = function(self, info_queue, card) + if card.ability.consumeable then + return { key = "cry_banana_consumeable", vars = { G.GAME.probabilities.normal or 1, 4 } } + elseif card.ability.set == "Voucher" then + return { key = "cry_banana_voucher", vars = { G.GAME.probabilities.normal or 1, 12 } } + elseif card.ability.set == "Booster" then + return { key = "cry_banana_booster" } + else + return { vars = { G.GAME.probabilities.normal or 1, 10 } } + end + end, +} +local verdant = { + object_type = "Stake", + name = "cry-Verdant Stake", + key = "verdant", + pos = { x = 2, y = 3 }, + atlas = "stake", + applied_stakes = { "cry_twilight" }, + modifiers = function() + G.GAME.modifiers.scaling = (G.GAME.modifiers.scaling or 1) + 1 + end, + shiny = true, + order = 26, + colour = G.C.CRY_VERDANT, +} +local ember = { + object_type = "Stake", + name = "cry-Ember Stake", + key = "ember", + pos = { x = 3, y = 3 }, + atlas = "stake", + applied_stakes = { "cry_verdant" }, + modifiers = function() + G.GAME.modifiers.cry_no_sell_value = true + end, + shiny = true, + order = 27, + colour = G.C.CRY_EMBER, +} +local dawn = { + object_type = "Stake", + name = "cry-Dawn Stake", + key = "dawn", + pos = { x = 4, y = 3 }, + atlas = "stake", + applied_stakes = { "cry_ember" }, + modifiers = function() + G.GAME.modifiers.cry_consumable_reduce = true + end, + shiny = true, + order = 28, + colour = G.C.CRY_DAWN, +} +local horizon = { + object_type = "Stake", + name = "cry-Horizon Stake", + key = "horizon", + pos = { x = 0, y = 4 }, + atlas = "stake", + applied_stakes = { "cry_dawn" }, + modifiers = function() + G.GAME.modifiers.cry_card_each_round = true + end, + shiny = true, + order = 29, + colour = G.C.CRY_HORIZON, +} +local blossom = { + object_type = "Stake", + name = "cry-Blossom Stake", + key = "blossom", + pos = { x = 1, y = 4 }, + atlas = "stake", + applied_stakes = { "cry_horizon" }, + modifiers = function() + G.GAME.modifiers.cry_big_showdown = true + end, + shiny = true, + order = 30, + colour = G.C.CRY_BLOSSOM, +} +local azure = { + object_type = "Stake", + name = "cry-Azure Stake", + key = "azure", + pos = { x = 2, y = 4 }, + atlas = "stake", + applied_stakes = { "cry_blossom" }, + modifiers = function() + G.GAME.modifiers.cry_jkr_misprint_mod = 0.8 + end, + shiny = true, + order = 31, + colour = G.C.CRY_AZURE, +} +local ascendant = { + object_type = "Stake", + name = "cry-Ascendant Stake", + key = "ascendant", + pos = { x = 3, y = 4 }, + atlas = "stake", + applied_stakes = { "cry_azure" }, + modifiers = function() + change_shop_size(-1) + end, + shiny = true, + order = 32, + colour = G.C.CRY_ASCENDANT, +} +local stake_atlas = { object_type = "Atlas", key = "stake", +path = "stake_cry.png", px = 29, py = 29 } +return { + name = "More Stakes", + init = function(self) + -- Disallow use of Debuffed Perishable consumables + local cuc = Card.can_use_consumeable + function Card:can_use_consumeable(any_state, skip_check) + if self.ability.perish_tally == nil then + self.ability.perish_tally = G.GAME.perishable_rounds or 5 + end + if self.ability.perishable and self.ability.perish_tally <= 0 then + return false + end + return cuc(self, any_state, skip_check) + end + -- Overriding Steamodded's registering of Incantation/Familiar/Grim + local function random_destroy(used_tarot) + local destroyed_cards = {} + local temp_hand = {} + local hasHand = false + for k, v in ipairs(G.hand.cards) do + if not v.ability.eternal then + temp_hand[#temp_hand + 1] = v + hasHand = true + end + end + if hasHand then + destroyed_cards[#destroyed_cards + 1] = pseudorandom_element(temp_hand, pseudoseed("random_destroy")) + end + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.4, + func = function() + play_sound("tarot1") + if used_tarot and used_tarot.juice_up then + used_tarot:juice_up(0.3, 0.5) + end + return true + end, + })) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.1, + func = function() + for i = #destroyed_cards, 1, -1 do + local card = destroyed_cards[i] + if card.ability.name == "Glass Card" then + card:shatter() + else + card:start_dissolve(nil, i ~= #destroyed_cards) + end + end + return true + end, + })) + return destroyed_cards + end + SMODS.Consumable:take_ownership("grim", { + use = function(self, card, area, copier) + local used_tarot = copier or card + local destroyed_cards = random_destroy(used_tarot) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.7, + func = function() + local cards = {} + for i = 1, card.ability.extra do + cards[i] = true + local suit_list = {} + for i = #SMODS.Suit.obj_buffer, 1, -1 do + suit_list[#suit_list + 1] = SMODS.Suit.obj_buffer[i] + end + local _suit, _rank = + SMODS.Suits[pseudorandom_element(suit_list, pseudoseed("grim_create"))].card_key, "A" + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if v.key ~= "m_stone" then + cen_pool[#cen_pool + 1] = v + end + end + create_playing_card({ + front = G.P_CARDS[_suit .. "_" .. _rank], + center = pseudorandom_element(cen_pool, pseudoseed("spe_card")), + }, G.hand, nil, i ~= 1, { G.C.SECONDARY_SET.Spectral }) + end + playing_card_joker_effects(cards) + return true + end, + })) + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({ remove_playing_cards = true, removed = destroyed_cards }) + end + end, + }, true) + SMODS.Consumable:take_ownership("familiar", { + use = function(self, card, area, copier) + local used_tarot = copier or card + local destroyed_cards = random_destroy(used_tarot) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.7, + func = function() + local cards = {} + for i = 1, card.ability.extra do + cards[i] = true + local suit_list = {} + for i = #SMODS.Suit.obj_buffer, 1, -1 do + suit_list[#suit_list + 1] = SMODS.Suit.obj_buffer[i] + end + local faces = {} + for _, v in ipairs(SMODS.Rank.obj_buffer) do + local r = SMODS.Ranks[v] + if r.face then + table.insert(faces, r.card_key) + end + end + local _suit, _rank = + SMODS.Suits[pseudorandom_element(suit_list, pseudoseed("familiar_create"))].card_key, + pseudorandom_element(faces, pseudoseed("familiar_create")) + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if v.key ~= "m_stone" then + cen_pool[#cen_pool + 1] = v + end + end + create_playing_card({ + front = G.P_CARDS[_suit .. "_" .. _rank], + center = pseudorandom_element(cen_pool, pseudoseed("spe_card")), + }, G.hand, nil, i ~= 1, { G.C.SECONDARY_SET.Spectral }) + end + playing_card_joker_effects(cards) + return true + end, + })) + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({ remove_playing_cards = true, removed = destroyed_cards }) + end + end, + }, true) + SMODS.Consumable:take_ownership("incantation", { + use = function(self, card, area, copier) + local used_tarot = copier or card + local destroyed_cards = random_destroy(used_tarot) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.7, + func = function() + local cards = {} + for i = 1, card.ability.extra do + cards[i] = true + local suit_list = {} + for i = #SMODS.Suit.obj_buffer, 1, -1 do + suit_list[#suit_list + 1] = SMODS.Suit.obj_buffer[i] + end + local numbers = {} + for _RELEASE_MODE, v in ipairs(SMODS.Rank.obj_buffer) do + local r = SMODS.Ranks[v] + if v ~= "Ace" and not r.face then + table.insert(numbers, r.card_key) + end + end + local _suit, _rank = + SMODS.Suits[pseudorandom_element(suit_list, pseudoseed("incantation_create"))].card_key, + pseudorandom_element(numbers, pseudoseed("incantation_create")) + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if v.key ~= "m_stone" then + cen_pool[#cen_pool + 1] = v + end + end + create_playing_card({ + front = G.P_CARDS[_suit .. "_" .. _rank], + center = pseudorandom_element(cen_pool, pseudoseed("spe_card")), + }, G.hand, nil, i ~= 1, { G.C.SECONDARY_SET.Spectral }) + end + playing_card_joker_effects(cards) + return true + end, + })) + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({ remove_playing_cards = true, removed = destroyed_cards }) + end + end, + }, true) + + local sc = Card.set_cost + function Card:set_cost() + sc(self) + if self.ability.set == "Voucher" and G.GAME.modifiers.cry_voucher_price_hike then + self.cost = math.floor(self.cost * G.GAME.modifiers.cry_voucher_price_hike) + --Update related costs + self.sell_cost = math.max(1, math.floor(self.cost / 2)) + (self.ability.extra_value or 0) + if + self.area + and self.ability.couponed + and (self.area == G.shop_jokers or self.area == G.shop_booster) + then + self.cost = 0 + end + self.sell_cost_label = self.facing == "back" and "?" or self.sell_cost + end + end + for _, v in pairs(self.items) do + if v.object_type == "Stake" then + v.sticker_pos = v.pos + v.sticker_atlas = "sticker" + end + end + end, + items = { + stake_atlas, + pink, + brown, + yellow, + jade, + cyan, + gray, + crimson, + diamond, + amber, + bronze, + quartz, + ruby, + glass, + sapphire, + emerald, + platinum, + twilight, + verdant, + ember, + dawn, + horizon, + blossom, + azure, + ascendant, + banana, + }, +} diff --git a/Cryptid/Items/Tags.lua b/Cryptid/Items/Tags.lua new file mode 100644 index 0000000..982634b --- /dev/null +++ b/Cryptid/Items/Tags.lua @@ -0,0 +1,967 @@ +local cat = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 0, y = 2 }, + key = "cat", + name = "cry-Cat Tag", + order = 12, +} +local epic_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 3, y = 0 }, + name = "cry-Epic Tag", + order = 1, + min_ante = 2, + requires = 'j_cry_googol_play', + config = { type = "store_joker_create" }, + key = "epic", + apply = function(self, tag, context) + if context.type == "store_joker_create" then + local rares_in_posession = { 0 } + for k, v in ipairs(G.jokers.cards) do + if v.config.center.rarity == "cry_epic" and not rares_in_posession[v.config.center.key] then + rares_in_posession[1] = rares_in_posession[1] + 1 + rares_in_posession[v.config.center.key] = true + end + end + local card + if #G.P_JOKER_RARITY_POOLS.cry_epic > rares_in_posession[1] then + card = create_card("Joker", context.area, nil, 'cry_epic', nil, nil, nil, "cry_eta") + create_shop_card_ui(card, "Joker", context.area) + card.states.visible = false + tag:yep("+", G.C.RARITY.cry_epic, function() + card:start_materialize() + card.misprint_cost_fac = 0.5 + card:set_cost() + return true + end) + else + tag:nope() + end + tag.triggered = true + return card + end + end, +} +local schematic = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 1, y = 2 }, + name = "cry-Schematic Tag", + order = 24, + requires = 'j_brainstorm', + config = { type = "store_joker_create" }, + key = "schematic", + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = { set = "Joker", key = "j_brainstorm" } + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_create" then + local card + card = create_card("Joker", context.area, nil, nil, nil, nil, "j_brainstorm") + create_shop_card_ui(card, "Joker", context.area) + card.states.visible = false + tag:yep("+", G.C.RED, function() + card:start_materialize() + card:set_cost() + return true + end) + tag.triggered = true + return card + end + end, +} +local empoweredPack = { + object_type = "Booster", + name = "cry-Empowered Pack", + key = "empowered", + kind = "Spectral", + no_doe = true, + atlas = "empowered", + pos = { x = 3, y = 1 }, + config = { extra = 2, choose = 1 }, + cost = 0, + weight = 0, + order = 8, + draw_hand = true, + update_pack = SMODS.Booster.update_pack, + loc_vars = SMODS.Booster.loc_vars, + ease_background_colour = function(self) + ease_background_colour_blind(G.STATES.SPECTRAL_PACK) + end, + create_UIBox = function(self) + return create_UIBox_spectral_pack() + end, + particles = function(self) + G.booster_pack_sparkles = Particles(1, 1, 0, 0, { + timer = 0.015, + scale = 0.1, + initialize = true, + lifespan = 3, + speed = 0.2, + padding = -1, + attach = G.ROOM_ATTACH, + colours = { G.C.WHITE, lighten(G.C.GOLD, 0.2) }, + fill = true, + }) + G.booster_pack_sparkles.fade_alpha = 1 + G.booster_pack_sparkles:fade(1, 0) + end, + create_card = function(self, card, i) + if i % 2 == 1 and Cryptid.enabled["Exotic Jokers"] then + return create_card("Spectral", G.pack_cards, nil, nil, true, true, "c_cry_gateway") + else + return create_card("Spectral", G.pack_cards, nil, nil, true, true, "c_soul") + end + end, + group_key = "k_spectral_pack", +} +local empoweredpack_sprite = { + object_type = "Atlas", + key = "empowered", + path = "pack_cry.png", + px = 71, + py = 95, +} +local empowered = { + object_type = "Tag", + name = "cry-Empowered Tag", + order = 18, + atlas = "tag_cry", + pos = { x = 1, y = 0 }, + config = { type = "new_blind_choice" }, + key = "empowered", + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.p_spectral_normal_1 + info_queue[#info_queue + 1] = { set = "Spectral", key = "c_soul" } + if Cryptid.enabled["Exotic Jokers"] then + info_queue[#info_queue + 1] = { set = "Spectral", key = "c_cry_gateway" } + end + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "new_blind_choice" then + if G.STATE ~= G.STATES.SPECTRAL_PACK then + G.GAME.PACK_INTERRUPT = G.STATE + end + tag:yep("+", G.C.SECONDARY_SET.Spectral, function() + local key = "p_cry_empowered" + local card = Card( + G.play.T.x + G.play.T.w / 2 - G.CARD_W * 1.27 / 2, + G.play.T.y + G.play.T.h / 2 - G.CARD_H * 1.27 / 2, + G.CARD_W * 1.27, + G.CARD_H * 1.27, + G.P_CARDS.empty, + G.P_CENTERS[key], + { bypass_discovery_center = true, bypass_discovery_ui = true } + ) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({ config = { ref_table = card } }) + card:start_materialize() + return true + end) + tag.triggered = true + return true + end + end, + in_pool = function() + return false + end, +} +local gambler = { + object_type = "Tag", + name = "cry-Gamblecore", + order = 13, + atlas = "tag_cry", + pos = { x = 2, y = 0 }, + config = { type = "immediate", odds = 4 }, + min_ante = 2, + key = "gambler", + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = { set = "Tag", key = "tag_cry_empowered" } + return { vars = { G.GAME.probabilities.normal or 1, self.config.odds } } + end, + apply = function(self, tag, context) + if context.type == "immediate" then + if pseudorandom("cry_gambler_tag") < G.GAME.probabilities.normal / tag.config.odds then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep("+", G.C.RARITY.cry_exotic, function() + add_tag(Tag("tag_cry_empowered")) + G.E_MANAGER:add_event(Event({ + trigger = "after", + delay = 0.3, + func = function() + if not G.GAME.PACK_INTERRUPT then + G.GAME.tags[#G.GAME.tags]:apply_to_run({ type = "new_blind_choice" }) + end + G.CONTROLLER.locks[lock] = nil + return true + end, + })) + return true + end) + else + tag:nope() + end + tag.triggered = true + return true + end + end, +} +local bundle = { + object_type = "Tag", + name = "cry-Bundle Tag", + order = 16, + atlas = "tag_cry", + pos = { x = 0, y = 0 }, + config = { type = "immediate" }, + key = "bundle", + min_ante = 2, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = { set = "Tag", key = "tag_standard" } + info_queue[#info_queue + 1] = { set = "Tag", key = "tag_charm" } + info_queue[#info_queue + 1] = { set = "Tag", key = "tag_meteor" } + info_queue[#info_queue + 1] = { set = "Tag", key = "tag_buffoon" } + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "immediate" then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep("+", G.C.ATTENTION, function() + add_tag(Tag("tag_standard")) + add_tag(Tag("tag_charm")) + add_tag(Tag("tag_meteor")) + add_tag(Tag("tag_buffoon")) + G.CONTROLLER.locks[lock] = nil + return true + end) + tag.triggered = true + return true + end + end, +} +local memory = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 3, y = 1 }, + name = "cry-Memory Tag", + order = 15, + config = { type = "immediate", num = 2 }, + key = "memory", + loc_vars = function(self, info_queue) + if G.GAME.cry_last_tag_used then + _c = G.P_TAGS[G.GAME.cry_last_tag_used] + end + local loc_tag = _c and localize({ type = "name_text", key = G.GAME.cry_last_tag_used, set = _c.set }) + or localize("k_none") + return { vars = { self.config.num, loc_tag } } + end, + apply = function(self, tag, context) + if context.type == "immediate" and G.GAME.cry_last_tag_used then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep("+", G.C.ATTENTION, function() + for i = 1, 2 do + local t = Tag(G.GAME.cry_last_tag_used) + t.ability.orbital_hand = G.GAME.cry_memory_orbital + add_tag(t) + end + G.CONTROLLER.locks[lock] = nil + return true + end) + tag.triggered = true + return true + end + end, + in_pool = function() + return G.GAME.cry_last_tag_used and true + end, +} +local glitched_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 5, y = 0 }, + name = "cry-Glitched Tag", + order = 2, + config = { type = "store_joker_modify", edition = "cry_glitched" }, + key = "glitched", + min_ante = 1, + requires = 'e_cry_glitched', + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_glitched + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_modify" then + local _applied = nil + if not context.card.edition and not context.card.temp_edition and context.card.ability.set == 'Joker' then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + context.card.temp_edition = true + tag:yep('+', G.C.DARK_EDITION,function() + context.card:set_edition({cry_glitched = true}, true) + context.card.ability.couponed = true + context.card:set_cost() + context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + tag.triggered = true + end + end + end, +} +local oversat_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 7, y = 1 }, + name = "cry-Oversaturated Tag", + order = 4, + config = { type = "store_joker_modify", edition = "cry_oversat" }, + key = "oversat", + requires = 'e_cry_oversat', + min_ante = 2, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_oversat + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_modify" then + local _applied = nil + if not context.card.edition and not context.card.temp_edition and context.card.ability.set == 'Joker' then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + context.card.temp_edition = true + tag:yep('+', G.C.DARK_EDITION,function() + context.card:set_edition({cry_oversat = true}, true) + context.card.ability.couponed = true + context.card:set_cost() + context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + tag.triggered = true + end + end + end, +} +local mosaic_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 5, y = 1 }, + name = "cry-Mosaic Tag", + order = 3, + config = { type = "store_joker_modify", edition = "cry_mosaic" }, + key = "mosaic", + requires = 'e_cry_mosaic', + min_ante = 2, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_mosaic + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_modify" then + local _applied = nil + if not context.card.edition and not context.card.temp_edition and context.card.ability.set == 'Joker' then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + context.card.temp_edition = true + tag:yep('+', G.C.DARK_EDITION,function() + context.card:set_edition({cry_mosaic = true}, true) + context.card.ability.couponed = true + context.card:set_cost() + context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + tag.triggered = true + end + end + end, +} +local gold_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 6, y = 0 }, + name = "cry-Golden Tag", + order = 6, + config = { type = "store_joker_modify", edition = "cry_gold" }, + key = "gold", + requires = 'e_cry_gold', + min_ante = 3, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_gold + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_modify" then + local _applied = nil + if not context.card.edition and not context.card.temp_edition and context.card.ability.set == 'Joker' then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + context.card.temp_edition = true + tag:yep('+', G.C.DARK_EDITION,function() + context.card:set_edition({cry_gold = true}, true) + context.card.ability.couponed = true + context.card:set_cost() + context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + tag.triggered = true + end + end + end, +} +local glass_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 4, y = 0 }, + name = "cry-Glass Tag", + order = 5, + config = { type = "store_joker_modify", edition = "cry_glass" }, + key = "glass", + requires = 'e_cry_glass', + min_ante = 3, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_glass + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_modify" then + local _applied = nil + if not context.card.edition and not context.card.temp_edition and context.card.ability.set == 'Joker' then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + context.card.temp_edition = true + tag:yep('+', G.C.DARK_EDITION,function() + context.card:set_edition({cry_glass = true}, true) + context.card.ability.couponed = true + context.card:set_cost() + context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + tag.triggered = true + end + end + end, +} +local blur_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 7, y = 0 }, + name = "cry-Blurred Tag", + order = 7, + config = { type = "store_joker_modify", edition = "cry_blur" }, + key = "blur", + requires = 'e_cry_blur', + min_ante = 3, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_blur + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_modify" then + local _applied = nil + if not context.card.edition and not context.card.temp_edition and context.card.ability.set == 'Joker' then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + context.card.temp_edition = true + tag:yep('+', G.C.DARK_EDITION,function() + context.card:set_edition({cry_blur = true}, true) + context.card.ability.couponed = true + context.card:set_cost() + context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + tag.triggered = true + end + end + end, +} +--order 8 reserved for Noisy tag (if it ever has a shader / comes into existence) +local astral_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 6, y = 1 }, + name = "cry-Astral Tag", + order = 9, + config = { type = "store_joker_modify", edition = "cry_astral" }, + key = "astral", + requires = 'e_cry_astral', + min_ante = 9, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_astral + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_modify" then + local _applied = nil + if not context.card.edition and not context.card.temp_edition and context.card.ability.set == 'Joker' then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + context.card.temp_edition = true + tag:yep('+', G.C.DARK_EDITION,function() + context.card:set_edition({cry_astral = true}, true) + context.card.ability.couponed = true + context.card:set_cost() + context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + tag.triggered = true + end + end + end, +} +local m_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 4, y = 1 }, + name = "cry-Jolly Tag", + order = 10, + config = { type = "store_joker_modify", edition = "cry_m" }, + key = "m", + requires = 'e_cry_m', + min_ante = 1, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_m + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_modify" then + local _applied = nil + if not context.card.edition and not context.card.temp_edition and context.card.ability.set == 'Joker' then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + context.card.temp_edition = true + tag:yep('M', G.C.DARK_EDITION,function() + context.card:set_edition({cry_m = true}, true) + context.card.ability.couponed = true + context.card:set_cost() + context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + tag.triggered = true + end + end + end, +} +--This is fully funcional but unobtainable without pointer at the moment +--I have plans for this soon, very soon... +local double_m_tag = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 7, y = 2 }, + name = "cry-Double M Tag", + order = 11, + config = { type = "store_joker_create", edition = "cry_m" }, + key = "double_m", + requires = 'j_cry_smallestm', + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_m + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_create" then + local card + local option = {} + for k, _ in pairs(Cryptid.M_jokers) do + if G.P_CENTERS[k] then + option[#option + 1] = k + end + end + card = create_card( + "Joker", + context.area, + nil, + nil, + nil, + nil, + pseudorandom_element(option, pseudoseed("M_is_love_M_is_life")) + ) + card:set_edition({ + cry_m = true, + }) + create_shop_card_ui(card, "Joker", context.area) + card.states.visible = false + tag:yep("MM", G.C.CRY_JOLLY, function() + card:start_materialize() + card.ability.couponed = true + card:set_cost() + return true + end) + tag.triggered = true + return card + end + end, + in_pool = function() + return false + end, +} +local banana = { + object_type = "Tag", + name = "cry-Banana Tag", + order = 27, + atlas = "tag_cry", + pos = { x = 5, y = 2 }, + config = { type = "immediate" }, + key = "banana", + loc_vars = function(self, info_queue) + local banana + if G.GAME.pool_flags.gros_michel_extinct == true then + banana = localize({ + type = "name_text", + set = "Joker", + key = G.P_CENTER_POOLS["Joker"][61].key + }) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_cavendish", + specific_vars = { 3, G.GAME.probabilities.normal or 1, 1000 } + } + else + banana = localize({ + type = "name_text", + set = "Joker", + key = G.P_CENTER_POOLS["Joker"][38].key + }) + info_queue[#info_queue + 1] = { + set = "Joker", + key = "j_gros_michel", + specific_vars = { 15, G.GAME.probabilities.normal or 1, 6 } + } + end + return { vars = { banana } } + end, + min_ante = 2, + apply = function(self, tag, context) + if context.type == "immediate" then + if G.jokers and #G.jokers.cards < G.jokers.config.card_limit then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep('+', G.C.PURPLE, function() + if G.jokers and #G.jokers.cards < G.jokers.config.card_limit then --Needs another check here because that's how tags work :D:D:D:D:D:D:D + if G.GAME.pool_flags.gros_michel_extinct == true then + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_cavendish") + card:add_to_deck() + G.jokers:emplace(card) + else + local card = create_card("Joker", G.jokers, nil, nil, nil, nil, "j_gros_michel") + card:add_to_deck() + G.jokers:emplace(card) + end + end + G.CONTROLLER.locks[lock] = nil + return true + end) + else + tag:nope() + end + tag.triggered = true + return true + end + + end +} +local scope = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 6, y = 2 }, + name = "cry-Scope Tag", + order = 23, + config = { type = "round_start_bonus", num = 2 }, + key = "scope", + min_ante = 2, + loc_vars = function(self, info_queue) + return { vars = {self.config.num} } + end, + apply = function(self, tag, context) + if context.type == 'round_start_bonus' then + tag:yep('+', G.C.BLUE,function() + return true + end) + ease_hands_played(tag.config.num) + ease_discard(tag.config.num) + tag.triggered = true + return true + end + end, +} +local loss = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 1, y = 3 }, + name = "cry-Loss Tag", + order = 25, + config = { type = "new_blind_choice" }, + key = "loss", + min_ante = 2, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = { set = "Other", key = "p_cry_meme_1", specific_vars = { 2, 5 } } + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "new_blind_choice" then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep('+', G.C.SECONDARY_SET.Spectral,function() + local key = 'p_cry_meme_1' + local card = Card(G.play.T.x + G.play.T.w/2 - G.CARD_W*1.27/2, + G.play.T.y + G.play.T.h/2-G.CARD_H*1.27/2, G.CARD_W*1.27, G.CARD_H*1.27, G.P_CARDS.empty, G.P_CENTERS[key], {bypass_discovery_center = true, bypass_discovery_ui = true}) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({config = {ref_table = card}}) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + G.CONTROLLER.locks[lock] = nil + return true + end) + tag.triggered = true + return true + end + end, +} +local gourmand = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 2, y = 3 }, + name = "cry-Gourmand Tag", + order = 17, + config = { type = "store_joker_create" }, + key = "gourmand", + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = { set = "Other", key = "food_jokers" } + return { vars = {} } + end, + apply = function(self, tag, context) + if context.type == "store_joker_create" then + local card + card = create_card( + "Joker", + context.area, + nil, + nil, + nil, + nil, + Cryptid.get_food("cry_gourmand_tag") + ) + create_shop_card_ui(card, "Joker", context.area) + card.states.visible = false + tag:yep("+", G.C.GREEN, function() + card:start_materialize() + card.ability.couponed = true + card:set_cost() + return true + end) + tag.triggered = true + return card + end + end, +} +local better_top_up = { + object_type = "Tag", + name = "cry-Better Top-up Tag", + order = 15, + atlas = "tag_cry", + pos = { x = 4, y = 3 }, + config = { type = "immediate", spawn_jokers = 2 }, + key = "bettertop_up", + loc_vars = function(self, info_queue) + return { vars = {self.config.spawn_jokers} } + end, + min_ante = 5, + apply = function(self, tag, context) + if context.type == "immediate" then + if G.jokers and #G.jokers.cards < G.jokers.config.card_limit then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep('+', G.C.GREEN, function() + for i = 1, tag.config.spawn_jokers do + if G.jokers and #G.jokers.cards < G.jokers.config.card_limit then + local card = create_card("Joker", G.jokers, nil, 0.8, nil, nil, nil, 'bettertop') + card:add_to_deck() + G.jokers:emplace(card) + end + end + G.CONTROLLER.locks[lock] = nil + return true + end) + else + tag:nope() + end + tag.triggered = true + return true + end + end +} +local better_voucher = { + object_type = "Tag", + atlas = "tag_cry", + pos = { x = 3, y = 3 }, + name = "cry-Golden Voucher Tag", + order = 14, + config = { type = "voucher_add" }, + min_ante = 4, + key = "better_voucher", + loc_vars = function(self, info_queue) + return { vars = {(SMODS.Mods["Tier3Sub"] and 4 or 3)} } + end, + apply = function(self, tag, context) + if context.type == "voucher_add" then + tag:yep('+', G.C.SECONDARY_SET.Voucher,function() + G.ARGS.voucher_tag = G.ARGS.voucher_tag or {} + local voucher_key = get_next_megavoucher_key(true) + G.ARGS.voucher_tag[voucher_key] = true + G.shop_vouchers.config.card_limit = G.shop_vouchers.config.card_limit + 1 + local card = Card(G.shop_vouchers.T.x + G.shop_vouchers.T.w/2, + G.shop_vouchers.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[voucher_key],{bypass_discovery_center = true, bypass_discovery_ui = true}) + cry_misprintize(card) + create_shop_card_ui(card, 'Voucher', G.shop_vouchers) + card:start_materialize() + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true) + end + + if G.GAME.modifiers.cry_force_sticker == 'eternal' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.modifiers.cry_force_sticker == 'perishable' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_perishable(true) + card.ability.perishable = true + end + if G.GAME.modifiers.cry_force_sticker == 'rental' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_rental(true) + card.ability.rental = true + end + if G.GAME.modifiers.cry_force_sticker == 'pinned' or G.GAME.modifiers.cry_sticker_sheet_plus then + card.pinned = true + end + if G.GAME.modifiers.cry_force_sticker == 'banana' or G.GAME.modifiers.cry_sticker_sheet_plus then + card.ability.banana = true + end + if G.GAME.modifiers.cry_sticker_sheet_plus then + if G.GAME.modifiers.cry_sticker_sheet then + for k, v in pairs(SMODS.Stickers) do + v:set_sticker(card, true) + end + end + end + G.shop_vouchers:emplace(card) + G.ARGS.voucher_tag = nil + return true + end) + tag.triggered = true + end + end, +} +local booster = { + object_type = "Tag", + name = "cry-Booster Tag", + order = 28, + atlas = "tag_cry", + pos = { x = 5, y = 3 }, + config = { type = "immediate" }, + key = "booster", + loc_vars = function(self, info_queue) + return { vars = {} } + end, + min_ante = 4, + apply = function(self, tag, context) + if context.type == "immediate" then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep('+', G.C.BLUE, function() + G.GAME.boostertag = true + G.CONTROLLER.locks[lock] = nil + return true + end) + tag.triggered = true + return true + end + + end +} +local tagitems = { + cat, + empoweredPack, + empowered, + gambler, + bundle, + memory, + schematic, + banana, + scope, + gourmand, + better_top_up, + booster, + empoweredpack_sprite, +} +if Cryptid.enabled["Vouchers"] then + tagitems[#tagitems + 1] = better_voucher +end +if Cryptid.enabled["Epic Jokers"] then + tagitems[#tagitems + 1] = epic_tag +end +if Cryptid.enabled["Misc."] then + tagitems[#tagitems + 1] = glitched_tag + tagitems[#tagitems + 1] = oversat_tag + tagitems[#tagitems + 1] = mosaic_tag + tagitems[#tagitems + 1] = gold_tag + tagitems[#tagitems + 1] = glass_tag + tagitems[#tagitems + 1] = blur_tag + tagitems[#tagitems + 1] = astral_tag + tagitems[#tagitems + 1] = loss + if Cryptid.enabled["M Jokers"] then + tagitems[#tagitems + 1] = m_tag + tagitems[#tagitems + 1] = double_m_tag + end +end +return { + name = "Tags", + init = function() + --Memory Tag Patches - store last tag used + local tapr = Tag.apply_to_run + function Tag:apply_to_run(x) + local ret = tapr(self, x) + if + self.triggered + and self.key ~= "tag_double" + and self.key ~= "tag_cry_memory" + and self.key ~= "tag_cry_triple" + and self.key ~= "tag_cry_quadruple" + and self.key ~= "tag_cry_quintuple" + then + G.GAME.cry_last_tag_used = self.key + G.GAME.cry_memory_orbital = self.ability.orbital_hand + end + return ret + end + end, + items = tagitems, +} diff --git a/Cryptid/Items/Vouchers.lua b/Cryptid/Items/Vouchers.lua new file mode 100644 index 0000000..6665403 --- /dev/null +++ b/Cryptid/Items/Vouchers.lua @@ -0,0 +1,971 @@ +local voucher_atlas = { + object_type = "Atlas", + key = "atlasvoucher", + path = "atlasvoucher.png", + px = 71, + py = 95, +} +local copies = { --Double tags become Triple Tags and are 2X as common + object_type = "Voucher", + key = "copies", + atlas = "atlasvoucher", + order = 1, + pos = { x = 1, y = 1 }, + loc_vars = function(self, info_queue) + info_queue[#info_queue+1] = {set = "Tag", key = "tag_double"} + info_queue[#info_queue+1] = {set = "Tag", key = "tag_cry_triple", specific_vars = {2}} + return { vars = {} } + end, +} +local tag_printer = { --Double tags become Quadruple Tags and are 3X as common + object_type = "Voucher", + key = "tag_printer", + order = 2, + atlas = "atlasvoucher", + pos = { x = 1, y = 2 }, + loc_vars = function(self, info_queue) + info_queue[#info_queue+1] = {set = "Tag", key = "tag_double"} + info_queue[#info_queue+1] = {set = "Tag", key = "tag_cry_quadruple", specific_vars = {3}} + return { vars = {} } + end, + requires = { "v_cry_copies" }, +} +local clone_machine = { --Double tags become Quintuple Tags and are 4X as common + object_type = "Voucher", + key = "clone_machine", + atlas = "atlasvoucher", + order = 91, + pos = { x = 1, y = 3 }, + loc_vars = function(self, info_queue) + info_queue[#info_queue+1] = {set = "Tag", key = "tag_double"} + info_queue[#info_queue+1] = {set = "Tag", key = "tag_cry_quintuple", specific_vars = {4}} + return { vars = {} } + end, + requires = { "v_cry_tag_printer" }, +} +local command_prompt = { --Code cards can appear in the shop + object_type = "Voucher", + key = "command_prompt", + atlas = "atlasvoucher", + order = 3, + pos = { x = 0, y = 1 }, + loc_vars = function(self, info_queue) + return { vars = {} } + end, + redeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.code_rate = (G.GAME.code_rate or 0) + 4 + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.code_rate = math.max(0, G.GAME.code_rate - 4) + return true + end, + })) + end, +} +local satellite_uplink = { --Code cards may appear in any of the Celestial Packs + object_type = "Voucher", + key = "satellite_uplink", + atlas = "atlasvoucher", + order = 4, + pos = { x = 0, y = 2 }, + loc_vars = function(self, info_queue) + return { vars = {} } + end, + requires = { "v_cry_command_prompt" }, +} +local quantum_computing = { --Code cards can spawn with Negative addition + object_type = "Voucher", + key = "quantum_computing", + order = 92, + atlas = "atlasvoucher", + pos = { x = 0, y = 3 }, + loc_vars = function(self, info_queue) + return { vars = {} } + end, + requires = { "v_cry_satellite_uplink" }, +} +local pairing = { --Retrigger all M Jokers if played hand is a Pair + object_type = "Voucher", + key = "pairing", + atlas = "atlasvoucher", + order = 5, + pos = { x = 4, y = 5 }, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Xaltios", + }, + }, + in_pool = function(self) + local mcheck = get_m_jokers() + if mcheck > 0 then + return true + end + return false + end, +} +local repair_man = { --Retrigger all M Jokers if played hand contains a pair + object_type = "Voucher", + key = "repair_man", + atlas = "atlasvoucher", + order = 6, + pos = { x = 5, y = 5 }, + requires = { "v_cry_pairing" }, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Xaltios", + }, + }, + in_pool = function(self) + local mcheck = get_m_jokers() + if mcheck > 0 then + return true + end + return false + end, +} +local pairamount_plus = { --Retrigger all M Jokers once for every pair contained in played hand + object_type = "Voucher", + key = "pairamount_plus", + atlas = "atlasvoucher", + order = 93, + pos = { x = 6, y = 5 }, + requires = { "v_cry_repair_man" }, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Xaltios", + }, + }, + in_pool = function(self) + local mcheck = get_m_jokers() + if mcheck > 0 then + return true + end + return false + end, +} +local double_vision = { --Double-Sided cards appear 4x more frequently + object_type = "Voucher", + key = "double_vision", + order = 7, + atlas = "atlasvoucher", + pos = { x = 4, y = 3 }, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_double_sided + end, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Axolotolus", + }, + }, +} +local double_slit = { --Meld can appear in the shop and Arcana Packs + object_type = "Voucher", + key = "double_slit", + atlas = "atlasvoucher", + order = 8, + pos = { x = 3, y = 4 }, + requires = { "v_cry_double_vision" }, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.c_cry_meld + end, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Axolotolus", + }, + }, +} +local double_down = { --After every round, X1.5 to all values on the back of Double-Sided Cards + object_type = "Voucher", + key = "double_down", + atlas = "atlasvoucher", + order = 94, + pos = { x = 4, y = 4 }, + requires = { "v_cry_double_slit" }, + loc_vars = function(self, info_queue) + info_queue[#info_queue + 1] = G.P_CENTERS.e_cry_double_sided + end, + cry_credits = { + jolly = { + "Jolly Open Winner", + "Axolotolus", + }, + }, +} +local overstock_multi = { --+1 card slot[s] and +1 booster pack slot[s] available in the shop + object_type = "Voucher", + key = "overstock_multi", + config = { extra = 1 }, + atlas = "atlasvoucher", + order = 75, + pos = { x = 4, y = 1 }, + requires = { "v_overstock_plus" }, + loc_vars = function(self, info_queue) + return { vars = { math.max(1, math.floor(self.config.extra)) } } + end, + redeem = function(self) + if not G.GAME.modifiers.cry_booster_packs then + G.GAME.modifiers.cry_booster_packs = 2 + end + G.GAME.modifiers.cry_booster_packs = G.GAME.modifiers.cry_booster_packs + + math.max(1, math.floor(self.config.extra)) --Booster slots + G.E_MANAGER:add_event(Event({ + func = function() --card slot + change_shop_size(math.max(1, math.floor(self.config.extra))) + return true + end, + })) + end, + unredeem = function(self) + if not G.GAME.modifiers.cry_booster_packs then + G.GAME.modifiers.cry_booster_packs = 2 + end + G.GAME.modifiers.cry_booster_packs = G.GAME.modifiers.cry_booster_packs + - math.max(1, math.floor(self.config.extra)) --Booster slots + G.E_MANAGER:add_event(Event({ + func = function() --card slot + change_shop_size(math.min(-1, -1*math.floor(self.config.extra))) + return true + end, + })) + end, +} +local massproduct = { --All cards and packs in the shop cost $1 + object_type = "Voucher", + key = "massproduct", + atlas = "atlasvoucher", + order = 76, + pos = { x = 6, y = 4 }, + requires = { "v_liquidation" }, + redeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.backup_discount_percent = G.GAME.backup_discount_percent or G.GAME.discount_percent + G.GAME.discount_percent = 100 + for k, v in pairs(G.I.CARD) do + if v.set_cost then + v:set_cost() + end + end + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.discount_percent = G.GAME.backup_discount_percent or 0 + for k, v in pairs(G.I.CARD) do + if v.set_cost then + v:set_cost() + end + end + return true + end, + })) + end, +} +local curate = { --All cards appear with an Edition + object_type = "Voucher", + key = "curate", + atlas = "atlasvoucher", + order = 77, + pos = { x = 6, y = 1 }, + requires = { "v_glow_up" }, +} +local rerollexchange = { --All rerolls cost $2 + object_type = "Voucher", + key = "rerollexchange", + atlas = "atlasvoucher", + order = 78, + pos = { x = 6, y = 2 }, + requires = { "v_reroll_glut" }, + redeem = function(self) + --most of the code for this (one line) is in cryptid.lua, check out the reroll function there + G.E_MANAGER:add_event(Event({ + func = function() + if G.GAME.current_round.reroll_cost > 2 then + G.GAME.current_round.reroll_cost = 2 + end + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + calculate_reroll_cost(true) + return true + end, + })) + end, +} +--Order 79 reserved for celestial storage (unimplemented) +local scope = { --Also unimplemented + object_type = "Voucher", + key = "scope", + atlas = "atlasvoucher", + order = 80, + pos = { x = 2, y = 0 }, + requires = { "v_observatory" }, +} +local dexterity = { --Permanently gain +2 hand[s] each round + object_type = "Voucher", + key = "dexterity", + config = { extra = 2 }, + atlas = "atlasvoucher", + order = 81, + pos = { x = 6, y = 3 }, + requires = { "v_nacho_tong" }, + loc_vars = function(self, info_queue) + return { vars = { math.max(1, math.floor(self.config.extra)) } } + end, + redeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.round_resets.hands = G.GAME.round_resets.hands + math.max(1, math.floor(self.config.extra)) + ease_hands_played(math.max(1, math.floor(self.config.extra))) + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.round_resets.hands = G.GAME.round_resets.hands - math.max(1, math.floor(self.config.extra)) + ease_hands_played(math.min(-1, -1*math.floor(self.config.extra))) + return true + end, + })) + end, +} +local threers = { --Permanently gain +2 discard[s] each round + object_type = "Voucher", + key = "threers", + config = { extra = 2 }, + atlas = "atlasvoucher", + order = 82, + pos = { x = 5, y = 0 }, + requires = { "v_recyclomancy" }, + loc_vars = function(self, info_queue) + return { vars = { math.max(1, math.floor(self.config.extra)) } } + end, + redeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.round_resets.discards = G.GAME.round_resets.discards + math.max(1, math.floor(self.config.extra)) + ease_discard(math.max(1, math.floor(self.config.extra))) + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.round_resets.discards = G.GAME.round_resets.discards - math.max(1, math.floor(self.config.extra)) + ease_discard(math.min(-1, math.floor(-1*self.config.extra))) + return true + end, + })) + end, +} +local tacclimator = { --Tarot cards appear X6 more frequently in the shop All future Tarot cards are free + object_type = "Voucher", + key = "tacclimator", + config = { extra = 56 / 4, extra_disp = 6 }, --blame thunk for this extra value + atlas = "atlasvoucher", + order = 83, + pos = { x = 1, y = 4 }, + requires = { "v_tarot_tycoon" }, + loc_vars = function(self, info_queue) + return { vars = { self.config.extra_disp } } + end, + redeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.tarot_rate = 4 * self.config.extra + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.tarot_rate = G.GAME.tarot_rate / self.config.extra * (56/4) / 6 + return true + end, + })) + end, +} +local pacclimator = { --Planet cards appear X6 more frequently in the shop All future Planet cards are free + object_type = "Voucher", + key = "pacclimator", + config = { extra = 56 / 4, extra_disp = 6 }, --blame thunk for this extra value + atlas = "atlasvoucher", + order = 84, + pos = { x = 0, y = 4 }, + requires = { "v_planet_tycoon" }, + loc_vars = function(self, info_queue) + return { vars = { self.config.extra_disp } } + end, + redeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.planet_rate = 4 * self.config.extra + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.planet_rate = G.GAME.planet_rate / self.config.extra * (56/4) / 6 + return true + end, + })) + end, +} +local moneybean = { --Raise the cap on interest earned in each round to $2.0e299 + object_type = "Voucher", + key = "moneybean", + config = { extra = 1e300 }, + atlas = "atlasvoucher", + order = 85, + pos = { x = 5, y = 1 }, + requires = { "v_money_tree" }, + loc_vars = function(self, info_queue) + return { vars = { self.config.extra / 5 } } + end, + redeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.interest_cap = self.config.extra + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.interest_cap = math.max(25, (G.P_CENTERS.v_money_tree.config.extra or 0), (G.P_CENTERS.v_seed_money.config.extra or 0)) + return true + end, + })) + end, +} +local fabric = { --+2 Joker slot[s] + object_type = "Voucher", + key = "fabric", + config = { extra = 2 }, + atlas = "atlasvoucher", + order = 86, + pos = { x = 6, y = 0 }, + requires = { "v_antimatter" }, + loc_vars = function(self, info_queue) + return { vars = { math.max(1, math.floor(self.config.extra)) } } + end, + redeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + G.jokers.config.card_limit = G.jokers.config.card_limit + math.max(1, math.floor(self.config.extra)) + end + return true + end, + })) + end, + unredeem = function(self) + G.E_MANAGER:add_event(Event({ + func = function() + if G.jokers then + G.jokers.config.card_limit = G.jokers.config.card_limit - math.max(1, math.floor(self.config.extra)) + end + return true + end, + })) + end, +} +--Order 87 reserved for Fake-out (unimplemented) +local function asteroglyph_ante() + if not (G.GAME or {}).modifiers then + return 0 + end + if not G.GAME.modifiers.cry_astero_ante then + G.GAME.modifiers.cry_astero_ante = 0 + end + return G.GAME.modifiers.cry_astero_ante +end + +local asteroglyph = { --Set Ante to 0 + object_type = "Voucher", + key = "asteroglyph", + atlas = "atlasvoucher", + order = 88, + pos = { x = 5, y = 2 }, + requires = { "v_petroglyph" }, + loc_vars = function(self, info_queue) + return { vars = { asteroglyph_ante() } } + end, + redeem = function(self) + local mod = -G.GAME.round_resets.ante + asteroglyph_ante() + ease_ante(mod) + G.GAME.modifiers.cry_astero_ante = (G.GAME.modifiers.cry_astero_ante or 0) > 0 + and math.min(math.ceil(G.GAME.modifiers.cry_astero_ante ^ 1.13), 1e300) + or 1 + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.round_resets.blind_ante = mod + return true + end, + })) + end +} +--Order 89 reserved for Ivory Script (unimplemented) +local blankcanvas = { --+2 hand size + object_type = "Voucher", + key = "blankcanvas", + config = { extra = 2 }, + atlas = "atlasvoucher", + order = 90, + pos = { x = 2, y = 4 }, + requires = { "v_palette" }, + loc_vars = function(self, info_queue) + return { vars = { math.max(1, math.floor(self.config.extra)) } } + end, + redeem = function(self) + G.hand:change_size(math.max(1, math.floor(self.config.extra))) + end, + unredeem = function(self) + G.hand:change_size(-1*math.max(1, math.floor(self.config.extra))) + end, +} + +local stickyhand = { --+1 card selection limit + object_type = "Voucher", + key = "stickyhand", + config = { extra = 1 }, + atlas = "atlasvoucher", + order = 9, + pos = { x = 0, y = 5 }, + loc_vars = function(self, info_queue) + return { vars = { math.max(1, math.floor(self.config.extra)) } } + end, + redeem = function(self) + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit + + math.max(1, math.floor(self.config.extra)) + end, + unredeem = function(self) + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit + - math.max(1, math.floor(self.config.extra)) + if G.hand.config.highlighted_limit < 5 then G.hand.config.highlighted_limit = 5 end + G.hand:unhighlight_all() + end, +} + +local grapplinghook = { --+1 card selection limit (replace me when "extra functionality" is added later) + object_type = "Voucher", + key = "grapplinghook", + config = { extra = 2 }, + atlas = "atlasvoucher", + order = 10, + pos = { x = 1, y = 5 }, + requires = { "v_cry_stickyhand" }, + loc_vars = function(self, info_queue) + return { vars = { math.max(1, math.floor(self.config.extra)) } } + end, + redeem = function(self) + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit + + math.max(1, math.floor(self.config.extra)) + end, + unredeem = function(self) + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit + - math.max(1, math.floor(self.config.extra)) + if G.hand.config.highlighted_limit < 5 then G.hand.config.highlighted_limit = 5 end + G.hand:unhighlight_all() + end, +} + +local hyperspacetether = { --+2 card selection limit (replace me when "extra functionality" is added later) + object_type = "Voucher", + key = "hyperspacetether", + config = { extra = 2 }, + atlas = "atlasvoucher", + pos = { x = 2, y = 5 }, + order = 95, + requires = { "v_cry_grapplinghook" }, + loc_vars = function(self, info_queue) + return { vars = { math.max(1, math.floor(self.config.extra)) } } + end, + redeem = function(self) + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit + + math.max(1, math.floor(self.config.extra)) + end, + unredeem = function(self) + G.hand.config.highlighted_limit = G.hand.config.highlighted_limit + - math.max(1, math.floor(self.config.extra)) + if G.hand.config.highlighted_limit < 5 then G.hand.config.highlighted_limit = 5 end + G.hand:unhighlight_all() + end, +} + + +local triple = { --Copies voucher triple tag + object_type = "Tag", + atlas = "tag_cry", + name = "cry-Triple Tag", + order = 20, + pos = { x = 0, y = 1 }, + config = { type = "tag_add", num = 2 }, + key = "triple", + loc_vars = function(self, info_queue) + return { vars = { self.config.num } } + end, + apply = function(self, tag, context) + if + context.type == "tag_add" + and context.tag.key ~= "tag_double" + and context.tag.key ~= "tag_cry_triple" + and context.tag.key ~= "tag_cry_quadruple" + and context.tag.key ~= "tag_cry_quintuple" + and context.tag.key ~= "tag_cry_memory" + then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep("+", G.C.RED, function() + if context.tag.ability and context.tag.ability.orbital_hand then + G.orbital_hand = context.tag.ability.orbital_hand + end + for i = 1, tag.config.num do + local tag = Tag(context.tag.key) + if context.tag.key == "tag_cry_rework" then + tag.ability.rework_edition = context.tag.ability.rework_edition + tag.ability.rework_key = context.tag.ability.rework_key + end + add_tag(tag) + end + G.orbital_hand = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + tag.triggered = true + return true + end + end, + in_pool = function() + return G.GAME.used_vouchers.v_cry_copies + end, +} +local quadruple = { --Tag printer voucher quadruple tag + object_type = "Tag", + atlas = "tag_cry", + name = "cry-Quadruple Tag", + order = 21, + pos = { x = 1, y = 1 }, + config = { type = "tag_add", num = 3 }, + key = "quadruple", + loc_vars = function(self, info_queue) + return { vars = { self.config.num } } + end, + apply = function(self, tag, context) + if + context.type == "tag_add" + and context.tag.key ~= "tag_double" + and context.tag.key ~= "tag_cry_triple" + and context.tag.key ~= "tag_cry_quadruple" + and context.tag.key ~= "tag_cry_quintuple" + and context.tag.key ~= "tag_cry_memory" + then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep("+", G.C.RED, function() + if context.tag.ability and context.tag.ability.orbital_hand then + G.orbital_hand = context.tag.ability.orbital_hand + end + for i = 1, tag.config.num do + local tag = Tag(context.tag.key) + if context.tag.key == "tag_cry_rework" then + tag.ability.rework_edition = context.tag.ability.rework_edition + tag.ability.rework_key = context.tag.ability.rework_key + end + add_tag(tag) + end + G.orbital_hand = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + tag.triggered = true + return true + end + end, + in_pool = function() + return G.GAME.used_vouchers.v_cry_tag_printer + end, +} +local quintuple = { --Clone machine voucher quintuple tag + object_type = "Tag", + atlas = "tag_cry", + name = "cry-Quintuple Tag", + order = 22, + pos = { x = 2, y = 1 }, + config = { type = "tag_add", num = 4 }, + key = "quintuple", + loc_vars = function(self, info_queue) + return { vars = { self.config.num } } + end, + apply = function(self, tag, context) + if + context.type == "tag_add" + and context.tag.key ~= "tag_double" + and context.tag.key ~= "tag_cry_triple" + and context.tag.key ~= "tag_cry_quadruple" + and context.tag.key ~= "tag_cry_quintuple" + and context.tag.key ~= "tag_cry_memory" + then + local lock = tag.ID + G.CONTROLLER.locks[lock] = true + tag:yep("+", G.C.RED, function() + if context.tag.ability and context.tag.ability.orbital_hand then + G.orbital_hand = context.tag.ability.orbital_hand + end + for i = 1, tag.config.num do + local tag = Tag(context.tag.key) + if context.tag.key == "tag_cry_rework" then + tag.ability.rework_edition = context.tag.ability.rework_edition + tag.ability.rework_key = context.tag.ability.rework_key + end + add_tag(tag) + end + G.orbital_hand = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + tag.triggered = true + return true + end + end, + in_pool = function() + return G.GAME.used_vouchers.v_cry_clone_machine + end, +} +-- If Tier 3 Vouchers is loaded, make Cryptid function as Tier 4 Vouchers +if SMODS.Mods["Tier3Sub"] then + overstock_multi.requires[#overstock_multi.requires + 1] = "v_overstock_three" + massproduct.requires[#massproduct.requires + 1] = "v_money_mint" + curate.requires[#curate.requires + 1] = "v_glow_in_dark" + rerollexchange.requires[#rerollexchange.requires + 1] = "v_reroll_addict" + dexterity.requires[#dexterity.requires + 1] = "v_applause" + threers.requires[#threers.requires + 1] = "v_down_to_zero" + tacclimator.requires[#tacclimator.requires + 1] = "v_tarot_factory" + pacclimator.requires[#pacclimator.requires + 1] = "v_planet_factory" + moneybean.requires[#moneybean.requires + 1] = "v_money_forest" + fabric.requires[#fabric.requires + 1] = "v_neutral_particle" + asteroglyph.requires[#asteroglyph.requires + 1] = "v_in_the_beginning" + blankcanvas.requires[#blankcanvas.requires + 1] = "v_happy_accident" + tacclimator.config.extra = tacclimator.config.extra * 8 + pacclimator.config.extra = pacclimator.config.extra * 8 +end + +--Add T3 Voucher pool for Golden Voucher Tag (in Tags.lua) and maybe other things in the future +--I am sorry in advance (this is extremely cursed) + +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_overstock_multi" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_massproduct" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_curate" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_rerollexchange" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_dexterity" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_threers" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_tacclimator" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_pacclimator" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_moneybean" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_fabric" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_asteroglyph" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_blankcanvas" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_hyperspacetether" +Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_clone_machine" + +if Cryptid.enabled["M Jokers"] then + Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_pairamount_plus" +end +if Cryptid.enabled["Code Cards"] then + Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_quantum_computing" +end +if Cryptid.enabled["Misc."] then + Cryptid.Megavouchers[#Cryptid.Megavouchers + 1] = "v_cry_double_down" +end + +function megavoucherpool(_type, _rarity, legendary, key_append) + G.ARGS.TEMP_POOL = EMPTY(G.ARGS.TEMP_POOL) + local _pool, _starting_pool, _pool_key, _pool_size = G.ARGS.TEMP_POOL, {}, "megavoucher", 0 + + for k, v in pairs(Cryptid.Megavouchers) do + if v then + _starting_pool[#_starting_pool + 1] = G.P_CENTERS[v] + end + end + + for k, v in ipairs(_starting_pool) do + local add = false + + if not G.GAME.cry_owned_vouchers[v.key] then + local check = true + if G.shop_vouchers and G.shop_vouchers.cards then + for kk, vv in ipairs(G.shop_vouchers.cards) do + if vv.config.center.key == v.key then check = false end + end + end + if check then + add = true + end + end + + if add and not G.GAME.banned_keys[v.key] then + _pool[#_pool + 1] = v.key + _pool_size = _pool_size + 1 + end + + if _pool_size == 0 then + _pool = EMPTY(G.ARGS.TEMP_POOL) + _pool[#_pool + 1] = "v_blank" + end + end + + return _pool, _pool_key .. G.GAME.round_resets.ante +end + +local megavouchergetcurrentpool = get_current_pool +function get_current_pool(_type, _rarity, _legendary, _append) + if _type == "megavoucher" then + return megavoucherpool(_type, _rarity, _legendary, _append) + end + return megavouchergetcurrentpool(_type, _rarity, _legendary, _append) +end + +function get_next_megavoucher_key(_from_tag) + local _pool, _pool_key = get_current_pool('megavoucher') + if _from_tag then _pool_key = 'Voucher_fromtag' end + local center = pseudorandom_element(_pool, pseudoseed(_pool_key)) + local it = 1 + while center == 'UNAVAILABLE' do + it = it + 1 + center = pseudorandom_element(_pool, pseudoseed(_pool_key..'_resample'..it)) + end + + return center +end + +local voucheritems = { + voucher_atlas, + copies, + tag_printer, + triple, + quadruple, + quintuple, + overstock_multi, + massproduct, + curate, + rerollexchange, + dexterity, + threers, + tacclimator, + pacclimator, + moneybean, + fabric, + asteroglyph, + blankcanvas, + clone_machine, + stickyhand, + grapplinghook, + hyperspacetether +} +if Cryptid.enabled["Code Cards"] then + voucheritems[#voucheritems + 1] = command_prompt + voucheritems[#voucheritems + 1] = satellite_uplink + voucheritems[#voucheritems + 1] = quantum_computing +end +if Cryptid.enabled["M Jokers"] then + voucheritems[#voucheritems + 1] = pairing + voucheritems[#voucheritems + 1] = repair_man + voucheritems[#voucheritems + 1] = pairamount_plus +end +if Cryptid.enabled["Misc."] then + voucheritems[#voucheritems + 1] = double_vision + voucheritems[#voucheritems + 1] = double_slit + voucheritems[#voucheritems + 1] = double_down +end +return { + name = "Vouchers", + init = function() + --Curate + local pe = poll_edition + function poll_edition(_key, _mod, _no_neg, _guaranteed, _options) + local ed = pe(_key, _mod, _no_neg, _guaranteed, _options) + while not ed and G.GAME.used_vouchers.v_cry_curate do + ed = pe(_key, _mod, _no_neg, _guaranteed, _options) + end + return ed + end + --Copies and upgrades + local gcp = get_current_pool + function get_current_pool(type, rarity, legendary, append, z) + pool, pool_append = gcp(type, rarity, legendary, append, z) + if type == "Tag" then + for i = 1, #pool do + if pool[i] == "tag_double" and G.GAME.used_vouchers.v_cry_copies then + pool[i] = "tag_cry_triple" + end + if + (pool[i] == "tag_double" or pool[i] == "tag_cry_triple") + and G.GAME.used_vouchers.v_cry_tag_printer + then + pool[i] = "tag_cry_quadruple" + end + if + (pool[i] == "tag_double" or pool[i] == "tag_cry_triple" or pool[i] == "tag_cry_quadruple") + and G.GAME.used_vouchers.v_cry_clone_machine + then + pool[i] = "tag_cry_quintuple" + end + end + end + return pool, pool_append + end + local tinit = Tag.init + function Tag:init(tag, y, z) + if tag == "tag_double" and G.GAME.used_vouchers.v_cry_copies then + tag = "tag_cry_triple" + end + if (tag == "tag_double" or tag == "tag_cry_triple") and G.GAME.used_vouchers.v_cry_tag_printer then + tag = "tag_cry_quadruple" + end + if + (tag == "tag_double" or tag == "tag_cry_triple" or tag == "tag_cry_quadruple") + and G.GAME.used_vouchers.v_cry_clone_machine + then + tag = "tag_cry_quintuple" + end + return tinit(self, tag, y, z) + end + local sc = Card.set_cost + function Card:set_cost() + sc(self) + if self.ability.set == "Tarot" and G.GAME.used_vouchers.v_cry_tacclimator then --Make Tarots free when Tarot Acclimator is redeemed + self.cost = 0 + end + if self.ability.set == "Planet" and G.GAME.used_vouchers.v_cry_pacclimator then --Make Planets free when Planet Acclimator is redeemed + self.cost = 0 + end + end + end, + items = voucheritems, +} diff --git a/Cryptid/Items/dummy_https.lua b/Cryptid/Items/dummy_https.lua new file mode 100644 index 0000000..6b513af --- /dev/null +++ b/Cryptid/Items/dummy_https.lua @@ -0,0 +1,3 @@ +-- dummy file to add https option to mod menu +return { name = "HTTPS Module" } +-- someone should make a system for this, engineers are crying diff --git a/Cryptid/Items/dummy_timerblinds.lua b/Cryptid/Items/dummy_timerblinds.lua new file mode 100644 index 0000000..68ee4e3 --- /dev/null +++ b/Cryptid/Items/dummy_timerblinds.lua @@ -0,0 +1,4 @@ +-- Dummy file, only here to add the timer blinds toggle to the menu +--Jevonn was here [MMMMMMM] +return { name = "Timer Mechanics" } +-- ^^^ M spotted diff --git a/Cryptid/README.md b/Cryptid/README.md new file mode 100644 index 0000000..3d39599 --- /dev/null +++ b/Cryptid/README.md @@ -0,0 +1,60 @@ +# Cryptid +An unbalanced Balatro mod. + +Note: Cryptid requires [Steamodded **1.0.0-Alpha**](https://github.com/Steamopollys/Steamodded/archive/refs/heads/main.zip) and [Talisman](https://github.com/MathIsFun0/Talisman/releases/latest). + +Cryptid currently adds: +![image](https://github.com/user-attachments/assets/0a03d6b2-d57f-4f92-9f42-a9ff201c5f2f) +![image](https://github.com/user-attachments/assets/c3f26d4d-2fb4-4b07-8f43-0d7a730e986d) + + +### [Official Discord](https://discord.gg/eUf9Ur6RyB) + +### [Video Installation Guide](https://www.youtube.com/watch?v=aUr0gXE77rk) + +### Frequently asked questions +* Credit to @jenwalter666 for creating this FAQ (forwarded from Cryptid Discord) + +## General + +> *My game is crashing on startup/something doesn't appear to be loading!* + +Make sure everything is properly installed. +For Talisman, the folder that contains Talisman should be named `Talisman`. If you've downloaded the source code from GitHub (by clicking on [Code] and then [Download ZIP]), the folder within the ZIP may be called `Talisman-main`, which will cause a problem. Simply rename the folder(s) so that the `-main` part of the name is removed, so that the folder is just called `Talisman`. +Whenever you are manually updating Talisman or Cryptid, or any other mod for that matter, it's a good approach to instead delete the contents of the folder, then install the contents of the folder from the ZIP into the existing folder to avoid having to constantly rename the folder over and over. +For automatic updates, try using the [automatic update script](https://discord.com/channels/1264429948970733782/1268911536638787625). +It could also be that Steamodded is out of date, sometimes mods may use features of a new version that's absent in an older version, but don't change the version that they ask for. +You can grab the latest Steamodded by [clicking here](https://github.com/Steamopollys/Steamodded/archive/refs/heads/main.zip). + +> *I can't see any new jokers/content!* + +If you've installed everything correctly, but no new jokers, blinds, etc. are ingame, check the Mods list by clicking on [MODS] in the main menu, and look for Cryptid/Talisman. +If they show up red, that means there's a problem. Hover over the mod icon (which should be an exclamation mark) to see what said problem is. It could be an outdated dependency in most cases. + +> *What's up with all the "M" jokers/references to Jolly Joker?* + +"M" is a reference to an incident in the official Balatro Discord where a user named mjiojio was spamming the letter "m" in various channels. When asked what their least favourite joker was, they answered with Jolly Joker, and that's how the "M" joke became a thing! + +> *There's some content in the mod I don't like! Is there any way to remove them?* + +You can disable some features of the mod by opening your Settings, going over to the Spectral Pack tab, clicking [Cryptid], then checking/unchecking what you want. +You can also enable/disable certain music tracks. If you're a streamer, consider disabling the Jimball music for your safety! + +> *I'm hitting infinity very often, is there any way to raise the limit of the scoring system?* + +Talisman may be configured to be on **BigNum** mode, which has a maximum limit of ee308. You can make the limit virtually disappear by changing it to **OmegaNum**, which can handle e10##1000. You can configure it the same way; going into Settings, then the Spectral Pack tab, then Talisman. Be careful; if you have a saved run that was on a different number system, you won't be able to load it (unless if you switch back)! + + +> *I've experienced a crash/bug!* + +Be sure to give us as much information about the bug/crash as possible. A way to reproduce the bug/crash is also especially useful information to help us fix it. +Remember; just saying you're crashing doesn't tell us anything. We need to know the details! + +> *How can I disable a specific boss blind/joker/etc.?* + +Let's use Blinds as an example. + +In your Balatro mods folder, navigate to `\Cryptid\Items` and find `Blinds.lua`. (Different types of items will be contained in different Lua files in this folder.) + +Towards the end of the file, there's an array named `items_togo`, near line 1230. You can disable specific Blinds by deleting the relevant lines here. These settings will take effect on a restart. +Most other files will have a similar items array near the bottom of the file that you can modify to remove specific things from Cryptid. This system will be changed to be in-game before Cryptid 0.6.0. diff --git a/Cryptid/assets/1x/atlasSleeves.png b/Cryptid/assets/1x/atlasSleeves.png new file mode 100644 index 0000000..5ba0b75 Binary files /dev/null and b/Cryptid/assets/1x/atlasSleeves.png differ diff --git a/Cryptid/assets/1x/atlasdeck.png b/Cryptid/assets/1x/atlasdeck.png new file mode 100644 index 0000000..f1afe6d Binary files /dev/null and b/Cryptid/assets/1x/atlasdeck.png differ diff --git a/Cryptid/assets/1x/atlasepic.png b/Cryptid/assets/1x/atlasepic.png new file mode 100644 index 0000000..f28df34 Binary files /dev/null and b/Cryptid/assets/1x/atlasepic.png differ diff --git a/Cryptid/assets/1x/atlasexotic.png b/Cryptid/assets/1x/atlasexotic.png new file mode 100644 index 0000000..60be5af Binary files /dev/null and b/Cryptid/assets/1x/atlasexotic.png differ diff --git a/Cryptid/assets/1x/atlasnotjokers.png b/Cryptid/assets/1x/atlasnotjokers.png new file mode 100644 index 0000000..627067c Binary files /dev/null and b/Cryptid/assets/1x/atlasnotjokers.png differ diff --git a/Cryptid/assets/1x/atlasone.png b/Cryptid/assets/1x/atlasone.png new file mode 100644 index 0000000..6942d8b Binary files /dev/null and b/Cryptid/assets/1x/atlasone.png differ diff --git a/Cryptid/assets/1x/atlasspooky.png b/Cryptid/assets/1x/atlasspooky.png new file mode 100644 index 0000000..63803aa Binary files /dev/null and b/Cryptid/assets/1x/atlasspooky.png differ diff --git a/Cryptid/assets/1x/atlasthree.png b/Cryptid/assets/1x/atlasthree.png new file mode 100644 index 0000000..8a3392f Binary files /dev/null and b/Cryptid/assets/1x/atlasthree.png differ diff --git a/Cryptid/assets/1x/atlastwo.png b/Cryptid/assets/1x/atlastwo.png new file mode 100644 index 0000000..fe9bf04 Binary files /dev/null and b/Cryptid/assets/1x/atlastwo.png differ diff --git a/Cryptid/assets/1x/atlasvoucher.png b/Cryptid/assets/1x/atlasvoucher.png new file mode 100644 index 0000000..dafa9a6 Binary files /dev/null and b/Cryptid/assets/1x/atlasvoucher.png differ diff --git a/Cryptid/assets/1x/b_cry_glowing.png b/Cryptid/assets/1x/b_cry_glowing.png new file mode 100644 index 0000000..7ca350c Binary files /dev/null and b/Cryptid/assets/1x/b_cry_glowing.png differ diff --git a/Cryptid/assets/1x/bl_cry.png b/Cryptid/assets/1x/bl_cry.png new file mode 100644 index 0000000..3fb6a3a Binary files /dev/null and b/Cryptid/assets/1x/bl_cry.png differ diff --git a/Cryptid/assets/1x/bl_nostalgia.png b/Cryptid/assets/1x/bl_nostalgia.png new file mode 100644 index 0000000..b427462 Binary files /dev/null and b/Cryptid/assets/1x/bl_nostalgia.png differ diff --git a/Cryptid/assets/1x/c_cry_code.png b/Cryptid/assets/1x/c_cry_code.png new file mode 100644 index 0000000..d560e95 Binary files /dev/null and b/Cryptid/assets/1x/c_cry_code.png differ diff --git a/Cryptid/assets/1x/cry_achievements.png b/Cryptid/assets/1x/cry_achievements.png new file mode 100644 index 0000000..929a4d6 Binary files /dev/null and b/Cryptid/assets/1x/cry_achievements.png differ diff --git a/Cryptid/assets/1x/cry_icon.png b/Cryptid/assets/1x/cry_icon.png new file mode 100644 index 0000000..12fe5a9 Binary files /dev/null and b/Cryptid/assets/1x/cry_icon.png differ diff --git a/Cryptid/assets/1x/cry_misc.png b/Cryptid/assets/1x/cry_misc.png new file mode 100644 index 0000000..c3680ee Binary files /dev/null and b/Cryptid/assets/1x/cry_misc.png differ diff --git a/Cryptid/assets/1x/goofy.png b/Cryptid/assets/1x/goofy.png new file mode 100644 index 0000000..f121446 Binary files /dev/null and b/Cryptid/assets/1x/goofy.png differ diff --git a/Cryptid/assets/1x/j_cry_jimball.png b/Cryptid/assets/1x/j_cry_jimball.png new file mode 100644 index 0000000..92169c7 Binary files /dev/null and b/Cryptid/assets/1x/j_cry_jimball.png differ diff --git a/Cryptid/assets/1x/jolly.png b/Cryptid/assets/1x/jolly.png new file mode 100644 index 0000000..3a81c46 Binary files /dev/null and b/Cryptid/assets/1x/jolly.png differ diff --git a/Cryptid/assets/1x/pack_cry.png b/Cryptid/assets/1x/pack_cry.png new file mode 100644 index 0000000..da9157d Binary files /dev/null and b/Cryptid/assets/1x/pack_cry.png differ diff --git a/Cryptid/assets/1x/placeholders.png b/Cryptid/assets/1x/placeholders.png new file mode 100644 index 0000000..16282ed Binary files /dev/null and b/Cryptid/assets/1x/placeholders.png differ diff --git a/Cryptid/assets/1x/resize.py b/Cryptid/assets/1x/resize.py new file mode 100644 index 0000000..180641d --- /dev/null +++ b/Cryptid/assets/1x/resize.py @@ -0,0 +1,25 @@ +import sys +from PIL import Image +import os +import time + +def upscale_pixel_art(input_image, output_directory, input_image_path): + # Double the size + new_size = (input_image.width * 2, input_image.height * 2) + resized_image = input_image.resize(new_size, Image.NEAREST) # NEAREST resampling preserves pixelation + + # Save the resized image + filename = os.path.basename(input_image_path) + output_image_path = os.path.join(output_directory, filename) + resized_image.save(output_image_path) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: py resize.py ") + sys.exit(1) + + input_image_path = sys.argv[1] + input_image = Image.open(input_image_path) + + output_directory = "../2x/" + upscale_pixel_art(input_image, output_directory, input_image_path) diff --git a/Cryptid/assets/1x/stake_cry.png b/Cryptid/assets/1x/stake_cry.png new file mode 100644 index 0000000..abf6de8 Binary files /dev/null and b/Cryptid/assets/1x/stake_cry.png differ diff --git a/Cryptid/assets/1x/sticker_cry.png b/Cryptid/assets/1x/sticker_cry.png new file mode 100644 index 0000000..07f9d45 Binary files /dev/null and b/Cryptid/assets/1x/sticker_cry.png differ diff --git a/Cryptid/assets/1x/tag_cry.png b/Cryptid/assets/1x/tag_cry.png new file mode 100644 index 0000000..86589d7 Binary files /dev/null and b/Cryptid/assets/1x/tag_cry.png differ diff --git a/Cryptid/assets/2x/atlasSleeves.png b/Cryptid/assets/2x/atlasSleeves.png new file mode 100644 index 0000000..4087301 Binary files /dev/null and b/Cryptid/assets/2x/atlasSleeves.png differ diff --git a/Cryptid/assets/2x/atlasdeck.png b/Cryptid/assets/2x/atlasdeck.png new file mode 100644 index 0000000..b1c8c7e Binary files /dev/null and b/Cryptid/assets/2x/atlasdeck.png differ diff --git a/Cryptid/assets/2x/atlasepic.png b/Cryptid/assets/2x/atlasepic.png new file mode 100644 index 0000000..205699e Binary files /dev/null and b/Cryptid/assets/2x/atlasepic.png differ diff --git a/Cryptid/assets/2x/atlasexotic.png b/Cryptid/assets/2x/atlasexotic.png new file mode 100644 index 0000000..2df7a33 Binary files /dev/null and b/Cryptid/assets/2x/atlasexotic.png differ diff --git a/Cryptid/assets/2x/atlasnotjokers.png b/Cryptid/assets/2x/atlasnotjokers.png new file mode 100644 index 0000000..e573310 Binary files /dev/null and b/Cryptid/assets/2x/atlasnotjokers.png differ diff --git a/Cryptid/assets/2x/atlasone.png b/Cryptid/assets/2x/atlasone.png new file mode 100644 index 0000000..a538f88 Binary files /dev/null and b/Cryptid/assets/2x/atlasone.png differ diff --git a/Cryptid/assets/2x/atlasspooky.png b/Cryptid/assets/2x/atlasspooky.png new file mode 100644 index 0000000..899ef58 Binary files /dev/null and b/Cryptid/assets/2x/atlasspooky.png differ diff --git a/Cryptid/assets/2x/atlasthree.png b/Cryptid/assets/2x/atlasthree.png new file mode 100644 index 0000000..e9f54ac Binary files /dev/null and b/Cryptid/assets/2x/atlasthree.png differ diff --git a/Cryptid/assets/2x/atlastwo.png b/Cryptid/assets/2x/atlastwo.png new file mode 100644 index 0000000..8bfbfc6 Binary files /dev/null and b/Cryptid/assets/2x/atlastwo.png differ diff --git a/Cryptid/assets/2x/atlasvoucher.png b/Cryptid/assets/2x/atlasvoucher.png new file mode 100644 index 0000000..5f99e8c Binary files /dev/null and b/Cryptid/assets/2x/atlasvoucher.png differ diff --git a/Cryptid/assets/2x/b_cry_glowing.png b/Cryptid/assets/2x/b_cry_glowing.png new file mode 100644 index 0000000..b27b73a Binary files /dev/null and b/Cryptid/assets/2x/b_cry_glowing.png differ diff --git a/Cryptid/assets/2x/bl_cry.png b/Cryptid/assets/2x/bl_cry.png new file mode 100644 index 0000000..d179068 Binary files /dev/null and b/Cryptid/assets/2x/bl_cry.png differ diff --git a/Cryptid/assets/2x/bl_nostalgia.png b/Cryptid/assets/2x/bl_nostalgia.png new file mode 100644 index 0000000..d66cb2b Binary files /dev/null and b/Cryptid/assets/2x/bl_nostalgia.png differ diff --git a/Cryptid/assets/2x/c_cry_code.png b/Cryptid/assets/2x/c_cry_code.png new file mode 100644 index 0000000..9f47d09 Binary files /dev/null and b/Cryptid/assets/2x/c_cry_code.png differ diff --git a/Cryptid/assets/2x/cry_achievements.png b/Cryptid/assets/2x/cry_achievements.png new file mode 100644 index 0000000..244dc2e Binary files /dev/null and b/Cryptid/assets/2x/cry_achievements.png differ diff --git a/Cryptid/assets/2x/cry_icon.png b/Cryptid/assets/2x/cry_icon.png new file mode 100644 index 0000000..4795b9a Binary files /dev/null and b/Cryptid/assets/2x/cry_icon.png differ diff --git a/Cryptid/assets/2x/cry_misc.png b/Cryptid/assets/2x/cry_misc.png new file mode 100644 index 0000000..f139154 Binary files /dev/null and b/Cryptid/assets/2x/cry_misc.png differ diff --git a/Cryptid/assets/2x/goofy.png b/Cryptid/assets/2x/goofy.png new file mode 100644 index 0000000..220317f Binary files /dev/null and b/Cryptid/assets/2x/goofy.png differ diff --git a/Cryptid/assets/2x/j_cry_jimball.png b/Cryptid/assets/2x/j_cry_jimball.png new file mode 100644 index 0000000..1eb061b Binary files /dev/null and b/Cryptid/assets/2x/j_cry_jimball.png differ diff --git a/Cryptid/assets/2x/jolly.png b/Cryptid/assets/2x/jolly.png new file mode 100644 index 0000000..3f7965a Binary files /dev/null and b/Cryptid/assets/2x/jolly.png differ diff --git a/Cryptid/assets/2x/pack_cry.png b/Cryptid/assets/2x/pack_cry.png new file mode 100644 index 0000000..a36a50d Binary files /dev/null and b/Cryptid/assets/2x/pack_cry.png differ diff --git a/Cryptid/assets/2x/placeholders.png b/Cryptid/assets/2x/placeholders.png new file mode 100644 index 0000000..c5577e7 Binary files /dev/null and b/Cryptid/assets/2x/placeholders.png differ diff --git a/Cryptid/assets/2x/resize.py b/Cryptid/assets/2x/resize.py new file mode 100644 index 0000000..f1fa03e --- /dev/null +++ b/Cryptid/assets/2x/resize.py @@ -0,0 +1,25 @@ +import sys +from PIL import Image +import os +import time + +def upscale_pixel_art(input_image, output_directory, input_image_path): + # Double the size + new_size = (int(input_image.width * 0.5), int(input_image.height * 0.5)) + resized_image = input_image.resize(new_size, Image.NEAREST) # NEAREST resampling preserves pixelation + + # Save the resized image + filename = os.path.basename(input_image_path) + output_image_path = os.path.join(output_directory, filename) + resized_image.save(output_image_path) + +if __name__ == "__main__": + if len(sys.argv) < 2: + print("Usage: py resize.py ") + sys.exit(1) + + input_image_path = sys.argv[1] + input_image = Image.open(input_image_path) + + output_directory = "../1x/" + upscale_pixel_art(input_image, output_directory, input_image_path) diff --git a/Cryptid/assets/2x/stake_cry.png b/Cryptid/assets/2x/stake_cry.png new file mode 100644 index 0000000..38ff8b9 Binary files /dev/null and b/Cryptid/assets/2x/stake_cry.png differ diff --git a/Cryptid/assets/2x/sticker_cry.png b/Cryptid/assets/2x/sticker_cry.png new file mode 100644 index 0000000..a52de86 Binary files /dev/null and b/Cryptid/assets/2x/sticker_cry.png differ diff --git a/Cryptid/assets/2x/tag_cry.png b/Cryptid/assets/2x/tag_cry.png new file mode 100644 index 0000000..1730544 Binary files /dev/null and b/Cryptid/assets/2x/tag_cry.png differ diff --git a/Cryptid/assets/shaders/astral.fs b/Cryptid/assets/shaders/astral.fs new file mode 100644 index 0000000..8229603 --- /dev/null +++ b/Cryptid/assets/shaders/astral.fs @@ -0,0 +1,151 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define MY_HIGHP_OR_MEDIUMP highp +#else + #define MY_HIGHP_OR_MEDIUMP mediump +#endif + +extern MY_HIGHP_OR_MEDIUMP vec2 astral; +extern MY_HIGHP_OR_MEDIUMP number dissolve; +extern MY_HIGHP_OR_MEDIUMP number time; +extern MY_HIGHP_OR_MEDIUMP vec4 texture_details; +extern MY_HIGHP_OR_MEDIUMP vec2 image_details; +extern bool shadow; +extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_1; +extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_2; + +vec4 dissolve_mask(vec4 tex, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, shadow ? tex.a*0.3: tex.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (tex.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + tex.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + tex.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, res > adjusted_dissolve ? (shadow ? tex.a*0.3: tex.a) : .0); +} + +number hue(number s, number t, number h) +{ + number hs = mod(h, 1.)*6.; + if (hs < 1.) return (t-s) * hs + s; + if (hs < 3.) return t; + if (hs < 4.) return (t-s) * (4.-hs) + s; + return s; +} + +vec4 RGB(vec4 c) +{ + if (c.y < 0.0001) + return vec4(vec3(c.z), c.a); + + number t = (c.z < .5) ? c.y*c.z + c.z : -c.y*c.z + (c.y+c.z); + number s = 2.0 * c.z - t; + return vec4(hue(s,t,c.x + 1./3.), hue(s,t,c.x), hue(s,t,c.x - 1./3.), c.w); +} + +vec4 HSL(vec4 c) +{ + number low = min(c.r, min(c.g, c.b)); + number high = max(c.r, max(c.g, c.b)); + number delta = high - low; + number sum = high+low; + + vec4 hsl = vec4(.0, .0, .5 * sum, c.a); + if (delta == .0) + return hsl; + + hsl.y = (hsl.z < .5) ? delta / sum : delta / (2.0 - sum); + + if (high == c.r) + hsl.x = (c.g - c.b) / delta; + else if (high == c.g) + hsl.x = (c.b - c.r) / delta + 2.0; + else + hsl.x = (c.r - c.g) / delta + 4.0; + + hsl.x = mod(hsl.x / 6., 1.); + return hsl; +} + +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + vec4 tex = Texel(texture, texture_coords); + vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.ba)/texture_details.ba; + + number low = min(tex.r, min(tex.g, tex.b)); + number high = max(tex.r, max(tex.g, tex.b)); + number delta = high - low; + + number saturation_fac = 1. - max(0., 0.05*(1.1-delta)); + + vec4 hsl = HSL(vec4(tex.r*saturation_fac, tex.g*saturation_fac, tex.b, tex.a)); + + float t = astral.y*2.221 + mod(time,1.); + vec2 floored_uv = (floor((uv*texture_details.ba)))/texture_details.ba; + vec2 uv_scaled_centered = (floored_uv - 0.5) * 50.; + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + + float res = (.5 + .5* cos( (astral.x) * 2.612 + ( field + -.5 ) *3.14)); + hsl.x = .8; + hsl.y = hsl.y * 0.8; + hsl.z = hsl.z * 0.2 + 0.6 * sin(hsl.z/2.5 - res/4. + sin(astral.y)/8. + 0.5)/1.4; + + tex.rgb = RGB(hsl).rgb; + + if (tex[3] < 0.7) + tex[3] = tex[3]/3.; + return dissolve_mask(tex*colour, texture_coords, uv); +} + +extern MY_HIGHP_OR_MEDIUMP vec2 mouse_screen_pos; +extern MY_HIGHP_OR_MEDIUMP float hovering; +extern MY_HIGHP_OR_MEDIUMP float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif \ No newline at end of file diff --git a/Cryptid/assets/shaders/blur.fs b/Cryptid/assets/shaders/blur.fs new file mode 100644 index 0000000..551ab5b --- /dev/null +++ b/Cryptid/assets/shaders/blur.fs @@ -0,0 +1,184 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define PRECISION highp +#else + #define PRECISION mediump +#endif + +// Card rotation +extern PRECISION vec2 blur; + +extern PRECISION number dissolve; +extern PRECISION number time; +extern PRECISION vec4 texture_details; +extern PRECISION vec2 image_details; +extern bool shadow; +extern PRECISION vec4 burn_colour_1; +extern PRECISION vec4 burn_colour_2; + +// extern PRECISION number blur_radius; + +vec4 RGB(vec4 c); + +vec4 HSL(vec4 c); + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv); + +// texture_coords = coolds of a original_pixel within the atlas texture +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + // GAUSSIAN BLUR SETTINGS {{{ + float direction = 16.0; // BLUR DIRECTIONS (Default 16.0 - More is better but slower) + float quality = 6.0; // BLUR QUALITY (Default 4.0 - More is better but slower) + float size = 4.0; // BLUR SIZE (radius) + // GAUSSIAN BLUR SETTINGS }}} + + vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.ba)/texture_details.ba; + + float two_pi = 6.28318530718; + + vec2 radius = size/image_details.xy; + + vec4 original_pixel = Texel(texture, texture_coords); + vec4 blurred_pixel = Texel(texture, texture_coords); + + // Blur calculations + float d_step = two_pi / direction; + float i_step = 1.0 / quality; + + for (float d = 0.0; d < two_pi; d += d_step) { + for (float i = i_step; i < 1.001; i+=i_step) { + blurred_pixel += Texel( texture, texture_coords + vec2(cos(d), sin(d)) * radius * i); + } + } + + // Final processing + blurred_pixel /= quality * direction + 1.; + vec4 final_pixel = vec4(blurred_pixel.rgb, original_pixel.a); + + vec4 hsl = HSL(final_pixel); + + // Adjust lightness for a glossy effect + + float t = blur.y*2.221 + mod(time,1.); + vec2 floored_uv = (floor((uv*texture_details.ba)))/texture_details.ba; + vec2 uv_scaled_centered = (floored_uv - 0.5) * 50.; + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + + float res = (.5 + .5* cos( (blur.x) * 2.612 + ( field + -.5 ) *3.14)); + // Mostly use original lightness, with slight change from moving the card & with time + hsl.z = 0.7*hsl.z + sin(hsl.z/2.5 - res/4. + sin(blur.y)/8. + 0.5)/3.; + + final_pixel = RGB(hsl); + + if (final_pixel[3] < 0.7) + final_pixel[3] = final_pixel[3]/3.; + return dissolve_mask(final_pixel * colour, texture_coords, uv); +} + + +number hue(number s, number t, number h) +{ + number hs = mod(h, 1.)*6.; + if (hs < 1.) return (t-s) * hs + s; + if (hs < 3.) return t; + if (hs < 4.) return (t-s) * (4.-hs) + s; + return s; +} + +vec4 RGB(vec4 c) +{ + if (c.y < 0.0001) + return vec4(vec3(c.z), c.a); + + number t = (c.z < .5) ? c.y*c.z + c.z : -c.y*c.z + (c.y+c.z); + number s = 2.0 * c.z - t; + return vec4(hue(s,t,c.x + 1./3.), hue(s,t,c.x), hue(s,t,c.x - 1./3.), c.w); +} + +vec4 HSL(vec4 c) +{ + number low = min(c.r, min(c.g, c.b)); + number high = max(c.r, max(c.g, c.b)); + number delta = high - low; + number sum = high+low; + + vec4 hsl = vec4(.0, .0, .5 * sum, c.a); + if (delta == .0) + return hsl; + + hsl.y = (hsl.z < .5) ? delta / sum : delta / (2.0 - sum); + + if (high == c.r) + hsl.x = (c.g - c.b) / delta; + else if (high == c.g) + hsl.x = (c.b - c.r) / delta + 2.0; + else + hsl.x = (c.r - c.g) / delta + 4.0; + + hsl.x = mod(hsl.x / 6., 1.); + return hsl; +} + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, shadow ? final_pixel.a*0.3: final_pixel.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (final_pixel.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + final_pixel.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + final_pixel.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, res > adjusted_dissolve ? (shadow ? final_pixel.a*0.3: final_pixel.a) : .0); +} + +extern PRECISION vec2 mouse_screen_pos; +extern PRECISION float hovering; +extern PRECISION float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif \ No newline at end of file diff --git a/Cryptid/assets/shaders/glass.fs b/Cryptid/assets/shaders/glass.fs new file mode 100644 index 0000000..d7d3a3d --- /dev/null +++ b/Cryptid/assets/shaders/glass.fs @@ -0,0 +1,109 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define PRECISION highp +#else + #define PRECISION mediump +#endif + +extern PRECISION vec2 glass; + +extern PRECISION number dissolve; +extern PRECISION number time; +extern PRECISION vec4 texture_details; +extern PRECISION vec2 image_details; +extern bool shadow; +extern PRECISION vec4 burn_colour_1; +extern PRECISION vec4 burn_colour_2; + +extern PRECISION float lines_offset; + +#define TWO_PI 6.28318530718 + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv); + +bool line(vec2 uv, float offset, float width) { + uv.x = uv.x * texture_details.z / texture_details.w; + + offset = offset + 0.35 * sin(glass.x + TWO_PI * lines_offset); + width = width + 0.005 * sin(glass.x); + + float min_y = -uv.x + offset; + float max_y = -uv.x + offset + width; + + return uv.y > min_y && uv.y < max_y; +} + +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.zw)/texture_details.zw; + vec4 pixel = Texel(texture, texture_coords); + + vec4 tex = vec4(.3, .8, 1., 0.2); + + if ( + lines_offset > 0. && (line(uv, 0.4, 0.1) || line(uv, 0.55, 0.1) || line(uv, 1.3, 0.05) || line(uv, 1.8, 0.1) || line(uv, 0.01, 0.07)) || + lines_offset <= 0. && (line(uv, -0.1, 0.05) || line(uv, 0.1, 0.12) || line(uv, 0.8, 0.1) || line(uv, 1.3, 0.08) || line(uv, 1.7, 0.05)) + ) { + tex.a = tex.a * 2.; + } + + pixel = vec4(pixel.rgb * 0.66 + tex.rgb * tex.a, pixel.a * 0.66); + + return dissolve_mask(pixel, texture_coords, uv); +} + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, shadow ? final_pixel.a*0.3: final_pixel.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (final_pixel.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + final_pixel.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + final_pixel.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, res > adjusted_dissolve ? (shadow ? final_pixel.a*0.3: final_pixel.a) : .0); +} + +extern PRECISION vec2 mouse_screen_pos; +extern PRECISION float hovering; +extern PRECISION float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif \ No newline at end of file diff --git a/Cryptid/assets/shaders/glitched.fs b/Cryptid/assets/shaders/glitched.fs new file mode 100644 index 0000000..a0de150 --- /dev/null +++ b/Cryptid/assets/shaders/glitched.fs @@ -0,0 +1,144 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define MY_HIGHP_OR_MEDIUMP highp +#else + #define MY_HIGHP_OR_MEDIUMP mediump +#endif + +extern MY_HIGHP_OR_MEDIUMP vec2 glitched; +extern MY_HIGHP_OR_MEDIUMP number dissolve; +extern MY_HIGHP_OR_MEDIUMP number time; +extern MY_HIGHP_OR_MEDIUMP vec4 texture_details; +extern MY_HIGHP_OR_MEDIUMP vec2 image_details; +extern bool shadow; +extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_1; +extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_2; + +vec4 dissolve_mask(vec4 tex, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, shadow ? tex.a*0.3: tex.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (tex.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + tex.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + tex.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, res > adjusted_dissolve ? (shadow ? tex.a*0.3: tex.a) : .0); +} + +number hue(number s, number t, number h) +{ + number hs = mod(h, 1.)*6.; + if (hs < 1.) return (t-s) * hs + s; + if (hs < 3.) return t; + if (hs < 4.) return (t-s) * (4.-hs) + s; + return s; +} + +vec4 RGB(vec4 c) +{ + if (c.y < 0.0001) + return vec4(vec3(c.z), c.a); + + number t = (c.z < .5) ? c.y*c.z + c.z : -c.y*c.z + (c.y+c.z); + number s = 2.0 * c.z - t; + return vec4(hue(s,t,c.x + 1./3.), hue(s,t,c.x), hue(s,t,c.x - 1./3.), c.w); +} + +vec4 HSL(vec4 c) +{ + number low = min(c.r, min(c.g, c.b)); + number high = max(c.r, max(c.g, c.b)); + number delta = high - low; + number sum = high+low; + + vec4 hsl = vec4(.0, .0, .5 * sum, c.a); + if (delta == .0) + return hsl; + + hsl.y = (hsl.z < .5) ? delta / sum : delta / (2.0 - sum); + + if (high == c.r) + hsl.x = (c.g - c.b) / delta; + else if (high == c.g) + hsl.x = (c.b - c.r) / delta + 2.0; + else + hsl.x = (c.r - c.g) / delta + 4.0; + + hsl.x = mod(hsl.x / 6., 1.); + return hsl; +} + +//Shader by Samario +float rand(vec2 co){ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.ba)/texture_details.ba; + + float POLY_THROWAWAY = (.5 + .5* cos( (glitched.x) * 2.612 + ( 0.2 + -.5 ) *3.14)); + float POLY_THROWAWAY_2 = POLY_THROWAWAY + glitched.y * 0.04; + + vec2 texCoordsR = texture_coords; + vec2 texCoordsG = texture_coords; + vec2 texCoordsB = texture_coords; + + float iTime = tan(2. * time); + + texCoordsR.x += (0.004 * rand(vec2(iTime, uv.y))) - 0.002 + (POLY_THROWAWAY * 0.0000001); + texCoordsG.x += (0.007 * rand(vec2(iTime*2., uv.y*0.9))) - 0.0035 + (POLY_THROWAWAY * 0.0000001); + texCoordsB.x += (0.010 * rand(vec2(iTime*3., uv.y*0.8))) - 0.005 + (POLY_THROWAWAY_2 * 0.0000001); + + vec4 texR = Texel(texture, texCoordsR); + vec4 texG = Texel(texture, texCoordsG); + vec4 texB = Texel(texture, texCoordsB); + + vec4 tex = vec4(texR.r, texG.g, texB.b, texR.a); + + return dissolve_mask(tex*colour, texture_coords, uv); +} + +extern MY_HIGHP_OR_MEDIUMP vec2 mouse_screen_pos; +extern MY_HIGHP_OR_MEDIUMP float hovering; +extern MY_HIGHP_OR_MEDIUMP float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif \ No newline at end of file diff --git a/Cryptid/assets/shaders/gold.fs b/Cryptid/assets/shaders/gold.fs new file mode 100644 index 0000000..cb3d4b2 --- /dev/null +++ b/Cryptid/assets/shaders/gold.fs @@ -0,0 +1,114 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define PRECISION highp +#else + #define PRECISION mediump +#endif + +extern PRECISION vec2 gold; + +extern PRECISION number dissolve; +extern PRECISION number time; +extern PRECISION vec4 texture_details; +extern PRECISION vec2 image_details; +extern bool shadow; +extern PRECISION vec4 burn_colour_1; +extern PRECISION vec4 burn_colour_2; + +extern PRECISION float lines_offset; + +#define TWO_PI 6.28318530718 + +vec4 gold_color = vec4(231., 164., 25., 0.) / 255.; + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv); + +bool line(vec2 uv, float offset, float width) { + uv.x = uv.x * texture_details.z / texture_details.w; + + offset = offset + 0.35 * sin(gold.x + TWO_PI * lines_offset); + width = width + 0.005 * sin(gold.x); + + float min_y = -uv.x + offset; + float max_y = -uv.x + offset + width; + + return uv.y > min_y && uv.y < max_y; +} + +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.zw)/texture_details.zw; + vec4 pixel = Texel(texture, texture_coords); + + vec4 tex = vec4(1., 1., 1., 0.1); + + if ( + lines_offset > 0. && (line(uv, 0.0, 0.07) || line(uv, 0.4, 0.1) || line(uv, 0.55, 0.1) || line(uv, 1.3, 0.05) || line(uv, 1.8, 0.1)) || + lines_offset <= 0. && (line(uv, -0.2, 0.13) || line(uv, 0.3, 0.05) || line(uv, 0.8, 0.1) || line(uv, 1.3, 0.11) || line(uv, 1.7, 0.07)) + ) { + tex.a = tex.a * 2.; + } else { + tex.a = 0.05; + } + + float avg = (pixel.r + pixel.g + pixel.b) / 3.; + pixel = vec4(gold_color.rgb * avg + tex.rgb * tex.a, pixel.a); + + return dissolve_mask(pixel, texture_coords, uv); +} + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, shadow ? final_pixel.a*0.3: final_pixel.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (final_pixel.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + final_pixel.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + final_pixel.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, res > adjusted_dissolve ? (shadow ? final_pixel.a*0.3: final_pixel.a) : .0); +} + +extern PRECISION vec2 mouse_screen_pos; +extern PRECISION float hovering; +extern PRECISION float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif \ No newline at end of file diff --git a/Cryptid/assets/shaders/m.fs b/Cryptid/assets/shaders/m.fs new file mode 100644 index 0000000..40199a7 --- /dev/null +++ b/Cryptid/assets/shaders/m.fs @@ -0,0 +1,135 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define PRECISION highp +#else + #define PRECISION mediump +#endif + +extern PRECISION vec2 m; + +extern PRECISION number dissolve; +extern PRECISION number time; +extern PRECISION vec4 texture_details; +extern PRECISION vec2 image_details; +extern bool shadow; +extern PRECISION vec4 burn_colour_1; +extern PRECISION vec4 burn_colour_2; + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv); + +float sdParabola(vec2 pos, float wi, float he) +{ + pos.x = abs(pos.x); + float ik = wi*wi/he; + float p = ik*(he-pos.y-0.5*ik)/3.0; + float q = pos.x*ik*ik*0.25; + float h = q*q - p*p*p; + float r = sqrt(abs(h)); + float x = (h>0.0) ? + pow(q+r,1.0/3.0) - pow(abs(q-r),1.0/3.0)*sign(r-q) : + 2.0*cos(atan(r/q)/3.0)*sqrt(p); + x = min(x,wi); + return length(pos-vec2(x,he-x*x/ik)) * + sign(ik*(pos.y-he)+pos.x*pos.x); +} + +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + vec2 uv = (((texture_coords) * (image_details)) - texture_details.xy * texture_details.zw) / texture_details.zw; + vec2 origin_uv = uv.xy; + vec4 pixel = Texel(texture, texture_coords); + + // Flip the y-coordinate + uv.y = 1.0 - uv.y; + + // Distort the x-coordinate + if (uv.x > 0.5) { + if (uv.x > 0.75) { + uv.x = 0.1 - uv.x; + } else { + uv.x = 1.4 - uv.x; + } + } else { + if (uv.x > 0.25) { + uv.x = 0.4 + uv.x; + } else if (uv.x > 0.0) { + uv.x = 0.9 - uv.x; + } + } + + uv.y = uv.y * 0.58 + m.x * 0.000000000001; + + // Compute opacity based on a parabola shape + float opacity = sdParabola(uv, 0.53, 0.55); + if (!(opacity < 0.4 && opacity > 0.3)) { + opacity = 0.0; + } else { + opacity = 1.0; + } + + opacity = min(pixel.a, opacity); + + // Darken the original texture by reducing its brightness + vec4 darkened_pixel = pixel * 0.5; // Adjust the factor (0.5) to control the darkness + + // Blend the darkened texture with the outline + vec4 final_color = mix(darkened_pixel, vec4(pixel.rgb, opacity), opacity); + + return dissolve_mask(final_color, texture_coords, origin_uv); +} + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, shadow ? final_pixel.a*0.3: final_pixel.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (final_pixel.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + final_pixel.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + final_pixel.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, res > adjusted_dissolve ? (shadow ? final_pixel.a*0.3: final_pixel.a) : .0); +} + +extern PRECISION vec2 mouse_screen_pos; +extern PRECISION float hovering; +extern PRECISION float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif \ No newline at end of file diff --git a/Cryptid/assets/shaders/mosaic.fs b/Cryptid/assets/shaders/mosaic.fs new file mode 100644 index 0000000..fd4cf65 --- /dev/null +++ b/Cryptid/assets/shaders/mosaic.fs @@ -0,0 +1,150 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define MY_HIGHP_OR_MEDIUMP highp +#else + #define MY_HIGHP_OR_MEDIUMP mediump +#endif + +extern MY_HIGHP_OR_MEDIUMP vec2 mosaic; +extern MY_HIGHP_OR_MEDIUMP number dissolve; +extern MY_HIGHP_OR_MEDIUMP number time; +extern MY_HIGHP_OR_MEDIUMP vec4 texture_details; +extern MY_HIGHP_OR_MEDIUMP vec2 image_details; +extern bool shadow; +extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_1; +extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_2; + +vec4 dissolve_mask(vec4 tex, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, shadow ? tex.a*0.3: tex.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (tex.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + tex.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + tex.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, res > adjusted_dissolve ? (shadow ? tex.a*0.3: tex.a) : .0); +} + +number hue(number s, number t, number h) +{ + number hs = mod(h, 1.)*6.; + if (hs < 1.) return (t-s) * hs + s; + if (hs < 3.) return t; + if (hs < 4.) return (t-s) * (4.-hs) + s; + return s; +} + +vec4 RGB(vec4 c) +{ + if (c.y < 0.0001) + return vec4(vec3(c.z), c.a); + + number t = (c.z < .5) ? c.y*c.z + c.z : -c.y*c.z + (c.y+c.z); + number s = 2.0 * c.z - t; + return vec4(hue(s,t,c.x + 1./3.), hue(s,t,c.x), hue(s,t,c.x - 1./3.), c.w); +} + +vec4 HSL(vec4 c) +{ + number low = min(c.r, min(c.g, c.b)); + number high = max(c.r, max(c.g, c.b)); + number delta = high - low; + number sum = high+low; + + vec4 hsl = vec4(.0, .0, .5 * sum, c.a); + if (delta == .0) + return hsl; + + hsl.y = (hsl.z < .5) ? delta / sum : delta / (2.0 - sum); + + if (high == c.r) + hsl.x = (c.g - c.b) / delta; + else if (high == c.g) + hsl.x = (c.b - c.r) / delta + 2.0; + else + hsl.x = (c.r - c.g) / delta + 4.0; + + hsl.x = mod(hsl.x / 6., 1.); + return hsl; +} + +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + vec4 tex = Texel(texture, texture_coords); + vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.ba)/texture_details.ba; + vec4 hsl = HSL(0.5*tex + 0.5*vec4(0.,0.,1.,tex.a)); + + float t = mosaic.y*7.221 + time; + vec2 floored_uv = (floor((uv*texture_details.ba)))/texture_details.ba; + vec2 uv_scaled_centered = (floored_uv - 0.5) * 250.; + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + + float res = (.5 + .5* cos( (mosaic.x) * 2.612 + ( field + -.5 ) *3.14)); + + number low = min(tex.b, min(tex.g, tex.r)); + number high = max(tex.b, max(tex.g, tex.r)); + number delta = 0.2+0.3*(high- low) + 0.1*high; + + number gridsize = 0.6; + number fac = max(max(0., 7.*abs(cos(uv.x*gridsize*20.))-6.), max(0., 7.*cos(uv.y*gridsize*45.)-6.)); + + hsl.x = hsl.x + res + fac; + hsl.y = hsl.y*1.3; + hsl.z = hsl.z*0.6+0.4; + + tex =(1.-delta)*tex + delta*RGB(hsl)*vec4(0.6,0.5,1.4,tex.a); + + return dissolve_mask(tex*colour, texture_coords, uv); +} + +extern MY_HIGHP_OR_MEDIUMP vec2 mouse_screen_pos; +extern MY_HIGHP_OR_MEDIUMP float hovering; +extern MY_HIGHP_OR_MEDIUMP float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif \ No newline at end of file diff --git a/Cryptid/assets/shaders/noisy.fs b/Cryptid/assets/shaders/noisy.fs new file mode 100644 index 0000000..f7862f3 --- /dev/null +++ b/Cryptid/assets/shaders/noisy.fs @@ -0,0 +1,93 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define PRECISION highp +#else + #define PRECISION mediump +#endif + +// Pseudorandom number generator +// https://stackoverflow.com/a/34223787 + +float rand(vec2 co){ + return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453); +} + +extern PRECISION vec2 noisy; + +extern PRECISION number dissolve; +extern PRECISION number time; +extern PRECISION vec4 texture_details; +extern PRECISION vec2 image_details; +extern bool shadow; +extern PRECISION vec4 burn_colour_1; +extern PRECISION vec4 burn_colour_2; + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv); + +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.ba)/texture_details.ba; + vec4 pixel = Texel(texture, texture_coords); + + float random = rand(uv); + + if (pixel.a > 0.) pixel.a += 0.1*sin(noisy.x) - 0.51; + + return dissolve_mask(vec4(pixel.rgb * random, pixel.a), texture_coords, uv); +} + +vec4 dissolve_mask(vec4 final_pixel, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, shadow ? final_pixel.a*0.3: final_pixel.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (final_pixel.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + final_pixel.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + final_pixel.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : final_pixel.xyz, res > adjusted_dissolve ? (shadow ? final_pixel.a*0.3: final_pixel.a) : .0); +} + +extern PRECISION vec2 mouse_screen_pos; +extern PRECISION float hovering; +extern PRECISION float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif \ No newline at end of file diff --git a/Cryptid/assets/shaders/oversat.fs b/Cryptid/assets/shaders/oversat.fs new file mode 100644 index 0000000..a4fe9e3 --- /dev/null +++ b/Cryptid/assets/shaders/oversat.fs @@ -0,0 +1,227 @@ +#if defined(VERTEX) || __VERSION__ > 100 || defined(GL_FRAGMENT_PRECISION_HIGH) + #define MY_HIGHP_OR_MEDIUMP highp +#else + #define MY_HIGHP_OR_MEDIUMP mediump +#endif + +extern MY_HIGHP_OR_MEDIUMP vec2 oversat; +extern MY_HIGHP_OR_MEDIUMP number dissolve; +extern MY_HIGHP_OR_MEDIUMP number time; +extern MY_HIGHP_OR_MEDIUMP vec4 texture_details; +extern MY_HIGHP_OR_MEDIUMP vec2 image_details; +extern bool shadow; +extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_1; +extern MY_HIGHP_OR_MEDIUMP vec4 burn_colour_2; + +vec4 dissolve_mask(vec4 tex, vec2 texture_coords, vec2 uv) +{ + if (dissolve < 0.001) { + return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, shadow ? tex.a*0.3: tex.a); + } + + float adjusted_dissolve = (dissolve*dissolve*(3.-2.*dissolve))*1.02 - 0.01; //Adjusting 0.0-1.0 to fall to -0.1 - 1.1 scale so the mask does not pause at extreme values + + float t = time * 10.0 + 2003.; + vec2 floored_uv = (floor((uv*texture_details.ba)))/max(texture_details.b, texture_details.a); + vec2 uv_scaled_centered = (floored_uv - 0.5) * 2.3 * max(texture_details.b, texture_details.a); + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + vec2 borders = vec2(0.2, 0.8); + + float res = (.5 + .5* cos( (adjusted_dissolve) / 82.612 + ( field + -.5 ) *3.14)) + - (floored_uv.x > borders.y ? (floored_uv.x - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y > borders.y ? (floored_uv.y - borders.y)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.x < borders.x ? (borders.x - floored_uv.x)*(5. + 5.*dissolve) : 0.)*(dissolve) + - (floored_uv.y < borders.x ? (borders.x - floored_uv.y)*(5. + 5.*dissolve) : 0.)*(dissolve); + + if (tex.a > 0.01 && burn_colour_1.a > 0.01 && !shadow && res < adjusted_dissolve + 0.8*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + if (!shadow && res < adjusted_dissolve + 0.5*(0.5-abs(adjusted_dissolve-0.5)) && res > adjusted_dissolve) { + tex.rgba = burn_colour_1.rgba; + } else if (burn_colour_2.a > 0.01) { + tex.rgba = burn_colour_2.rgba; + } + } + + return vec4(shadow ? vec3(0.,0.,0.) : tex.xyz, res > adjusted_dissolve ? (shadow ? tex.a*0.3: tex.a) : .0); +} + +number hue(number s, number t, number h) +{ + number hs = mod(h, 1.)*6.; + if (hs < 1.) return (t-s) * hs + s; + if (hs < 3.) return t; + if (hs < 4.) return (t-s) * (4.-hs) + s; + return s; +} + +vec4 RGB(vec4 c) +{ + if (c.y < 0.0001) + return vec4(vec3(c.z), c.a); + + number t = (c.z < .5) ? c.y*c.z + c.z : -c.y*c.z + (c.y+c.z); + number s = 2.0 * c.z - t; + return vec4(hue(s,t,c.x + 1./3.), hue(s,t,c.x), hue(s,t,c.x - 1./3.), c.w); +} + +vec4 HSL(vec4 c) +{ + number low = min(c.r, min(c.g, c.b)); + number high = max(c.r, max(c.g, c.b)); + number delta = high - low; + number sum = high+low; + + vec4 hsl = vec4(.0, .0, .5 * sum, c.a); + if (delta == .0) + return hsl; + + hsl.y = (hsl.z < .5) ? delta / sum : delta / (2.0 - sum); + + if (high == c.r) + hsl.x = (c.g - c.b) / delta; + else if (high == c.g) + hsl.x = (c.b - c.r) / delta + 2.0; + else + hsl.x = (c.r - c.g) / delta + 4.0; + + hsl.x = mod(hsl.x / 6., 1.); + return hsl; +} + +vec4 RGBtoHSV(vec4 rgb) +{ + vec4 hsv; + float minVal = min(min(rgb.r, rgb.g), rgb.b); + float maxVal = max(max(rgb.r, rgb.g), rgb.b); + float delta = maxVal - minVal; + + // Value + hsv.z = maxVal; + + // Saturation + if (maxVal != 0.0) + hsv.y = delta / maxVal; + else { + // r = g = b = 0, s = 0, v is undefined + hsv.y = 0.0; + hsv.x = -1.0; + return hsv; + } + + // Hue + if (rgb.r == maxVal) + hsv.x = (rgb.g - rgb.b) / delta; // between yellow & magenta + else if (rgb.g == maxVal) + hsv.x = 2.0 + (rgb.b - rgb.r) / delta; // between cyan & yellow + else + hsv.x = 4.0 + (rgb.r - rgb.g) / delta; // between magenta & cyan + + hsv.x = hsv.x * (1.0 / 6.0); + if (hsv.x < 0.0) + hsv.x += 1.0; + + // Alpha + hsv.w = rgb.a; + + return hsv; +} + +vec4 HSVtoRGB(vec4 hsv) { + vec4 rgb; + + float h = hsv.x * 6.0; + float c = hsv.z * hsv.y; + float x = c * (1.0 - abs(mod(h, 2.0) - 1.0)); + float m = hsv.z - c; + + if (h < 1.0) { + rgb = vec4(c, x, 0.0, hsv.a); + } else if (h < 2.0) { + rgb = vec4(x, c, 0.0, hsv.a); + } else if (h < 3.0) { + rgb = vec4(0.0, c, x, hsv.a); + } else if (h < 4.0) { + rgb = vec4(0.0, x, c, hsv.a); + } else if (h < 5.0) { + rgb = vec4(x, 0.0, c, hsv.a); + } else { + rgb = vec4(c, 0.0, x, hsv.a); + } + + rgb.rgb += m; + + return rgb; +} +vec3 increaseContrast(vec3 color, float amount) +{ + // Convert from [0,1] to [-0.5,0.5] range + vec3 centered = color - 0.5; + + // Increase contrast + centered *= amount; + + // Convert back to [0,1] range and clamp + return clamp(centered + 0.5, 0.0, 1.0); +} +vec4 effect( vec4 colour, Image texture, vec2 texture_coords, vec2 screen_coords ) +{ + vec4 tex = Texel(texture, texture_coords); + vec2 uv = (((texture_coords)*(image_details)) - texture_details.xy*texture_details.ba)/texture_details.ba; + + // Dummy, doesn't do anything but at least it makes the shader useable + if (uv.x > uv.x * 2.){ + uv = oversat; + } + + float mod = oversat.r * 1.0; + + number low = min(tex.r, min(tex.g, tex.b)); + number high = max(tex.r, max(tex.g, tex.b)); + number delta = high - low; + + number saturation_fac = 1. - max(0., 0.05*(1.1-delta)); + + vec4 hsl = HSL(vec4(tex.r*saturation_fac, tex.g*saturation_fac, tex.b, tex.a)); + + float t = oversat.y*2.221 + time; + vec2 floored_uv = (floor((uv*texture_details.ba)))/texture_details.ba; + vec2 uv_scaled_centered = (floored_uv - 0.5) * 50.; + + vec2 field_part1 = uv_scaled_centered + 50.*vec2(sin(-t / 143.6340), cos(-t / 99.4324)); + vec2 field_part2 = uv_scaled_centered + 50.*vec2(cos( t / 53.1532), cos( t / 61.4532)); + vec2 field_part3 = uv_scaled_centered + 50.*vec2(sin(-t / 87.53218), sin(-t / 49.0000)); + + float field = (1.+ ( + cos(length(field_part1) / 19.483) + sin(length(field_part2) / 33.155) * cos(field_part2.y / 15.73) + + cos(length(field_part3) / 27.193) * sin(field_part3.x / 21.92) ))/2.; + + float res = (.5 + .5* cos( (oversat.x) * 2.612 + ( field + -.5 ) *3.14)); + vec4 textp = RGB(hsl); + tex.rgb = increaseContrast(textp.rgb,4.0); + return dissolve_mask(tex*colour, texture_coords, uv); +} + +extern MY_HIGHP_OR_MEDIUMP vec2 mouse_screen_pos; +extern MY_HIGHP_OR_MEDIUMP float hovering; +extern MY_HIGHP_OR_MEDIUMP float screen_scale; + +#ifdef VERTEX +vec4 position( mat4 transform_projection, vec4 vertex_position ) +{ + if (hovering <= 0.){ + return transform_projection * vertex_position; + } + float mid_dist = length(vertex_position.xy - 0.5*love_ScreenSize.xy)/length(love_ScreenSize.xy); + vec2 mouse_offset = (vertex_position.xy - mouse_screen_pos.xy)/screen_scale; + float scale = 0.2*(-0.03 - 0.3*max(0., 0.3-mid_dist)) + *hovering*(length(mouse_offset)*length(mouse_offset))/(2. -mid_dist); + + return transform_projection * vertex_position + vec4(0,0,0,scale); +} +#endif diff --git a/Cryptid/assets/sounds/e_blur.ogg b/Cryptid/assets/sounds/e_blur.ogg new file mode 100644 index 0000000..09804c2 Binary files /dev/null and b/Cryptid/assets/sounds/e_blur.ogg differ diff --git a/Cryptid/assets/sounds/e_double_sided.ogg b/Cryptid/assets/sounds/e_double_sided.ogg new file mode 100644 index 0000000..f7568c8 Binary files /dev/null and b/Cryptid/assets/sounds/e_double_sided.ogg differ diff --git a/Cryptid/assets/sounds/e_fragile.ogg b/Cryptid/assets/sounds/e_fragile.ogg new file mode 100644 index 0000000..2513021 Binary files /dev/null and b/Cryptid/assets/sounds/e_fragile.ogg differ diff --git a/Cryptid/assets/sounds/e_glitched.ogg b/Cryptid/assets/sounds/e_glitched.ogg new file mode 100644 index 0000000..1af7c1a Binary files /dev/null and b/Cryptid/assets/sounds/e_glitched.ogg differ diff --git a/Cryptid/assets/sounds/e_golden.ogg b/Cryptid/assets/sounds/e_golden.ogg new file mode 100644 index 0000000..29f8a02 Binary files /dev/null and b/Cryptid/assets/sounds/e_golden.ogg differ diff --git a/Cryptid/assets/sounds/e_jolly.ogg b/Cryptid/assets/sounds/e_jolly.ogg new file mode 100644 index 0000000..27c974e Binary files /dev/null and b/Cryptid/assets/sounds/e_jolly.ogg differ diff --git a/Cryptid/assets/sounds/e_mosaic.ogg b/Cryptid/assets/sounds/e_mosaic.ogg new file mode 100644 index 0000000..77dd85e Binary files /dev/null and b/Cryptid/assets/sounds/e_mosaic.ogg differ diff --git a/Cryptid/assets/sounds/e_noisy.ogg b/Cryptid/assets/sounds/e_noisy.ogg new file mode 100644 index 0000000..c4caceb Binary files /dev/null and b/Cryptid/assets/sounds/e_noisy.ogg differ diff --git a/Cryptid/assets/sounds/e_oversaturated.ogg b/Cryptid/assets/sounds/e_oversaturated.ogg new file mode 100644 index 0000000..73e0bf2 Binary files /dev/null and b/Cryptid/assets/sounds/e_oversaturated.ogg differ diff --git a/Cryptid/assets/sounds/meow1.ogg b/Cryptid/assets/sounds/meow1.ogg new file mode 100644 index 0000000..a2ab09e Binary files /dev/null and b/Cryptid/assets/sounds/meow1.ogg differ diff --git a/Cryptid/assets/sounds/meow2.ogg b/Cryptid/assets/sounds/meow2.ogg new file mode 100644 index 0000000..2f192fa Binary files /dev/null and b/Cryptid/assets/sounds/meow2.ogg differ diff --git a/Cryptid/assets/sounds/meow3.ogg b/Cryptid/assets/sounds/meow3.ogg new file mode 100644 index 0000000..358ed30 Binary files /dev/null and b/Cryptid/assets/sounds/meow3.ogg differ diff --git a/Cryptid/assets/sounds/meow4.ogg b/Cryptid/assets/sounds/meow4.ogg new file mode 100644 index 0000000..9852c87 Binary files /dev/null and b/Cryptid/assets/sounds/meow4.ogg differ diff --git a/Cryptid/assets/sounds/music_big.ogg b/Cryptid/assets/sounds/music_big.ogg new file mode 100644 index 0000000..56f0f08 Binary files /dev/null and b/Cryptid/assets/sounds/music_big.ogg differ diff --git a/Cryptid/assets/sounds/music_code.ogg b/Cryptid/assets/sounds/music_code.ogg new file mode 100644 index 0000000..1040306 Binary files /dev/null and b/Cryptid/assets/sounds/music_code.ogg differ diff --git a/Cryptid/assets/sounds/music_exotic.ogg b/Cryptid/assets/sounds/music_exotic.ogg new file mode 100644 index 0000000..a7cce71 Binary files /dev/null and b/Cryptid/assets/sounds/music_exotic.ogg differ diff --git a/Cryptid/assets/sounds/music_jimball.ogg b/Cryptid/assets/sounds/music_jimball.ogg new file mode 100644 index 0000000..f4c6b5a Binary files /dev/null and b/Cryptid/assets/sounds/music_jimball.ogg differ diff --git a/Cryptid/assets/sounds/speed.py b/Cryptid/assets/sounds/speed.py new file mode 100644 index 0000000..2c9cdc9 --- /dev/null +++ b/Cryptid/assets/sounds/speed.py @@ -0,0 +1,24 @@ +from pydub import AudioSegment +from scipy.io import wavfile +import numpy as np + +def change_speed_and_pitch(sound, speed=1.0): + # Changing speed and pitch + sound_with_altered_frame_rate = sound._spawn(sound.raw_data, overrides={ + "frame_rate": int(sound.frame_rate * speed) + }) + return sound_with_altered_frame_rate.set_frame_rate(sound.frame_rate) + +# Load the OGG file +audio = AudioSegment.from_file("input.ogg", format="ogg") + +# Speed factor (10/7) +speed_factor = 10 / 7 + +# Change speed and pitch +new_audio = change_speed_and_pitch(audio, speed_factor) + +# Export the result +new_audio.export("output.ogg", format="ogg") + +print("The audio file has been processed and saved as output.ogg") diff --git a/Cryptid/assets/sounds/studiofromhelsinki.ogg b/Cryptid/assets/sounds/studiofromhelsinki.ogg new file mode 100644 index 0000000..11ba427 Binary files /dev/null and b/Cryptid/assets/sounds/studiofromhelsinki.ogg differ diff --git a/Cryptid/config.lua b/Cryptid/config.lua new file mode 100644 index 0000000..b252ad1 --- /dev/null +++ b/Cryptid/config.lua @@ -0,0 +1,26 @@ +return { + ["More Stakes"] = true, + ["M Jokers"] = true, + ["Misc."] = true, + ["Tags"] = true, + ["Challenges"] = true, + ["Spectrals"] = true, + ["Vouchers"] = true, + ["Timer Mechanics"] = true, + ["Achievements"] = true, + ["Planets"] = true, + ["Cryptid"] = { ["jimball_music"] = true, ["code_music"] = true, ["exotic_music"] = true, ["big_music"] = true }, + ["Epic Jokers"] = true, + ["Antimatter Deck"] = true, + ["Misc. Jokers"] = true, + ["Code Cards"] = true, + ["Misc. Decks"] = true, + ["Exotic Jokers"] = true, + ["Enhanced Decks"] = true, + ["Blinds"] = true, + ["HTTPS Module"] = false, + ["JokerDisplay"] = true, + ["PokerHands"] = true, + ["Spooky"] = true, + ["Menu"] = true, +} diff --git a/Cryptid/https/linux-https.so b/Cryptid/https/linux-https.so new file mode 100644 index 0000000..c1c0a6d Binary files /dev/null and b/Cryptid/https/linux-https.so differ diff --git a/Cryptid/https/macos-https.so b/Cryptid/https/macos-https.so new file mode 100644 index 0000000..a6e5c98 Binary files /dev/null and b/Cryptid/https/macos-https.so differ diff --git a/Cryptid/https/thread.lua b/Cryptid/https/thread.lua new file mode 100644 index 0000000..10c767a --- /dev/null +++ b/Cryptid/https/thread.lua @@ -0,0 +1,34 @@ +require "love.system" + +-- mac/linux support? + +local script_path = debug.getinfo(1, "S").source:sub(2) +local script_dir = script_path:match("(.*/)") + +package.path = script_dir .. "?.lua;" .. package.path +package.cpath = script_dir .. "?.so;" .. package.cpath + +local index_os = love.system.getOS() + +if index_os == 'OS X' then + loc_https = require("macos-https") +elseif index_os == 'Linux' then + loc_https = require("linux-https") +else + loc_https = require("https") +end + +local last_update_time = 0 +local initial = true +while true do + if (os.time() - last_update_time >= 60) or initial then + initial = nil + last_update_time = os.time() + local resp, txt = loc_https.request("https://discord.com/api/v10/invites/eUf9Ur6RyB?with_counts=true".."&v=" .. tostring(os.time())) + if resp == 200 then + love.thread.getChannel('member_count'):push(txt) + else + love.thread.getChannel('member_error'):push("Failed to get count: "..resp) + end + end +end \ No newline at end of file diff --git a/Cryptid/localization/de.lua b/Cryptid/localization/de.lua new file mode 100644 index 0000000..de68736 --- /dev/null +++ b/Cryptid/localization/de.lua @@ -0,0 +1,3864 @@ +--Translation by Foegro +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antimaterie Deck", + text = { + "Hat die {C:legendary,E:1}positiven Effekte{}", + "von {C:attention}jedem{} Deck", + }, + }, + b_cry_beta = { + name = "Nostalgisches Deck", + text = { + "{C:attention}Joker{} und {C:attention}Verbrauchgegenstände{}", + "Slots sind {C:attention}zusammengefasst", + "{C:attention}Nostalgische{} Blinds ersetzen", + "ihre neuen Versionen." + }, + }, + b_cry_blank = { + name = "Unbedrucktes Deck", + text = { + "{C:inactive,E:1}Tut nichts?", + }, + }, + b_cry_bountiful = { + name = "Größzügiges Deck", + text = { + "Ziehe immer 5 Karten nach", + "{C:attention}Gespielter Hand{} oder {C:attention}Abwurf{}", + }, + }, + b_cry_CCD = { + name = "CCD Deck", + text = { + "Jede Karte ist auch", + "ein {C:attention}zufälliger{} Verbrauchsgegenstand", + }, + }, + b_cry_conveyor = { + name = "Fließband Deck", + text = { + "Joker können {C:attention}nicht{} bewegt werden", + "Am Anfang der Runde,", + "{C:attention}dupliziere{} den rechtesten Joker", + "und {C:attention}zerstöre{} den linkesten Joker", + }, + }, + b_cry_critical = { + name = "Kritisches Deck", + text = { + "Nach jeder gespielten Hand,", + "Chance von {C:green}#1# zu 4{} für {X:dark_edition,C:white} ^2 {} Mult", + "Chance von {C:green}#1# zu 8{} für {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + b_cry_encoded = { + name = "Verschlüsseltes Deck", + text = { + "Starte mit einem {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "und einem {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Nur {C:cry_code}Code Karten{} erscheinen im Shop", + }, + }, + b_cry_equilibrium = { + name = "Deck des Gleichgewichts", + text = { + "Alle Karten haben die", + "{C:attention}selbe Chance{}", + "im Shop aufzutauchen,", + "beginne den Durchlauf mit", + "{C:attention,T:v_overstock_plus}Überbestand Plus", + }, + }, + b_cry_glowing = { + name = "Glühendes Deck", + text = { + "Multipliziere die Werte", + "aller Joker mit {X:dark_edition,C:white} X1.25 {}", + "wenn die Boss Blind besiegt wurde.", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Unendliches Deck", + text = { + "Du kannst {C:attention}unendlich{} viele", + "Karten auswählen.", + "{C:attention}+1{} Handgröße", + }, + }, + b_cry_misprint = { + name = "Fehlgedrucktes Deck", + text = { + "Werte von Karten", + "und Pokerhänden", + "sind {C:attention}zufällig", + }, + }, + b_cry_redeemed = { + name = "Eingelöstes Deck", + text = { + "Wenn ein {C:attention}Gutschein{} gekauft wird,", + "bekomme seine {C:attention}verbesserten Versionen", + }, + }, + b_cry_spooky = { + name = "Gruseliges Deck", + text = { + "Starte mit einem {C:eternal}Ewigen{} {C:attention,T:j_cry_chocolate_dice}Schokoladenwürfel", + "Nach jeder {C:attention}Ante{}, erstelle eine", + "{C:cry_candy}Süßigkeit{} oder einen {X:cry_cursed,C:white}Verfluchten{} Joker", + } + }, + b_cry_very_fair = { + name = "Sehr Faires Deck", + text = { + "{C:blue}-2{} Hände, {C:red}-2{} Abwürfe", + "jede Runde", + "{C:attention}Gutscheine{} erscheinen", + "nicht mehr im Shop", + }, + }, + b_cry_wormhole = { + name = "Wurmloch Deck", + text = { + "Beginne mit einem {C:cry_exotic}Exotischen{C:attention} Joker", + "Joker sind {C:attention}20X{} warscheinlicher", + "{C:dark_edition}Negativ{} zu sein", + "{C:attention}-2{} Joker-Slots", + }, + }, + b_cry_legendary = { + name = "Legendäres Deck", + text = { + "Beginne mit einem {C:legendary}Legendären{C:legendary} Joker", + "Chance von {C:green}1 in 5{} einen weiteren zu erstellen", + "wenn Boss Blind besiegt wurde {C:inactive}(Muss Platz haben){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "Die Box", + text = { + "Alle Gewöhnlichen Joker", + "sind geschwächt.", + }, + }, + bl_cry_clock = { + name = "Die Uhr", + text = { + "+0.1X Blindgröße für alle", + "3 Sekunden, die du in dieser Ante verbringst.", + }, + }, + bl_cry_hammer = { + name = "Der Hammer", + text = { + "Alle Karten mit ungeradem", + "Rang sind geschwächt.", + }, + }, + bl_cry_joke = { + name = "Der Witz", + text = { + "Wenn deine Chips mehr als 2x die benötigten sind,", + "setze die Ante zu einer mehrzahl von #1#", + }, + }, + bl_cry_magic = { + name = "Die Magie", + text = { + "Alle Karten mit geradem", + "Rang sind geschwächt", + }, + }, + bl_cry_lavender_loop = { + name = "Lavendelschleife", + text = { + "1.25X Blindgröße für alle", + "1.5 Sekunden, die du in dieser Runde verbringst", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidian Kugel", + text = { + "Hat die Fähigkeiten", + "aller besiegten Bosse", + }, + }, + bl_cry_oldarm = { + name = "Nostalgischer Arm", + text = { + "Du musst 4 oder weniger", + "Karten spielen", + }, + }, + bl_cry_oldfish = { + name = "Nostalgischer Fisch", + text = { + "Alle Hände starten", + "mit 1 Mult", + }, + }, + bl_cry_oldflint = { + name = "Nostalgischer Feuerstein", + text = { + "Keine Flushes", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgisches Haus", + text = { + "Keine Full Houses", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgische Handfessel", + text = { + "Dividiere Mult durch Abwürfe", + }, + }, + bl_cry_oldmark = { + name = "Nostalgische Marke", + text = { + "Keine Hände die", + "ein Paar enthalten", + }, + }, + bl_cry_oldox = { + name = "Nostalgischer Ochse", + text = { + "Alle Hände starten", + "mit 0 Chips", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgische Säule", + text = { + "Keine Straßen", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgische Schlange", + text = { + "Dividiere Mult durch das Level", + "der gespielten Poker Hand", + }, + }, + bl_cry_pin = { + name = "Die Stecknadel", + text = { + "Joker mit epischer oder höherer", + "Seltenheit sind geschwächt.", + }, + }, + bl_cry_pinkbow = { + name = "Pinke Schleife", + text = { + "Rang der Karten in der Hand", + "werden beim Spielen einer Hand verzufälligt", + }, + }, + bl_cry_sapphire_stamp = { + name = "Saphirstempel", + text = { + "Du kannst eine weitere Karte auswählen, vor der", + "Auswertung wird die Auswahl einer zufälligen Karte aufgehoben", + }, + }, + bl_cry_shackle = { + name = "Die Schelle", + text = { + "Alle negativen Joker", + "sind geschwächt", + }, + }, + bl_cry_striker = { + name = "Der Stürmer", + text = { + "Alle seltenen Joker", + "sind geschwächt", + }, + }, + bl_cry_tax = { + name = "Die Steuer", + text = { + "Wert einer Hand kann nicht", + "mehr als 0.4 mal das benötigte sein", + }, + }, + bl_cry_tornado = { + name = "Türkiser Tornado", + text = { + "Chance von #1# zu #2#, dass", + "die gespielte Hand nicht gewertet wird", + }, + }, + bl_cry_trick = { + name = "Der Trick", + text = { + "Nach jeder Hand, drehe alle Karten", + "mit dem Gesicht oben in der Hand um", + }, + }, + bl_cry_vermillion_virus = { + name = "Zinnober Virus", + text = { + "Ein zufälliger Joker wird", + "jede Hand ersetzt", + }, + }, + bl_cry_windmill = { + name = "Die Windmühle", + text = { + "Alle ungewöhnlichen Joker", + "sind geschwächt", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Mache {C:cry_code}#1#{} ausgewählte Karte", + "zu einer {C:cry_code}gewählten{} Verstärkung", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Zerstöre einen {C:cry_code}gewählten{} Joker,", + "erstelle einen {C:cry_code}neuen{} Joker", + "mit der {C:cry_code}selben Seltenheit", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Nein", + }, + }, + c_cry_ctrl_v = { + name = "://CTRL+V", + text = { + "Erstelle eine Kopie einer {C:cry_code}gewälten{} Spielkarte oder Gebrauchsgegenstand." + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "Entferne ein {C:cry_code}gewählten{}", + "Shopgegenstand {C:cry_code}permanent{}", + "{C:inactive,s:0.8}Item kann in diesem Durchlauf nicht mehr auftauchen.", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "{C:cry_code}Halbiere{} alle gelisteten Preise", + "Im momentanen Shop", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "Die {C:cry_code}nächste{} gespielte Hand wird", + "gewertet als ob sie eine {C:cry_code}gewählte{} Pokerhand enthält.", + "{C:inactive,s:0.8}Geheime Hände müssen", + "{C:inactive,s:0.8}entdeckt weden um gültig zu sein.", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Wähle zwei Joker", + "die {C:cry_code}Verhackt{} werden", + }, + }, + c_cry_inst = { + name = "://INSTANTIATE", + text = { + "Ziehe eine Karte mit dem {C:attention}Rang{} einer ausgewählten Karte", + "und eine mit der {C:attention}Farbe{} der ausgewählten Karte", + "{C:inactive}(Wenn möglich){}", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Füge {C:dark_edition}Fehlerhaft{} zu allen", "Karten {C:cry_code}in der Hand{} hinzu" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Verschmelze ein gewählten {C:cry_code}Verbrauchsgegenstand", + "mit einer gewählten {C:cry_code}Spielkarte", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Verdopple{} alle Werte", + "eines gewählten {C:cry_code}Jokers{} bis", + "zum ende der Runde.", + }, + }, + c_cry_patch = { + name = "://PATCH", + text = { + "Entfernt alle Sticker und Schwächungen von", + "allen momentan sichtbaren Gegenständen", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Nächste besiegte Blind", + "gibt {C:cry_code}X#1#{} Zinsen", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Nächstes {C:cry_code}Booster Packet{} hat", + "{C:cry_code}#1#{} zusätzliche Karte und", + "{C:cry_code}#1#{} zusätzliche Auswahl", + "{C:inactive}(Momentan {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Erfrische {C:blue}Hände{} und {C:red}Abwürfe{},", + "tue {C:cry_code}alle{} Karten zurück ins Deck", + "und ziehe eine {C:cry_code}neue{} Hand", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Setzt {C:cry_code}Spielstand{} zum", + "Start {C:cry_code}dieser Ante{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Zerstöre einen {C:cry_code}gewählten{} Joker,", + "erstelle einen {C:cry_code}Überarbeitungs-Tag{} mit", + "einer Edition {C:cry_code}besser{}", + "{C:inactive,s:0.8}Nutzt Reinfolge in der Sammlung", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Besuche einen {C:cry_code}Shop", + "während einer {C:cry_code}Blind", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Wähle einen Joker", + "oder eine Spielkarte", + "um {C:cry_code}Manipuliert{} zu werden", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Enden momentane nicht-Boss {C:cry_code}Blind{}", "{C:cry_code}ohne{} Preisgeld zu bekommen" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Erstelle einen {C:cry_code}Fehlerhaften{}", + "Essen Joker", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Mache {C:cry_code}#1#{} gewählte Karten", + "zu einem {C:cry_code}gewählten{} Rang", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} Mult", + }, + }, + e_cry_blur = { + name = "Verschwommen", + text = { + "{C:attention}Löse{} diese Karte", + "{C:attention}1{} mal mehr {C:attention}aus{}", + "Chance von {C:green}#1# zu #2#{}", + "sie {C:attention}#3#{} weiteres", + "mal {C:attention}auszulösen", + }, + }, + e_cry_double_sided = { + name = "Doppelseitig", + text = { + "Diese Karte kann", + "{C:attention}gedreht{} werden um", + "eine andere Karte zu zeigen.", + "{C:inactive}(Leere Seite kann mit anderer", + "{C:inactive}Karte verschmolzen werden)" + }, + }, + e_cry_glass = { + name = "Zerbrechlich", + label = "Zerbrechlich", + text = { + "{C:white,X:mult} X#3# {} Mult", + "Chance von {C:green}#1# zu #2#{}, dass diese", + "Karte beim auslösen", + "{C:attention}nicht{} {C:red}zerstört{} wird", + }, + }, + e_cry_glitched = { + name = "Fehlerhaft", + text = { + "Alle Werte auf dieser Karte sind", + "ein {C:dark_edition}zufälliger{} Wert", + "zwichen {C:attention}X0.1{} und {C:attention}X10{} des originalen.", + "{C:inactive}(Wenn möglich){}", + }, + }, + e_cry_gold = { + name = "Golden", + label = "Golden", + text = { + "Verdiene {C:money}$#1#{} bei Benutzung", + "oder beim Auslösen", + }, + }, + e_cry_m = { + name = "Lustig", + text = { + "{C:mult}+#1#{} Mult", + "Diese Karte fühlt sich", + "ziemlich {C:attention}lustig{}", + }, + }, + e_cry_mosaic = { + name = "Mosaik", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + e_cry_noisy = { + name = "Geräuchvoll", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Übersättigt", + text = { + "Alle Werte", + "auf dieser Karte", + "sind {C:attention}verdoppelt{}", + "{C:inactive}(Wenn möglich)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Echo Karte", + text = { + "Chance von {C:green}#2# zu #3#{} um diese Karte", + "#1# weiteres mal {C:attention}auszulösen{}", + "wenn sie gewertet wird", + }, + }, + }, + Joker = { + j_cry_adroit = { + name = "Geschickter Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_altgoogol = { + name = "Nostalgische Googol Play Karte", + text = { + "Verkaufe diese Karte um", + "{C:attention}2{} Kopien von dem linkesten {C:attention}Joker{} zu machen", + "{C:inactive,s:0.8}Kopiert keine Nostalgischen Googol Play Karten{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Wie Antennen zum Himmel", + text = { + "Dieser Joker bekommt", + "{X:chips,C:white} X#1# {} Chips wenn jede", + "gespielte {C:attention}7{} oder {C:attention}4{} gewertet wurde", + "{C:inactive}(Momentan {X:chips,C:white}X#2# {C:inactive} Chips)", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "{X:mult,C:white} X#1# {} Mult gegen {C:attention}Boss Blinds{}" }, + }, + j_cry_big_cube = { + name = "Großer Würfel", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_biggestm = { + name = "Riesig", + text = { + "{X:mult,C:white} X#1# {} Mult bis zum Ende", + "der Runde wenn {C:attention}Pokerhand{}", + "ein {C:attention}#2#{} ist", + "{C:inactive}(Momentan {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}nicht fett, hat nur große Knochen.", + }, + }, + j_cry_blacklist = { + name = "Blacklist", + text = { + "Wenn ein(e) {C:attention}#1#{} in der Hand oder gespielt ist,", + "setze {C:chips}Chips{} und {C:mult}Mult{} zu 0", + "{C:red,E:2}zerstört sich selbst{} wenn kein {C:attention}#1#{} im deck ist", + "{C:inactive,s:0.8}Rang ändert sich nicht" + } + }, + j_cry_blender = { + name = "Blender", + text = { + "Erstelle ein {C:attention}zufäligen{}", + "Verbrauchsgegenstand wenn eine", + "{C:cry_code}Code{} Karte benutzt wird.", + "{C:inactive}(Muss Platz haben){}", + }, + }, + j_cry_blurred = { + name = "Verschwommener Joker", + text = { + "Gain {C:blue}+#1#{} Hand/Hände wenn", + "{C:attention}Blind{} ausgewählt wird", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Jeder {C:attention}Joker{} gibt {C:chips}+#1#{} Chips", + "Erhöhe anzahl um {C:chips}+#2#{} wenn", + "{C:attention}Pokerhand{} ein {C:attention}#3#{} ist", + "{C:inactive,s:0.8}Lustige Joker geben statdessen{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips{}", + }, + }, + j_cry_bonkers = { + name = "Bekloppter Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "Chance von {C:green}#1# zu #2#{} für jede", + "gespielte {C:attention}Bonus{} Karte die", + "{C:attention}Joker{} or {C:attention}Verbrauchgegenstand Slots", + "um {C:dark_edition}1{} zu erhöhen wenn sie gewertet werden.", + "{C:red}Funktioniert einmal pro Runde", + "{C:inactive,s:0.8}(Gleiche Chance für beide){}", + }, + }, + j_cry_booster = { + name = "Booster Joker", + text = { + "{C:attention}+#1#{} Booster Packet Slot", + "im Shop verfügbar", + }, + }, + j_cry_boredom = { + name = "Lange Weile", + text = { + "Chance von {C:green}#1# zu #2#{} um", + "jeden {C:attention}Joker{} oder jede", + "{C:attention}Spielkarte{} erneut {C:attention}auszulösen{}", + "{C:inactive,s:0.8}Funktioniert nicht auf andere Lange Weilen{}", + }, + }, + j_cry_brittle = { + name = "Zerbrechliche Süßigkeit", + text = { + "Für die nächsten {C:attention}#1#{} Hände,", + "füge {C:attention}Stein{}, {C:attention}Gold{}, oder {C:attention}Stahl{} zu", + "der rechtesten gewerteten Karte hinzu" + } + }, + j_cry_bubblem = { + name = "Blasen M", + text = { + "Erstelle einen {C:dark_edition}Foil {C:attention}Lustigen Joker{}", + "wenn gespielte Hand einen", + "{C:attention}#1#{} enthält.", + "{C:red,E:2}Zerstört sich selbst{}", + }, + }, + j_cry_busdriver = { + name = "Bus Fahrer", + text = { + "Chance von {C:green}#1# zu #3#{}", + "für {C:mult}+#2#{} Mult", + "Chance von {C:green}1 zu 4{}", + "für {C:mult}-#2#{} Mult", + }, + }, + j_cry_candy_basket = { + name = "Süßigkeitenkorb", + text = { + "Verkaufe diese Karte up {C:attention}#1#{} {C:cry_candy}Süßigkeiten{} zu erstellen", + "{C:attention}+#2#{} {C:cry_candy}Süßigkeiten{} für alle {C:attention}2{} besiegte Blinds", + "{C:attention}+#3#{} {C:cry_candy}Süßigkeiten{} für jede besiegte {C:attention}Boss Blind{}" + } + }, + j_cry_candy_buttons = { + name = "Süßigkeitenknöpfe", + text = { + "Die nächsten {C:attention}#1#{} Aktualisierungen", + "kosten {C:money}$1{}", + } + }, + j_cry_candy_cane = { + name = "Zuckerstange", + text = { + "Für die nächsten {C:attention}#1#{} Runden,", + "geben gespielte Karten {C:money}$#2#", + "wenn {C:attention}erneut ausgelöst" + } + }, + j_cry_candy_dagger = { + name = "Süßigkeitendolch", + text = { + "Wenn {C:attention}Blind{} ausgewält wurde,", + "zerstöre rechten Joker", + "um eine {C:cry_candy}Süßigkeit{} zu erstellen.", + } + }, + j_cry_candy_sticks = { + name = "Zuckerstöcke", + text = { + "Der Effekt der nächsten Boss Bind ist nicht Aktiv", + "bis {C:attention}#1#{} Hand gespielt wurde.", + } + }, + j_cry_canvas = { + name = "Leinwand", + text = { + "{C:attention}Löse{} alle {C:attention}Jokers{} auf der linken Seite", + "{C:attention}so oft neu aus{} wie du nicht-{C:blue}Gewöhnliche{C:attention} Joker{}", + "auf der rechten Seite hast", + }, + }, + j_cry_caramel = { + name = "Karamel", + text = { + "Jede gespielte Karte gibt", + "{X:mult,C:white}X#1#{} Mult wenn gewertet", + "für die nächsten {C:attention}#2#{} Runden", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Löse {C:attention}linkesten{} Joker", + "{C:attention}#1#{} weitere(s) mal(e) aus", + }, + }, + j_cry_chili_pepper = { + name = "Chilischote", + text = { + "Dieser Joker bekommt {X:mult,C:white} X#2# {} Mult", + "am Ende der Runde,", + "{C:red,E:2}zerstört sich selbst{} nach {C:attention}#3#{} Runden", + "{C:inactive}(Momentan{} {X:mult,C:white} X#1# {} {C:inactive}Mult){}", + }, + }, + j_cry_chocolate_dice = { + name = "Schokoladenwürfel", + text = { + "Werfe einen {C:green}Zehnerwürfel{} wenn", + "{C:attention}Boss Blind{} besiegt wurde", + "um ein {C:cry_ascendant,E:1}Event{} zu starten.", + "{C:inactive}(Currently: #1#)" + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Chips und {X:dark_edition,C:white}^#1#{} Mult", + "wenn {C:attention}genau{} #2#", + "Hände ubrig sind.", + }, + }, + j_cry_circus = { + name = "Zirkus", + text = { + "{C:red}Seltene{} Joker geben {X:mult,C:white} X#1# {} Mult", + "{C:cry_epic}Epische{} Joker geben {X:mult,C:white} X#2# {} Mult", + "{C:legendary}Legendäre{} Joker geben {X:mult,C:white} X#3# {} Mult", + "{C:cry_exotic}Exotische{} Joker geben {X:mult,C:white} X#4# {} Mult", + }, + }, + j_cry_clash = { + name = "Der Kampf", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält", + }, + }, + j_cry_CodeJoker = { + name = "Code Joker", + text = { + "Erstelle eine {C:dark_edition}Negative{}", + "{C:cry_code}Code Karte{} wenn", + "{C:attention}Blind{} ausgewählt wird", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Verdiene zwischen", + "{C:money}$#1#{} und {C:money}$#2#{} für", + "jeden {C:attention}verkauften{} Joker", + }, + }, + j_cry_compound_interest = { + name = "Zinseszins", + text = { + "Bekomme {C:money}#1#%{} deines gesammten Geldes", + "am ende der Runde,", + "erhöht sich um {C:money}#2#%{} für jede", + "aufeinenderfolgende Auszahlung", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "Wenn eine {C:cry_code}Code{} Karte benutzt wird,", + "Chance von {C:green}#1# zu #2#{} um eine Kopie", + "zu deinen Verbrauchsgegenständen hinzuzufügen", + "{C:inactive}(Muss Platz haben)", + }, + }, + j_cry_cotton_candy = { + name = "Zuckerwatte", + text = { + "Wenn verkauft, bekommen", + "benachbarte {C:attention}Joker{} {C:dark_edition}Negativ{}" + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "Dieser Joker bekommt {C:chips}+#2#{} Chips", + "für jede {C:attention}Aktualisierung{} im Shop", + "{C:green}Alle Aktualisierungen sind kostenlos{}", + "{C:inactive}(Momentan {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Kette", + text = { + "Verkaufe diese Karte um", + "{C:money}$#1#{} {C:attention}Verkaufswert{} zu jeder", + "{C:attention}Joker{} Karte hinzuzufügen", + }, + }, + j_cry_cube = { + name = "Würfel", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob = { + name = "Schluchz", + text = { + "{C:edition,E:1}du kannst nicht{} {C:cry_ascendant,E:1}rennen...{}", + "{C:edition,E:1}du kannst dich nicht{} {C:cry_ascendant,E:1}verstecken...{}", + "{C:dark_edition,E:1}du kannst nicht entkommen...{}", + "{C:inactive}(Muss Platz haben){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "Dieser Joker bekommt {C:chips}+#2#{} Chips", + "für jede {C:attention}gekaufte{} Karte", + "{C:inactive}(Momentan {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Ausschneiden", + text = { + "Dieser Joker zerstört", + "eine zufällige {C:cry_code}Code{} Karte", + "und bekommt {X:mult,C:white} X#1# {} Mult", + "am ende des {C:attention}Shops{}", + "{C:inactive}(Momentan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Deliriouser Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält" + } + }, + j_cry_discreet = { + name = "Diskreter Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält" + } + }, + j_cry_doodlem = { + name = "Gekritzeltes M", + text = { + "Erstelle 2 {C:dark_edition}Negative{} {C:attention}Verbrauchsgegenstände{}", + "wenn {C:attention}Blind{} ausgewählt wird", + "Erstelle 1 weiteren {C:attention}Verbrauchsgegenstand", + "für jeden {C:attention}Lustigen Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Doppelwage", + text = { + "Steigende {C:attention}Jokers{}", + "steigen {C:attention}Quadratisch", + "{C:inactive,s:0.8}(z.B. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(wächst um +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "Dieser Joker bekommt {X:mult,C:white} X#1# {} Mult für", + "jede gespielte, {C:attention}nicht gewertete{} {V:1}#2#{} Karte,", + "Farbe ändert sich jede Runde", + "{C:inactive}(Momentan {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubioser Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "eine {C:attention}#2#{} enthält" + } + }, + j_cry_duos = { + name = "Die Duos", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Dieser Joker bekommt {X:mult,C:white} X#2# {} Mult", + "wenn ein {C:attention}Joker{} oder eine", + "Spielkarte ausgelöst wird", + "{C:inactive}(Momentan {X:mult,C:white} X#1# {C:inactive} Mult)", + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Zieht {C:green}volles Deck{} zur Hand", + "wenn {C:attention}Blind{} ausgewählt wird", + "{C:inactive,s:0.8}\"Wenn du mich bei meinem 1x nicht aushälst,", + "{C:inactive,s:0.8}verdienst du much bei meinem 2x nicht\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "Wenn du einen {C:attention}Tag{} erhälst,", + "erställe {C:attention}#1#{} Kopien", + "und {C:attention}erhöhe{} die Nummer der", + "Kopien um {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ass Aequilibrium", + text = { + "Joker erscheinen in der", + "Reinfolge der {C:attention}Sammlung{}", + "Erstelle {C:attention}#1#{} {C:dark_edition}Negative(n){} Joker", + "wenn eine Hand gespielt wird.", + "{C:cry_exotic,s:0.8}Exotische {C:inactive,s:0.8}oder höhere Joker können nicht erscheinen", + "{s:0.8}Letzter erstellter Joker: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Ewige Flamme", + text = { + "Dieser Joker bekommt {X:mult,C:white} X#1# {} Mult", + "für jede {C:attention}verkaufte{} Karte", + "{C:inactive}(Momentan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographische{} Karten", + "geben {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "Dieser Joker bekommt {X:dark_edition,C:white} ^#1# {} Mult", + "wenn {X:red,C:white} XMult {} ausgelöst wird.", + "{C:inactive}(Momentan {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult wenn", + "gespielte Karten {C:attention}#2#{}", + "oder weniger mal ausgelöst werden.", + }, + }, + j_cry_filler = { + name = "Der Füller", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "eine {C:attention}#2#{} enthält", + }, + }, + j_cry_fractal = { + name = "Fraktal Finger", + text = { + "{C:attention}+#1#{} Karten auswahl Limit", + }, + }, + j_cry_flip_side = { + name = "Auf der anderen Seite...", + text = { + "{C:dark_edition}Doppelseitige{} Joker nutzen", + "ihre Rückseite für Effekte", + "statt der Vorderseite", + "{C:attention}Löse{} alle {C:dark_edition}Doppelseitigen{} Joker erneut {C:attention}aus" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}zerstört sich selbst{} in {C:attention}#2#{} Runde(n)", + "Erhöht sich um {C:attention}#3#{} Runden wenn", + "{C:attention}Lustiger Joker verkauft{} wird", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Größe Pommes, 20 Stücke & Großer Kuchen{}", + }, + }, + j_cry_foolhardy = { + name = "Tollkühner Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_formidiulosus = { + name = "Formidiulosus", + text = { + "Wenn ein {X:cry_cursed,C:white}Verfluchter{} Joker erhalten wird, zerstöre ihn", + "Erstelle {C:attention}#1#{} {C:dark_edition}Negative {C:cry_candy}Süßigkeiten{} am Ende des Shops", + "{X:dark_edition,C:white}+^#2#{} Mult für jeden {C:cry_candy}Süßigkeitenjoker{} den du besitzt", + "{C:inactive}(Momenan {X:dark_edition,C:white}^#3#{C:inactive} Mult)", + }, + }, + j_cry_foxy = { + name = "Fuchsiger Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "Dieser Joker bekommt {C:chips}+#2#{} Chips", + "wenn gespielte hand {C:attention}nicht{}", + "die meistgespielte {C:attention}Pokerhand{} ist", + "{C:inactive}(Momentan {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_fuckedup = { + name = "Abgefuckter Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_gardenfork = { + name = "Garden der Kreuzungen", + text = { + "Verdiene {C:money}$#1#{} wenn {C:attention}gespielte Hand{}", + "Ein {C:attention}Ass{} und eine {C:attention}7{} enthält", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Verdoppelt{} alle Werte", + "des linkesten {C:attention}Jokers", + "am Ende der Runde", + }, + }, + j_cry_ghost = { + name = "Geist", + text = { + "Am Ende der Runde:", + "Chance von {C:green}#1# zu #2#{} einen", + "zufälligen {C:attention}Joker{} zu {C:attention}besetzen", + "Chance von {C:green}#1# zu #3#{}", + "{E:2,C:red}sich selbst zu zerstören" + } + }, + j_cry_giggly = { + name = "Absurder Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "eine {C:attention}#2#{} enthält" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Verdiene {C:money}#1#%{} des gesammten", + "Geldes am ende der Runde", + "Auszahlung erhöht sich um {C:money}#2#%{}", + "wenn eine {C:attention}Gold{}", + "Karte gewertet wird.", + }, + }, + j_cry_googol_play = { + name = "Googol Play Karte", + text = { + "Chance von {C:green}#1# zu #2#{} für", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Erstelle einen zufälligen {C:attention}Joker{}", + "am Ende der Runde", + "Verkaufe diese Karte um", + "einen zufälligen {C:attention}Joker{} zu erstellen", + "{C:inactive}(Muss Platz haben){}", + }, + }, + j_cry_happyhouse = { + name = "Fröhliches Haus", + text = { + "{X:dark_edition,C:white}^#1#{} Mult nachdem du", + "{C:attention}114{} Hände{} gespielt hast", + "{C:inactive}(Momentan #2#/114){}", + "{C:inactive,s:0.8}Es gibt keinen Platz so schön wie dein Zuhause!{}", + }, + }, + j_cry_home = { + name = "Das Zuhause", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Verdiene {C:money}$#1#{} wenn", + "ein {C:attention}Verbrauchsgegenstand{} benutzt wird", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Löse alle Karten", + "{C:attention}#2#{} mal mehr aus,", + "jede Karte gibt", + "{X:mult,C:white} X#1# {} Mult wenn sie gewertet wird", + }, + }, + j_cry_jawbreaker = { + name = "Kieferbrecher", + text = { + "Wenn {C:attention}Boss Blind{} besiegt wurde,", + "{C:attention}verdopple{} die Werte benachbarter Joker", + "{E:2,C:red}Zerstört sich selbst{}", + } + }, + j_cry_jimball = { + name = "Jimball", + text = { + "Dieser Joker bekommt {X:mult,C:white} X#1# {} Mult", + "für jede {C:attention}aufeinenderfolgende{} gespielte Hand", + "währende du deine", + "am meisten gespielte {C:attention}Pokerhand{} spielst", + "{C:inactive}(Momentan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Lustiger Joker?", + text = { + "Erstelle einen {C:dark_edition}Lustigen{} Joker", + "Wenn ein Joker {C:attention}verkauft{} wird", + "{C:red}Funktioniert einmal pro Runde{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Entführung", + text = { + "Verdiene {C:money}$#2#{} am Ende der Runde", + "Erhöhe Auszahlung um {C:money}$#1#{}", + "wenn ein {C:attention}Mult Typ{} oder", + "{C:attention}Chip Typ{} Joker verkauft wird", + }, + }, + j_cry_kooky = { + name = "Wahnsinniger Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_krustytheclown = { + name = "Krusty der Clown", + text = { + "Dieser Joker bekommt", + "{X:mult,C:white} X#1# {} Mult wenn", + "jede gespielte {C:attention}Karte{} gewertet wird", + "{C:inactive}(Momenan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoskop", + text = { + "Füge {C:dark_edition}Polychrome{} zu", + "einem zufälligen {C:attention}Joker{} hinzu", + "wenn {C:attention}Boss Blind{} besiegt wird", + }, + }, + j_cry_lightupthenight = { + name = "Erläuchte die Nacht", + text = { + "Jede gespielte {C:attention}7{} oder {C:attention}2{}", + "gibt {X:mult,C:white}X#1#{} Mult wenn gewertet", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Gib zukünftigen Kopien", + "dieses Jokers {X:mult,C:white}X#1#{} Mult", + "am Ende der Runde", + "{C:inactive}(Momentan {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Löse{} alle Joker für jeden", + "{C:attention}Jolly Joker{}, der in dieser Runder", + "verkauft wurde erneut {C:attention}aus", + "{C:inactive}(Momentan{}{C:attention:} #1#{}{C:inactive} Auslösung(en)){}", + "{C:inactive,s:0.8}Es war nicht genug Platz...{}", + }, + }, + j_cry_lucky_joker = { + name = "Glücksjoker", + text = { + "Verdiene {C:money}$#1#{} jedes mal wenn eine", + "{C:attention}Glückskarte{} {C:green}erfolgreich{}", + "ausgelöst wird", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "Alle Joker geben", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "Dieser Joker bekommt {X:mult,C:white} X#1# {} Mult", + "wenn {C:attention}Lustiger Joker{} verkauft wird", + "{C:inactive}(Momentan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Erstelle einen {C:dark_edition}Negativen{}", + "{C:attention}Lustigen Joker{} wenn", + "{C:attention}Blind{} ausgewählt wird", + }, + }, + j_cry_macabre = { + name = "Makaberer Joker", + text = { + "Wenn {C:attention}Blind{} ausgewählt wird,", + "zerstöre jeden {C:attention}Joker{} außer", + "{C:legendary}M-Joker{} und {C:attention}Lustige Jokers{}", + "und erstelle 1 {C:attention}Lustigen Joker{}", + "für jede zerstörte Karte", + }, + }, + j_cry_magnet = { + name = "Kühlschrankmagnet", + text = { + "Verdiene {C:money}$#1#{} am Ende der Runde", + "Es verdient {X:money,C:white} X#2# {} wenn du", + "{C:attention}#3#{} oder weniger {C:attention}Joker{} Karten hast", + }, + }, + j_cry_manic = { + name = "Manischer Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Löse alle Joker", + "{C:attention}#1#{} weitere(s) mal aus", + }, + }, + j_cry_maximized = { + name = "Maximiert", + text = { + "Alle {C:attention}Bildkarten{}", + "zählen als {C:attention}Könige{},", + "alle {C:attention}Zahlkarten{}", + "zählen als {C:attention}10{}", + }, + }, + j_cry_maze = { + name = "Labyrint", + text = { + "Alle Hände zählen als", + "{C:attention}erste Hand{} der Runde,", + "alle Abwürfe zählen als", + "{C:attention}erster Abwurf{} der Runde", + }, + }, + j_cry_Megg = { + name = "Mei", + text = { + "Verkaufe diese Karte um", + "{C:attention}#2#{} Lustige #3# zu erstellen, erhöhe", + "um {C:attention}#1#{} am Ende der Runde", + }, + }, + j_cry_mellowcreme = { + name = "Mellowcreme", + text = { + "Verkaufe diese Karte um", + "Verkaufswert aller {C:attention}Verbrauchgegenstände{}", + "mit {C:attention}X#1#{} zu {C:attention}multiplizieren" + } + }, + j_cry_membershipcard = { + name = "Mitgliedskarte", + text = { + "{X:mult,C:white}X#1#{} Mult für jedes Mitglied", + "im {C:attention}Cryptid Discord{}", + "{C:inactive}(Momentan {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Alte Mitgliedskarte", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips für jedes Mitglied", + "im {C:attention}Cryptid Discord{}", + "{C:inactive}(Momentan {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteorschauer", + text = { + "{C:dark_edition}Foil{} Karten", + "geben {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Bekomme {C:money}$#2#{} am Ende der Runde", + "Erhöhe Auszahlung um", + "{C:money}$#1#{} für jeden {C:attention}Lustigen Joker{}", + "oder {C:legendary}M Joker{} am", + "Ende der Runde", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "Dieser Joker erhält {X:mult,C:white} X#1# {} Mult", + "wenn keine {C:attention}Abwürfe{}", + "am Ende der Runde genutzt wurden", + "{C:inactive}(Momentan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Affendolch", + text = { + "Wenn {C:attention}Blind{} ausgewählt wird,", + "wird der linke Joker zerstört", + "und {C:attention}10 mal{} der Verkaufswert", + "zu den {C:chips}Chips{} hinzugefügt", + "{C:inactive}(Momentan {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_monopoly_money = { + name = "Monopoly Geld", + text = { + "Chance von {C:green}#1# zu #2#{} gekaufte", + "Gegenstände zu {C:attention}zerstören{}", + "Halbiert Geld wenn {C:attention}Verkauft", + } + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Verdiene {C:money}$#2#{} am Ende der Runde", + "Erhöhe die Auszahlung um {C:money}$#1#{} wenn", + "eine Karte mit {C:attention}Edition{} verkauft wird", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Erstelle einen {C:legendary}M Joker{} am Ende der Runde", + "Jeder {C:attention}Lustige Joker{} oder {C:legendary}M Joker", + "gibt {X:dark_edition,C:white}^#1#{} Mult", + "Erhöhe anzahl um {X:dark_edition,C:white}^#2#{}", + "wenn {C:attention}Lustiger Joker verkauft{} wird", + "{C:inactive,s:0.8}(Tredecim exkludiert)", + }, + }, + j_cry_mstack = { + name = "M Stapel", + text = { + "Führe alle gespielten Karten einmal", + "für alle {C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Lustige Joker{} verkauft", + "erneut aus", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Auslösungen){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "Chance von {C:green}#1# zu #2#{} für jede", + "gespielte {C:attention}Mult{} Karte eine", + "{C:spectral}Cryptid{} Karte zu erstellen wenn sie Ausgewertet wird", + "{C:inactive}(Muss Platz haben)", + }, + }, + j_cry_necromancer = { + name = "Totenbeschwörer", + text = { + "Wenn ein Joker für mehr als {C:attention}$0 verkauft{} wird", + "Erhalte einen {C:attention}zufälligen{} Joker der in diesem Durchlauf {C:attention}verkauft{} wurde", + "und setzte seinen {C:attention}Verkaufspreis{} zu {C:attention}$0{}", + }, + }, + j_cry_negative = { + name = "Negativer Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} Slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips wenn gespielte Hand", + "eine {C:attention}6{} und eine {C:attention}9{} enthält", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Nacht", + text = { + "{X:dark_edition,C:white}^#1#{} Mult für die letzte", + "Hand der Runde", + "{E:2,C:red}zerstört sich selbst{} bei", + "der letzten Hand der Runde", + }, + }, + j_cry_nosound = { + name = "Kein Geräuch, keine Errinerung", + text = { + "Löse alle {C:attention}7{}", + "{C:attention:}#1#{} weitere(s) mal(e) aus", + }, + }, + j_cry_notebook = { + name = "Notizbuch", + text = { + "Chance von {C:green} #1# zu #2#{} um {C:dark_edition}+1{} Joker", + "Slot pro {C:attention}Aktualisierung{} im Shop zu erhalten", + "{C:green}Funktioniert immer{} wenn du", + "{C:attention}#5#{} oder mehr {C:attention}Lustige Joker{} hast", + "{C:red}Funktioniert einmal pro Runde{}", + "{C:inactive}(Momentan {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Zahlblöcke", + text = { + "Verdiene {C:money}$#1#{} am Ende der Runde", + "Erhöhe Auszahlung um {C:money}$#2#{}", + "für jede {C:attention}#3#{} in der Hand,", + "Rang ändert sich jede Runde", + }, + }, + j_cry_nuts = { + name = "Die Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält" + } + }, + j_cry_oil_lamp = { + name = "Öl Lampe", + text = { + "Am Ende der Runde", + "werden alle Werte des {C:attention}rechten{} Joker um {C:attention}x#1#{} erhöht", + }, + }, + j_cry_oldblueprint = { + name = "Alter Bauplan", + text = { + "Kopiert Fähigkeit des", + "rechten {C:attention}Jokers{}", + "Chance von {C:green}#1# zu #2#{}, dass diese", + "Karte am Ende der Runde", + "zerstört wird", + }, + }, + j_cry_oldcandy = { + name = "Nostalgische Süßigkeit", + text = { + "Verkaufe diese Karte um", + "permantent {C:attention}+#1#{} Handgröße", + "zu bekommen", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgischer Unsichtbarer Joker", + text = { + "{C:attention}Dupliziere{} einen zufälligen", + "{C:attention}Joker{} für alle {C:attention}4", + "verkauften Joker Karten", + "{s:0.8}Nostalgischer unsichbarer Joker Excludiert{}", + "{C:inactive}(Momentan #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panoptikum", + text = { + "Alle Hände zählen als", + "{C:attention}letzte Hand{} der Runde", -- +$4 + }, + }, + j_cry_penetrating = { + name = "Durchdringender Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_pickle = { + name = "Saure Gurke", + text = { + "Wenn {C:attention}Blind{} übersprungen wird, erstelle", + "{C:attention}#1#{} Tags, reduziere um", + "{C:red}#2#{} wenn {C:attention}Blind{} ausgewählt wird", + }, + }, + j_cry_pirate_dagger = { + name = "Piratendolch", + text = { + "Wenn {C:attention}Blind{} ausgewählt wird,", + "zerstöre rechten Joker", + "und füge {C:attention}ein Viertel{} seines", + "Verkaufwertes zu seinem {X:chips,C:white} XChips {} hinzugefügt", + "{C:inactive}(Momentan {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + + j_cry_pity_prize = { + name = "Trostpreis", + text = { + "Wenn ein {C:attention}Booster Pack{} übersprungen wird, erhalte einen zufälligen {C:attention}Tag{}" + }, + }, + j_cry_pot_of_jokes = { + name = "Topf der Witze", + text = { + "{C:attention}#1#{} Handgröße,", + "Erhöht sich um", + "{C:blue}#2#{} jede Runde", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "Dieser Joker erhält {X:dark_edition,C:white} ^#1# {} Mult", + "Wenn alle Karten in der Hand", + "{C:attention}Asse{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, oder {C:attention}7{} sind", + "{C:inactive}(Momentan {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "Dieser Joker erhält", + "{X:mult,C:white} X#1# {} Mult wenn eine", + "{C:cry_code}Code{} Karte benutzt wird", + "{C:inactive}(Momentan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "Wenn {C:attention}Pokerhand{} ein", + "{C:attention}Royal Flush{} ist, zerstöre die gewertete", + "{C:attention}Dame{} und erstelle einen", + "{C:dark_edition}Negativen {}{C:red}Seltenen{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "Das Quintett", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält", + }, + }, + j_cry_redbloon = { + name = "Roter Ballon", + text = { + "Verdiene {C:money}$#1#{} in {C:attention}#2#{} Runde(n)", + "{C:red,E:2}zerstört sich selbst{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante wenn", + "{C:money}$#2#{} {C:inactive}($#3#){} ausgegeben wurde", + "{s:0.8}Bedingung erhöht sich", + "{C:attention,s:0.8}expotentiel{s:0.8} für jede benutzung", + "{C:money,s:0.8}Nächste erhöhung: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "Wenn ein {C:attention}Joker{} verkauft wird,", + "füge seinen Effekt", + "zu jeden anderen Joker hinzu", + "{C:inactive,s:0.8}Hat keinen effekt auf andere Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fülle alle Joker slots {C:inactive}(Max 100){}", + "mit {C:dark_edition}Holographischen{} {C:attention}Lustigen Jokers{} wenn", + "{C:attention}Abgewurfene Pokerhand{} ein {C:attention}#1#{} ist", + "{C:red,E:2}zerstört sich selbst{}", + "{C:inactive,s:0.8}Das ULTIMATIVE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Zufälliger effekt jede {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Opfer", + text = { + "Erstelle einen {C:green}Ungewönlichen{} Joker", + "und 3 {C:attention}Lustige Jokers{} wenn", + "eine {C:spectral}Geisterkarte{} benutzt wird", + "{C:red}Funktioniert einmal pro Runde{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Setzling", + text = { + "Nachdem {C:attention}#2#{} {C:inactive}[#1#]{} Verstärkte", + "Karten gewertet wurden, verkaufe diese Karte um", + "einen {C:cry_epic}Epischen{} {C:attention}Joker{} zu erstellen", + "{C:inactive,s:0.8}Erstellt einen {C:red,s:0.8}Seltenen{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}wenn {C:cry_epic,s:0.8}Epische{} {C:inactive,s:0.8}Joker deaktiviert sind{}", + }, + }, + j_cry_savvy = { + name = "Versierter Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Steigende {C:attention}Joker{} steigen", + "als ein Grad-{C:attention}#1#{} Polynom", + "erhöhe Grad um {C:attention}#2#{}", + "am Ende der Runde", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} exkludiert)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Kachel", + text = { + "Chance von {C:green}#1# zu #2#{} um einen", + "{C:dark_edition}Lustigen {C:green}Ungewöhnlichen{} Joker", + "zu erstellen wenn eine Hand gespielt wird", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Füge ein {C:attention}zufälliges Siegel{} zu jeder Karte,", + "die in der {C:attention}letzten Hand{} der Runde gewertet wird", + }, + }, + j_cry_shrewd = { + name = "Kluger Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält" + } + }, + j_cry_silly = { + name = "Doofer Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_smallestm = { + name = "Winzig", + text = { + "Erstelle ein {C:cry_jolly}Doppel M", + "Tag wenn {C:attention}Poker Hand{}", + "ein {C:attention}#1#{} ist", + "{C:inactive,s:0.8}ok, also im wesentlichen bin ich richtig klen", + }, + }, + j_cry_soccer = { + name = "Einer für Alle", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker Slot", + "{C:attention}+#1#{} Booster Packer Slot", + "{C:attention}+#1#{} Handgröße", + "{C:attention}+#1#{} Verbrauchsgegenstand Slot", + "{C:attention}+#1#{} Karte im Shop", + }, + }, + j_cry_fleshpanopticon = { + name = "Fleischpanoptikum", + text = { + "{C:red}X#1#{} {C:attention}Boss Blind{} größe", + "Wenn eine {C:attention}Boss Blind{} besiegt wurde,", + "{C:red}zerstört sich selbst{}, und erstellt", + "eine {C:dark_edition}Negative{} {C:spectral}Portal{} Karte", + "{C:inactive,s:0.8}\"Ein Gefängnis... zum halten von... mir?\"" + }, + }, + j_cry_spaceglobe = { + name = "Himmelsglobus", + text = { + "Dieser Joker bekommt {X:chips,C:white}X#2#{} Chips", + "wenn {C:attention}poker hand #3#{} ist,", + "Hand ändert sich jede Runde", + "{C:inactive}(Momentan{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Erstellt eine {C:dark_edition}Negative{} Kopie", + "Eines Zufälligen {C:attention}Jokers{}", + "am Ende des {C:attention}Shops", + "{C:inactive,s:0.8}Kopiert keine anderen Speculo{}", + }, + }, + j_cry_spy = { + name = "Spion", + text = { + "{X:mult,C:white} X#2# {} Mult, {C:dark_edition}+1{C:attention} Joker{} slot", + "{C:inactive}Dieser #1# ist ein Spion!", + }, + }, + j_cry_stardust = { + name = "Sternstaub", + text = { + "{C:dark_edition}Polychrome{} Karten", + "geben {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "Dieser Joker zerstört eine", + "zufällige {C:planet}Planeten{} Karte", + "und erhält {X:dark_edition,C:white} ^#1# {} Mult", + "am Ende das {C:attention}Shops{}", + "{C:inactive}(Momentan {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_stronghold = { + name = "Die Festung", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält", + }, + }, + j_cry_subtle = { + name = "Subtiler Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Verdiene {C:money}$#3#{} am", + "Ende der Runde", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "Am Ende der Runde, erstelle", + "eine {C:attention}Kopie{} einer Zufälligen", + "Karte {C:attention}in der Hand{},", + "zerstöre alle anderen", + "{C:attention,s:0.8}Könige{s:0.8} von {C:hearts,s:0.8}Herz{s:0.8} sind priorisiert", + }, + }, + j_cry_swarm = { + name = "Der Schwarm", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Katalysator", + text = { + "Gleicht {C:chips}Chips{} und {C:mult}Mult{} aus", + "{C:inactive,s:0.8}Hey! Das habe ich schonmal gesehen!", + }, + }, + j_cry_tax_fraud = { + name = "Steuerbetrug", + text = { + "Am Ende der Runde", + "Erhalte {C:attention}$#1#{} für jeden {C:attention}Gemieteten Joker", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} Slots", + "Verdiene {C:money}$#2#{} am Ende der Runde", + }, + }, + j_cry_translucent = { + name = "Durchscheinender Joker", + text = { + "Verkaufe diese Karte um", + "eine {C:attention}Bananen Verderbliche{} Kopie", + "eines zufälligen {C:attention}Jokers{} zu erstellen", + "{s:0.8,C:inactive}(Egal ob der Joker kompatibel ist)", + }, + }, + j_cry_treacherous = { + name = "Tückischer Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_trick_or_treat = { + name = "Süßes oder Saures", + text = { + "Wenn {C:attention}verkauft{}:", + "Chance von {C:green}#1# zu #2#{} {C:attention}2{} {C:cry_candy}Süßigkeiten{} zu erstellen", + "Sonst erstelle einen {X:cry_cursed,C:white}Verfluchten{} Joker", + "{C:inactive}(Kann überfießen)" + } + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips wenn", + "gespielte Hand", + "einen {C:attention}#2#{} enthält" + } + }, + j_cry_triplet_rhythm = { + name = "Triolenrhythmus", + text = { + "{X:mult,C:white} X#1# {} Mult wenn gewertete Hand", + "{C:attention}exakt{} drei {C:attention}3{} enthält", + }, + }, + j_cry_unity = { + name = "The Einigkeit", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Pokerhände{} erhalten", + "{X:red,C:white} X#1# {} Mult und {X:blue,C:white} X#1# {} Chips", + "wenn ihr Level erhöht wird", + }, + }, + j_cry_unjust_dagger = { + name = "Ungerechter Dolch", + text = { + "Wenn {C:attention}Blind{} ausgewählt wird,", + "zerstöre linken Joker", + "und füge {C:attention}ein Fünftel{} des", + "Verkaufwertes als {X:mult,C:white} XMult {} hinzu", + "{C:inactive}(Momentan {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "Wenn eine Warscheinlichkeit", + "{C:green}erfolgreich{} auslöst,", + "erhällt dieser Joker {X:red,C:white}XMult{}", + "gleich der {C:attention}Warscheinlichkeit", + "{C:inactive}(Momentan {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "Dieser Joker erhält {C:money}$#1#{} {C:attention}Verkaufswert{}", + "wenn {C:attention}Pokerhand{} ein {C:attention}#2#{} enthält", + "Verkaufe diese Karte um einen", + "{C:dark_edition}Polychrome{} {C:attention}Lustigen Joker{} für", + "alle {C:money}$4{} {C:attention}Verkaufswert{} zu erstellen {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Spinner Joker", + text = { + "{C:red}+#1#{} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "Alle Joker geben", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "Alle Joker geben", + "{C:money}$#1#{} wenn ausgelöst", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "Dieser Joker bekommt", + "{C:mult}+#2#{} Mult wenn ein", + "{C:attention}Ass{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, oder {C:attention}8{}", + "gewertet wird", + "{C:inactive}(Momentan {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Löse jede gespielte {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} weitere(s) mal aus", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Rad der Hoffnung", + text = { + "Dieser Joker erhält", + "{X:mult,C:white} X#1# {} Mult wenn ein", + "{C:attention}Rad des Schicksals{} nicht erfolgreich auslöst", + "{C:inactive}(Momentan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "Die PEITCHE", + text = { + "Dieser Joker erhält {X:mult,C:white} X#1# {} Mult", + "wenn die {C:attention}Poker hand{} eine", + "{C:attention}2{} und {C:attention}7{} unterschiedlicher Farben enthält", + "{C:inactive}(Momentan {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_wrapped = { + name = "Eingewickelte Süßigkeit", + text = { + "Erstelle einen zufälligen {C:attention}Essen Joker{}", + "in {C:attention}#1#{} Runde(n)", + "{C:red,E:2}Zerstört sich selbst{}", + }, + }, + j_cry_wtf = { + name = "Die Hölle!?", + text = { + "{X:mult,C:white} X#1# {} Mult wenn", + "gespielte Hand", + "ein {C:attention}#2#{} enthält", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Aufwertung für", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "und {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Aufwertung für", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "und {C:attention}#3#{}", + }, + }, + c_cry_Kaikki = { + name = "Kaikki", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Aufwertung für", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "und {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutronenstern", + text = { + "Verbessere eine zufällige", + "Pokerhand um", + "{C:attention}1{} level für jeden", + "{C:attention}Neutronenstern{}", + "der in diesem Durchlauf", + "genutzt wurde", + "{C:inactive}(Momentan{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "Chance von {C:green}#1# zu #2#{} jede", + "{C:legendary,E:1}Pokerhand{}", + "um {C:attention}1{} Level aufzuwerten", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Aufwertung für", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "und {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Aufwertung für", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "und {C:attention}#3#{}", + }, + }, + c_cry_marsmoons = { + name = 'Phobos & Deimos', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){} Aufwertung für", + "{C:attention}#2#", + "{C:mult}+#3#{} Mult und", + "{C:chips}+#4#{} chips", + }, + }, + c_cry_void = { + name = 'Leere', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){} Aufwertung für", + "{C:attention}#2#", + "{C:mult}+#3#{} Mult und", + "{C:chips}+#4#{} chips", + }, + }, + c_cry_asteroidbelt = { + name = 'Asteroidengürtel', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){} Aufwertung für", + "{C:attention}#2#", + "{C:mult}+#3#{} Mult und", + "{C:chips}+#4#{} chips", + }, + }, + c_cry_universe = { + name = 'Das Universum in seiner verfickten Gesamtheit.', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){} Aufwertung für", + "{C:attention}#2#", + "{C:mult}+#3#{} Mult und", + "{C:chips}+#4#{} chips", + }, + }, + }, + Sleeve = { + sleeve_cry_bountiful_sleeve = { + name = "Größzügige Hülle", + text = { + "Ziehe immer 5 Karten nach", + "{C:attention}Gespielter Hand{} oder {C:attention}Abwurf{}", + }, + }, + sleeve_cry_ccd_sleeve = { + name = "CCD Hülle", + text = { + "Jede Karte ist auch", + "ein {C:attention}zufälliger{} Verbrauchsgegenstand", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Fließbandhülle", + text = { + "Joker können {C:attention}nicht{} bewegt werden", + "Am Anfang der Runde,", + "{C:attention}dupliziere{} den rechtesten Joker", + "und {C:attention}zerstöre{} den linkesten Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Kritische Hülle", + text = { + "Nach jeder gespielten Hand,", + "Chance von {C:green}#1# zu 4{} für {X:dark_edition,C:white} ^2 {} Mult", + "Chance von {C:green}#1# zu 8{} für {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Verschlüsselte Hülle", + text = { + "Starte mit einem {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "und einem {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Nur {C:cry_code}Code Karten{} erscheinen im Shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balancierte Hülle", + text = { + "Alle Karten haben die", + "{C:attention}selbe Chance{}", + "im Shop aufzutauchen,", + "beginne den Durchlauf mit", + "{C:attention,T:v_overstock_plus}Überbestand Plus", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unendliche Hülle", + text = { + "Du kannst {C:attention}unendlich{} viele", + "Karten auswählen.", + "{C:attention}+1{} Handgröße", + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Fehlgedruckte Hülle", + text = { + "Werte von Karten", + "und Pokerhänden", + "sind {C:attention}zufällig", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Eingelöste Hülle", + text = { + "Wenn ein {C:attention}Gutschein{} gekauft wird,", + "bekomme seine {C:attention}verbesserten Versionen", + }, + }, + sleeve_cry_spooky_sleeve = { + name = "Gruselige Hülle", + text = { + "Starte mit einem {C:eternal}Ewigen{} {C:attention,T:j_cry_chocolate_dice}Schokoladenwürfel", + "Nach jeder {C:attention}Ante{}, erstelle eine", + "{C:cry_candy}Süßigkeit{} oder einen {X:cry_cursed,C:white}Verfluchten{} Joker", + } + }, + sleeve_cry_wormhole_sleeve = { + name = "Wurmlochhülle", + text = { + "Beginne mit einem {C:cry_exotic}Exotischen{C:attention} Joker", + "Joker sind {C:attention}20X{} warscheinlicher", + "{C:dark_edition}Negativ{} zu sein", + "{C:attention}-2{} Joker-Slots", + }, + }, + sleeve_cry_legendary_sleeve = { + name = "Legendäre Hülle", + text = { + "Beginne mit einem {C:legendary}Legendären{C:legendary} Joker", + "Chance von {C:green}1 in 5{} einen weiteren zu erstellen", + "wenn Boss Blind besiegt wurde {C:inactive}(Muss Platz haben){}", + }, + }, + }, + Spectral = { + c_cry_analog = { + name = "Analog", + text = { + "Erstelle {C:attention}#1#{} Kopien eines", + "zufälligen {C:attention}Jokers{}, zerstöre", + "alle anderen Joker, {C:attention}+#2#{} Ante", + }, + }, + c_cry_gateway = { + name = "Portal", + text = { + "Erstelle einen zufälligen", + "{C:cry_exotic,E:1}Exotischen{C:attention} Joker{}, zerstöre", + "alle anderen Joker", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Füge einen zufälligen {C:attention}Verbrauchsgegenstand{}", + "als währen sie {C:dark_edition}Verstärkungen{}", + "zu allen Karten in der Hand hinzu", + }, + }, + c_cry_lock = { + name = "Schloss", + text = { + "Entferne {C:red}alle{} Sticker", + "von {C:red}allen{} Jokern,", + "dann füge {C:purple,E:1}Ewig{}", + "zu einem zufälligen {C:attention}Joker{} hinzu", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Erstelle eine Karte", + "{C:cry_code}deiner Wahl", + "{C:inactive,s:0.8}(Exotische Joker exkludiert)", + }, + }, + c_cry_replica = { + name = "Replik", + text = { + "Verwandle alle Karten", + "in der Hand", + "in eine {C:attention}zufällige{}", + "Karte in der Hand", + }, + }, + c_cry_source = { + name = "Quelle", + text = { + "Füge ein {C:cry_code}Grünes Siegel{}", + "zu {C:attention}#1#{} gewählten", + "Karte(n) hinzu", + }, + }, + c_cry_summoning = { + name = "Beschwörung", + text = { + "Erstelle einen zufälligen", + "{C:cry_epic}Epischen{} {C:joker}Joker{}, zerstöre", + "einen zufälligen {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Handel", + text = { + "{C:attention}Verliere{} einen zufälligen Gutschein,", + "bekomme {C:attention}2{} zufällige Gutscheine", + }, + }, + c_cry_typhoon = { + name = "Taifun", + text = { + "Füge ein {C:cry_azure}Azurblaues Siegel{}", + "zu {C:attention}#1#{} gewählten", + "Karte(n) hinzu", + }, + }, + c_cry_vacuum = { + name = "Vakuum", + text = { + "Entferne {C:red}alle {C:green}Modifikationen{}", + "von {C:red}allen{} Karten in der Hand,", + "Verdiene {C:money}$#1#{} pro entfernte {C:green}Modifikation{}", + "{C:inactive,s:0.7}(z.B. Verstärkungen, Siegel, Editionen)", + }, + }, + c_cry_white_hole = { + name = "Weißes Loch", + text = { + "{C:attention}Entferne{} alle Handlevel,", + "verbessere {C:legendary,E:1}meistgespielte{} Pokerhand", + "um {C:attention}3{} für jedes entfernte Level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pinker Einsatz", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Benötigte Punktzahl skaliert", + "schneller für jede {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brauner Einsatz", + colour = "Brown", + text = { + "Alle {C:attention}Sticker{} sind", + "gegenseitig Kompatibel", + }, + }, + stake_cry_yellow = { + name = "Gelber Einsatz", + colour = "Yellow", + text = { + "{C:attention}Sticker{} können auf", + "allen kaufbaren Gegenständen erscheinen", + }, + }, + stake_cry_jade = { + name = "Jade Einsatz", + colour = "Jade", + text = { + "Karten können mit {C:attention}Gesicht unten{} gezogen werden", + }, + }, + stake_cry_cyan = { + name = "Cyan Einsatz", + colour = "Cyan", + text = { + "{C:green}Ungewöhnliche{} und {C:red}Seltene{} Joker sind", + "seltener", + }, + }, + stake_cry_gray = { + name = "Grauer Einsatz", + colour = "Gray", + text = { + "Aktualisierungen erhöhen sich um {C:attention}$2{}", + }, + }, + stake_cry_crimson = { + name = "Karmesin Einsatz", + colour = "Crimson", + text = { + "Gutscheine füllen sich auf allen {C:attention}geraden{} Antes auf", + }, + }, + stake_cry_diamond = { + name = "Diamant Einsatz", + colour = "Diamond", + text = { + "Zum gewinnen must du Ante {C:attention}10{} besiegen", + }, + }, + stake_cry_amber = { + name = "Bernstein Einsatz", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Packet Slots", + }, + }, + stake_cry_bronze = { + name = "Bronze Einsatz", + colour = "Bronze", + text = { + "Gutscheine sind {C:attention}50%{} teurer", + }, + }, + stake_cry_quartz = { + name = "Quartz Einsatz", + colour = "Quartz", + text = { + "Joker können {C:attention}Angeheftet{} sein", + "{s:0.8,C:inactive}(Bleibt an der Position ganz links angeheftet){}", + }, + }, + stake_cry_ruby = { + name = "Rubin Einsatz", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds können", + "{C:attention}Boss{} Blinds werden", + }, + }, + stake_cry_glass = { + name = "Glass Einsatz", + colour = "Glass", + text = { + "Karten können {C:attention}zerbrechen{} wenn sie gewertet werden", + }, + }, + stake_cry_sapphire = { + name = "Saphir Einsatz", + colour = "Sapphire", + text = { + "Verliere {C:attention}25%{} deines momentanen Geldes", + "am Ende der Ante", + "{s:0.8,C:inactive}(Maximal $10){}", + }, + }, + stake_cry_emerald = { + name = "Smaragd Einsatz", + colour = "Emerald", + text = { + "Karten, Packete und Gutscheine", + "können mit {C:attention}Gesicht unten{} sein", + "{s:0.8,C:inactive}(Nicht sichtbar bis gekauft){}", + }, + }, + stake_cry_platinum = { + name = "Platin Einsatz", + colour = "Platinum", + text = { + "Small Blinds sind {C:attention}entfernt{}", + }, + }, + stake_cry_twilight = { + name = "Zwielichtiger Einsatz", + colour = "Twilight", + text = { + "Karten können {C:attention}Banane{} sein", + "{s:0.8,C:inactive}(Chance von 1 zu 10, dass die Karte am Ende der Runde zerstört wird){}", + }, + }, + stake_cry_verdant = { + name = "Grassgrüner Einsatz", + colour = "Verdant", + text = { + "Benötigte Punktzahl skaliert", + "schneller für jede {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Glühender Einsatz", + colour = "Ember", + text = { + "Alle Gegenstände geben beim verkaufen kein Geld", + }, + }, + stake_cry_dawn = { + name = "Dämmernder Einsatz", + colour = "Dawn", + text = { + "Tarot- und Geisterkarten beinflussen {C:attention}1", + "Karte weniger", + "{s:0.8,C:inactive}(Minimal 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizont Einsatz", + colour = "Horizon", + text = { + "Wenn Blind asgewählt ist, füge eine", + "{C:attention}zufällige Karte{} zum Deck hinzu", + }, + }, + stake_cry_blossom = { + name = "Blühender Einsatz", + colour = "Blossom", + text = { + "{C:attention}Letzte{} Boss Blinds können nun", + "in {C:attention}allen{} Antes auftauchen", + }, + }, + stake_cry_azure = { + name = "Azurblauer Einsatz", + colour = "Azure", + text = { + "Werte auf Jokern sind", + "um {C:attention}20%{} reduziert", + }, + }, + stake_cry_ascendant = { + name = "Aufgestiegener Einsatz", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop Slots", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral-Tag", + text = { + "Nächster Basisedition", + "Joker ist kostenlos und", + "wird {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Bananen-Tag", + text = { + "Erstellt {C:attention}#1#", + "{C:inactive}(Muss Platz haben){}", + }, + }, + tag_cry_bettertop_up = { + name = "Besserer Auffüllungs-Tag", + text = { + "Erstellt bis zu {C:attention}#1#", + "{C:green}Ungewöhnliche{} Joker", + "{C:inactive}(Muss Platz haben){}", + }, + }, + tag_cry_better_voucher = { + name = "Golder Gutschein-Tag", + text = { + "Fügt einen Level {C:attention}#1#{} Gutschein", + "zum Shop hinzu", + }, + }, + tag_cry_blur = { + name = "Verschwommener Tag", + text = { + "Nächster Basisedition", + "Joker ist kostenlos und", + "wird {C:dark_edition}Verschwommen{}", + }, + }, + tag_cry_booster = { + name = "Booster-Tag", + text = { + "Nächstes {C:cry_code}Booster Packet{} hat", + "{C:attention}doppelt so viele{} Karten und", + "{C:attention}doppelt so viele{} Auswahlen", + }, + }, + tag_cry_bundle = { + name = "Bündel-Tag", + text = { + "Erstelle einen {C:attention}Standard-Tag{}, {C:tarot}Charme-Tag{},", + "{C:attention}Clown-Tag{}, und {C:planet}Meteor-Tag", + }, + }, + tag_cry_cat = { + name = "Katzen-Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Konsolen-Tag", + text = { + "Gibt ein kostenloses", + "{C:cry_code}Program Packet", + }, + }, + tag_cry_double_m = { + name = "Doppel M Tag", + text = { + "Shop hat einen", + "{C:dark_edition}Lustigen {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Ermächtigter Tag", + text = { + "Gibt ein kostenloses {C:spectral}Geisterpacket", + "mit {C:legendary,E:1}Die Seele{} und {C:cry_exotic,E:1}Portal{}", + }, + }, + tag_cry_epic = { + name = "Epischer Tag", + text = { + "Shop hat einen", + "{C:cry_epic}Epischen Joker{} mit halben Preis", + }, + }, + tag_cry_gambler = { + name = "Zocker Tag", + text = { + "Chance von {C:green}#1# zu #2#{} einen", + "{C:cry_exotic,E:1}Ermächtigten Tag{} zu erstellen", + }, + }, + tag_cry_glass = { + name = "Zerbrechlicher Tag", + text = { + "Nächster Basisedition", + "Joker ist kostenlos und", + "wird {C:dark_edition}Zerbrechlich{}", + }, + }, + tag_cry_glitched = { + name = "Fehlerhafter Tag", + text = { + "Nächster Basisedition", + "Joker ist kostenlos und", + "wird {C:dark_edition}Fehlerhaft{}", + }, + }, + tag_cry_gold = { + name = "Goldener Tag", + text = { + "Nächster Basisedition", + "Joker ist kostenlos und", + "wird {C:dark_edition}Gold{}", + }, + }, + tag_cry_gourmand = { + name = "Vielfraß Tag", + text = { + "Shop hat einen Kostenlosen", + "{C:attention}Essen Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gibt ein Kostenloses", + "{C:cry_ascendant}Meme Packet", + }, + }, + tag_cry_m = { + name = "Lustiger Tag", + text = { + "Nächster Basisedition", + "Joker ist kostenlos und", + "wird {C:dark_edition}Lustig{}", + }, + }, + tag_cry_memory = { + name = "Erinnerungs Tag", + text = { + "Erstelle {C:attention}#1#{} Kopien des", + "als letztes genutzen {C:attention}Tag{}", + "in diesem Durchlauf", + "{s:0.8,C:inactive}Kopierende Tags exkludiert", + "{s:0.8,C:inactive}Momentan: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaik Tag", + text = { + "Nächster Basisedition", + "Joker ist kostenlos und", + "wird {C:dark_edition}Mosaik{}", + }, + }, + tag_cry_oversat = { + name = "Übersättigter Tag", + text = { + "Nächster Basisedition", + "Joker ist kostenlos und", + "wird {C:dark_edition}Übersättigt{}", + }, + }, + tag_cry_quadruple = { + name = "Vierfach Tag", + text = { + "Gibt {C:attention}#1#{} Kopien des", + "als nächstes erhaltenen {C:attention}Tags", + "{s:0.8,C:inactive}Kopierende Tags exkludiert", + }, + }, + tag_cry_quintuple = { + name = "Fünffach Tag", + text = { + "Gibt {C:attention}#1#{} Kopien des", + "als nächstes erhaltenen {C:attention}Tags", + "{s:0.8,C:inactive}Kopierende Tags exkludiert", + }, + }, + tag_cry_rework = { + name = "Überarbeitungs-Tag", + text = { + "Shop hat einen", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schema-Tag", + text = { + "Shop hat einen", + "{C:attention}Brainstorming", + }, + }, + tag_cry_scope = { + name = "Scope-Tag", + text = { + "{C:attention}+#1# {C:blue}Hände{} und", + "{C:red}Abwürfe{} nächste Runde", + }, + }, + tag_cry_triple = { + name = "Dreifach Tag", + text = { + "Gibt {C:attention}#1#{} Kopien des", + "als nächstes erhaltenen {C:attention}Tags", + "{s:0.8,C:inactive}Kopierende Tags exkludiert", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "Der Automat", + text = { + "Erstelle bis zu {C:attention}#1#", + "zufällige {C:cry_code}Code{} Karte", + "{C:inactive}(Muss Platz haben)", + }, + }, + c_cry_eclipse = { + name = "Die Finsterniss", + text = { + "Verstärkt {C:attention}#1#{} gewählte Karte", + "in eine {C:attention}Echo Karte", + }, + }, + c_cry_meld = { + name = "Verschmelzen", + text = { + "Wähle einen {C:attention}Joker{} oder eine", + "{C:attention}Spielkarte{} welche", + "{C:dark_edition}Doppelseitig{} wird", + }, + }, + c_cry_theblessing = { + name = "Der Segen", + text = { + "Erstellt {C:attention}1{}", + "zufälligen {C:attention}Verbrauchsgegenstand{}", + "{C:inactive}(Muss Platz haben){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Astroglyphe", + text = { + "Setzt Ante zu {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Lehre Leinwand", + text = { + "{C:attention}+#1#{} Handgröße", + }, + }, + v_cry_clone_machine = { + name = "Klonmaschine", + text = { + "Doppeltags werden", + "{C:attention}Fünffach-Tags{} und", + "erscheinen {C:attention}4X{} öfter", + }, + }, + v_cry_command_prompt = { + name = "Eingabeaufforderung", + text = { + "{C:cry_code}Code{} Karten", + "können im", + "{C:attention}shop{} auftauchen", + }, + }, + v_cry_copies = { + name = "Kopien", + text = { + "Doppeltags werden", + "{C:attention}Dreifach-Tags{} und", + "erscheinen {C:attention}2X{} öfter", + }, + }, + v_cry_curate = { + name = "Kurat", + text = { + "Alle Karten", + "erscheinen mit", + "einer {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Geschicklichkeit", + text = { + "Bekomme permanent", + "{C:blue}+#1#{} Hand/Hände", + "pro Runde", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "Nach jeder runde,", + "{X:dark_edition,C:white} X1.5 {} zu allen Werten", + "auf der Rückseite von", + "{C:dark_edition}Doppelseitigen{} Karten" + }, + }, + v_cry_double_slit = { + name = "Doppelschlitz", + text = { + "{C:attention}Verschmelzen{} kann im", + "Shop und in", + "Arkana Packeten auftauchen", + }, + }, + v_cry_double_vision = { + name = "Doppelsicht", + text = { + "{C:dark_edition}Doppelseitige{} Karten erscheinen", + "{C:attention}4X{} öfter", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker Slot(s)", + }, + }, + v_cry_massproduct = { + name = "Massenproduktion", + text = { + "Alle Karten und Parkete", + "im Shop kosten {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Geld Bohnenranke", + text = { + "Erhöhe Obergrenze", + "für Zinsen", + "zu {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multibestand", + text = { + "{C:attention}+#1#{} Karten Slot(s) und", + "{C:attention}+#1#{} Booster Packet Slot(s)", + "im Shop", + }, + }, + v_cry_pacclimator = { + name = "Planetenakklimatisierung", + text = { + "{C:planet}Planet{} Karten erscheinen", + "{C:attention}X#1#{} öfter", + "im Shop.", + "Alle zukünftigen {C:planet}Planeten{}", + "Karten sind {C:green}kostenlos{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Löse{} alle M Joker", + "für jedes Paar in", + "der gespielten Hand", + "{C:attention}erneut aus" + }, + }, + v_cry_pairing = { + name = "Paarung", + text = { + "{C:attention}Löse{} alle M Joker {C:attention}erneut aus", + "wenn gespielte Hand ein {C:attention}Paar{} ist", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computer", + text = { + "{C:cry_code}Code{} Karten können", + "{C:dark_edition}Negativ{} erscheinen", + }, + }, + v_cry_repair_man = { + name = "Reparaturmann", + text = { + "{C:attention}Löse{} alle M Joker {C:attention}erneut aus", + "wenn gespielte Hand ein {C:attention}Paar{} enthält", + }, + }, + v_cry_rerollexchange = { + name = "Aktualisierungsaustausch", + text = { + "Alle Aktualisierungen", + "kosten {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satelitenverbindung", + text = { + "{C:cry_code}Code{} Karten können", + "in allen {C:attention}Himmelpacketen{}", + "erscheinen", + }, + }, + v_cry_scope = { + name = "Galaktischer Umfang", + text = { + "Erstelle die {C:planet}Planeten", + "Karte für die gespielte", + "{C:attention}Pokerhand{}", + "{C:inactive}(Muss Platz haben){}", + }, + }, + v_cry_tacclimator = { + name = "Tarotakklimatisierung", + text = { + "{C:tarot}Tarot{} Karten erscheinen", + "{C:attention}X#1#{} öfter", + "im Shop.", + "Alle zukünftigen {C:tarot}Tarot{}", + "Karten sind {C:green}kostenlos{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Drucker", + text = { + "Doppeltags werden", + "{C:attention}Vierfach-Tags{} und", + "erscheinen {C:attention}3X{} öfter", + }, + }, + v_cry_threers = { + name = "Die 3 Rs", + text = { + "Bekomme permanent", + "{C:red}+#1#{} Abwurf/Abwürfe", + "pro Runde", + }, + }, + v_cry_stickyhand = { + name = "Klebrige Hand", + text = { + "{C:attention}+#1#{} Karten auswahl limit", + }, + }, + v_cry_grapplinghook = { + name = "Enterhaken", + text = { + "{C:attention}+#1#{} Karten auswahl limit", + "{C:inactive,s:0.7}NOTE: Hiermit kannst du mehr{}", + "{C:inactive,s:0.7}machen als du denkst{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperraum-Halteleine", + text = { + "{C:attention}+#1#{} Karten", + "auswahl limit", + "{C:inactive,s:0.7}NOTE: Bekommt später noch{}", + "{C:inactive,s:0.7}mehr funktionen{}", + }, + }, + }, + Other = { + banana = { + name = "Banane", + text = { + "Chance von {C:green}#1# zu #2#{} jede Runde", + "zerstört zu werden", + }, + }, + cry_rigged = { + name = "Manipuliert", + text = { + "Alle {C:cry_code}aufgelisteten{} Warscheinlichkeiten", + "sind {C:cry_code}garantiert", + }, + }, + cry_hooked = { + name = "Verhackt", + text = { + "Wenn dieser Joker {C:cry_code}ausgelöst{} wird,", + "löse {C:cry_code}#1#{} aus", + }, + }, + cry_flickering = { + name = "Flackernd", + text = { + "Zerstört nach", + "{C:attention}#1#{} Auslösungen", + "{C:inactive}({C:attention}#2#{C:inactive} übrig)" + }, + }, + cry_flickering_desc = { --von Schokowürfel genutzt + name = "Flackernd", + text = { + "Zerstört nach", + "{C:attention}#1#{} Auslösungen", + }, + }, + cry_possessed = { + name = "Besessen", + text = { + "{C:attention}Deaktiviert{} Effekte und", + "{C:attention}kehrt sie um{} wenn möglich", + "Zerstört sich mit {C:attention}Geist" + }, + }, + --todo? add candy jokers to list + food_jokers = { + name = "Essen Joker", + text = { + "{s:0.8}Gros Michel, Ei, Eiscreme, Cavendish,", + "{s:0.8}Schwarze Bohne, Diät-Cola, Popcorn, Ramen,", + "{s:0.8}Selters, Saure Gurke, Chilischote, Karamel,", + "{s:0.8}Nostalgische Süßigkeit, Fast Food M, etc.", + }, + }, + v_cry_choco0 = { + name = "", + text = { + "Details eines aktiven", + "{C:cry_ascendant,E:1}Events{} werden hier erscheinen" + } + }, + ev_cry_choco1 = { + name = "1: Bessetzung", + text = { + "{C:attention}Joker{} und Spielkarten haben eine", + "Chance von {C:green}1 zu 3{} Flackernd zu bekommen", + "Erstelle einen {C:attention}Geist", + "{C:inactive,s:0.7}Du wurdest von einem Geist besessen und dein", + "{C:inactive,s:0.7}Bewustsein flackert ein und aus." + } + }, + ev_cry_choco2 = { + name = "2: Geisterhaus", + text = { + "Eine {C:attention}Blind{} überspringen bringt nichts", + "Eine {C:attention}Aktualisierung{} pro Shop erlaubt", + "{C:attention}Gutschein{} Preise sind verdoppelt", + "{C:inactive,s:0.7}Gruselige Geister haben Kontrolle übernommen! Berühre nichts", + "{C:inactive,s:0.7}und fliehe so schnell du kannst!", + } + }, + ev_cry_choco3 = { + name = "3: Hexengebräu", + text = { + "Erstelle 3 {C:attention}Tränke", + "Benutze einen am Ende der {C:attention}Small Blind{},", + "oder {C:attention}alle{} werden diese {C:attention}Ante{} angewendet", + "{C:inactive,s:0.7}Du wurderst von einer Hexe entführt!", + "{C:inactive,s:0.7}Sie bietet dir drei Tränke an und beobachtet dich genau.", + "{C:inactive,s:0.7}Wähle einen oder sie macht die Entscheidung für dich.", + } + }, + ev_cry_choco4 = { + name = "4: Mondabgrund", + text = { + "Gespielte Karten haben eine Chance von {C:green}1 zu 4{}", + "sich in eine zufällige {C:club}Kreuz{} Bildkarte zu verwandeln", + "Dividiere {C:attention}Mult{} durch die Ankahl gespielter Bildkarten", + "{C:inactive,s:0.7}Selbst ein Mann reines Herzes", + "{C:inactive,s:0.7}der Nachts immer betet..." + } + }, + ev_cry_choco5 = { + name = "5: Blutsauger", + text = { + "Entferne {C:attention}Verbesserungen{} von allen gespielten Karten", + "Chance von {C:green}1 zu 3{} {C:heart}Herz{} und", + "{C:diamond}Karo{} Karten zu zerstören", + "{C:inactive,s:0.7}Achte dich in der Nacht, denn", + "{C:inactive,s:0.7,E:1}die in den Schatten{C:inactive,s:0.7} suchen eine Stillung für ihren Durst..." + } + }, + ev_cry_choco6 = { + name = "6: Bitte nimm eins", + text = { + "Am {C:attention}Ende der Runde{}, öffne ein", + "zufälliges {C:attention}Booster{} Packet", + "{C:inactive,s:0.7}Als du die Straße hinunter läufst, siehst du eine", + "{C:inactive,s:0.7}Box mit vielen Booster Packeten. Warum nicht eins nehmen?" + } + }, + ev_cry_choco7 = { + name = "7: Festliche Atmosphäre", + text = { + "Erstelle 3 {C:attention}Süßes oder Saures{} und 1 {C:attention}Süßigkeitenkorb", + "Shops haben jede Runde ein {C:attention}Süßes oder Saures{}", + "{C:cry_candy}Süßigkeiten{} geben {C:money}$3{} wenn erhalten", + "{C:inactive,s:0.7}Die ganze Nachbarschaft ist gruselig dekoriert,", + "{C:inactive,s:0.7}komm und hab Spaß in der Festlichen Atmosphäre!" + } + }, + ev_cry_choco8 = { + name = "8: Candy Rain", + text = { + "When {C:attention}Blind{} defeated, obtain 1 {C:cry_candy}Candy{}", + "per hand remaining; Obtain a {C:attention}Food Joker{}", + "when a {C:cry_candy}Candy{} is generated", + "{C:inactive,s:0.7}Candies rain from the sky! Quick,", + "{C:inactive,s:0.7,E:1}grab as much as you can!" + } + }, + ev_cry_choco9 = { + name = "9: Süßigkeitenregen", + text = { + "Erhalte {C:money}$20", + "Alles verdiente {C:money}Geld{} ist {C:attention}verdoppelt", + "{C:inactive,s:0.7}Der Geist eines lang verstörbenen Verwanten", + "{C:inactive,s:0.7}besucht dich in der Mitte der Nacht!", + "{C:inactive,s:0.7}Ohne ein Wort gibt er dir ein einen Sack voll Geld,", + "{C:inactive,s:0.7}gibt dir ein freundliches Lächeln, und winkt als er wieder verschwindet.", + } + }, + ev_cry_choco10 = { + name = "10: Verehrte Antiquität", + text = { + "Ein {C:legendary}Legendärer{} {C:attention}Joker{} erscheint", + "in dem {C:attention}Gutschein{} slot für {C:money}$50", + "Nur kaufbar als {C:attention}letzter{} Gegenstand im Shop", + "{C:inactive,s:0.7}Du hast die Aufmerksamkeit des Geistes eines Relikts,", + "{C:inactive,s:0.7}bekommen, aber er wird nicht leicht zu bändigen sein.", + } + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} ist von den standard ({C:attention,s:0.7}HTTPS Modul{s:0.7} deaktiviert)", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Ewig", + text = { + "Alle Karten im Packet", + "sind {C:attention}Ewig{}", + }, + }, + cry_perishable_booster = { + name = "Verderblich", + text = { + "Alle Karten im Packet", + "sind {C:attention}Verderblich{}", + }, + }, + cry_rental_booster = { + name = "Gemietet", + text = { + "Alle Karten im Packet", + "sind {C:attention}Gemietet{}", + }, + }, + cry_pinned_booster = { + name = "Angeheftet", + text = { + "Alle Karten im Packet", + "sind {C:attention}Angeheftet{}", + }, + }, + cry_banana_booster = { + name = "Banane", + text = { + "Alle Karten im Packet", + "sind {C:attention}Banane{}", + }, + }, + cry_eternal_voucher = { + name = "Ewig", + text = { + "Can nicht gehandelt werden", + }, + }, + cry_perishable_voucher = { + name = "Verderblich", + text = { + "Geschwächt nach", + "{C:attention}#1#{} Runden", + "{C:inactive}({C:attention}#2#{C:inactive} übrig)", + }, + }, + cry_rental_voucher = { + name = "Gemietet", + text = { + "Verliere {C:money}$#1#{} am", + "Ende der Runde", + }, + }, + cry_pinned_voucher = { + name = "Angeheftet", + text = { + "Bleibt im Shop", + "bis eingelöst", + }, + }, + cry_banana_voucher = { + name = "Banane", + text = { + "Chance von {C:green}#1# zu #2#{} die", + "Einlösung am Ende der Runde aufzuheben", + }, + }, + cry_perishable_consumeable = { + name = "Verderblich", + text = { + "Geschwächt am", + "Ende der Runde", + }, + }, + cry_rental_consumeable = { + name = "Gemietet", + text = { + "Verliere {C:money}$#1#{} am Ende der", + "Runde und bei Nutzung", + }, + }, + cry_pinned_consumeable = { + name = "Angeheftet", + text = { + "Du kannst keine", + "nicht-{C:attention}Angeheftetn{} Verbrauchsgegenstände nutzen", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "Chance von {C:green}#1# zu #2#{} beim nutzen", + "nichts zu tuhen", + }, + }, + p_cry_code_normal_1 = { + name = "Program Packet", + text = { + "Wähle {C:attention}#1#{} aus bis zu", + "{C:attention}#2#{C:cry_code} Code{} Karten", + }, + }, + p_cry_code_normal_2 = { + name = "Program Packet", + text = { + "Wähle {C:attention}#1#{} aus bis zu", + "{C:attention}#2#{C:cry_code} Code{} Karten", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Packet", + text = { + "Wähle {C:attention}#1#{} aus bis zu", + "{C:attention}#2#{C:cry_code} Code{} Karten", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Packet", + text = { + "Wähle {C:attention}#1#{} aus bis zu", + "{C:attention}#2#{C:cry_code} Code{} Karten", + }, + }, + p_cry_empowered = { + name = "Geister Packet [Ermächtigter Tag]", + text = { + "Wähle {C:attention}#1#{} von bis zu", + "{C:attention}#2#{C:spectral} Geister{} Karten", + "{s:0.8,C:inactive}(Erstellt von Ermächtigten Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Packet", + text = { + "Wähle {C:attention}#1#{} von bis zu", + "{C:attention}#2# Meme Jokern{}", + }, + }, + p_cry_meme_two = { + name = "Meme Packet", + text = { + "Wähle {C:attention}#1#{} von bis zu", + "{C:attention}#2# Meme Jokern{}", + }, + }, + p_cry_meme_three = { + name = "Meme Packet", + text = { + "Wähle {C:attention}#1#{} von bis zu", + "{C:attention}#2# Meme Jokern{}", + }, + }, + undiscovered_code = { + name = "Nicht entdeckt", + text = { + "Kaufe oder nutze", + "diese Karte in einem", + "Durchlauf ohne Code", + "um zu lernen was es tuht" + } + }, + undiscovered_unique = { + name = "Nicht entdeckt", + text = { + "Kaufe oder nutze", + "diese Karte in einem", + "Durchlauf ohne Code", + "um zu lernen was es tuht" + } + }, + cry_green_seal = { + name = "Grünen Siegel", + text = { + "Erstellt eine {C:cry_code}Code{} Karte", + "wenn gespielt aber nicht gewertet", + "{C:inactive}(Muss Platz haben)", + }, + }, + cry_azure_seal = { + name = "Azurblaues Siegel", + text = { + "Erstelle {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planeten{} für gespielte", + "{C:attention}Pokerhand{}, dann", + "{C:red}zerstöre{} diese Karte", + }, + }, + Unique = { + c_cry_potion = { + name = "Trank", + text = { + "Gibt einen unbekannten", + "{C:attention}Fluch{} wenn genutz", + "{C:inactive,s:0.7}Von Schokoladenwürfel erhalten" + } + } + } + }, + }, + misc = { + poker_hands = { + ['cry_Bulwark'] = "Bollwerk", + ['cry_Clusterfuck'] = "Clusterfuck", + ['cry_UltPair'] = "Ultimatives Paar", + ['cry_WholeDeck'] = "Das ganze verfickte Deck", + }, + poker_hand_descriptions = { + ['cry_Bulwark'] = { + '5 ranglose, farbenlose Karten zusammen gespielt', + }, + ['cry_Clusterfuck'] = { + 'Mindestens 8 Karten, welche nicht', + 'Paar, Flush oder Straße enthalten' + }, + ['cry_UltPair'] = { + 'Zwei zwei Paare, bei denen jedes', + 'von ihnen von einer Farbe ist,', + 'was insgesammt 2 Farben ergibt.' + }, + ['cry_WholeDeck'] = { + 'Eine Hand, die jede Karte in', + 'in einem 52-Karten Deck enthält.', + 'Bist du verrückt?', + }, + }, + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Gesetzlich blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Durchbreche Unendlichkeit", + ach_cry_cryptid_the_cryptid = "Cryptid das Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Immobilienmakler", + ach_cry_jokes_on_you = "Der Witz geht auf dich, Kumpel!", + ach_cry_niw_uoy = "!nennoweg tsah uD", + ach_cry_now_the_fun_begins = "Hier fängt der Spaß erst richtig an.", + ach_cry_patience_virtue = "Geduld ist eine Tugend", + ach_cry_perfectly_balanced = "Perfekt ausgeglichen", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Stau", + ach_cry_ult_full_skip = "Ultimativer Full Skip", + ach_cry_used_crash = "Wir haben dich gewarnt", + ach_cry_what_have_you_done = "WAS HAST DU GETAHN?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Bekomme einen verschwommen Verschwommen Joker", + ach_cry_bullet_hell = "Habe 15 AP Joker", + ach_cry_break_infinity = "Bekomme 1.79e308 Chips in einer einzigen Hand", + ach_cry_cryptid_the_cryptid = "Benutze Cryptid auf Cryptid", + ach_cry_exodia = "Habe 5 Exotische Joker", + ach_cry_freak_house = "Spiele ein Flush House aus 6 und 9 von Herz während du Nice hast", + ach_cry_googol_play_pass = "Manipuliere eine Googol Play Karte", + ach_cry_haxxor = "Benutze einen cheat code", + ach_cry_home_realtor = "Aktiviere Fröhliches Haus vor Ante 8 (ohne DoE/Antimaterie)", + ach_cry_jokes_on_you = "Löse den Effect von dem Witz in Ante 1 aus und gewinne den Durchlauf trotzdem.", + ach_cry_niw_uoy = "Erreiche Ante -8", + ach_cry_now_the_fun_begins = "Bekomme Leinwand", + ach_cry_patience_virtue = "Warte bei der Lavendelschleife für 2 minuten bevor du sie besiegst", + ach_cry_perfectly_balanced = "Besiege das Sehr Faire Deck auf Aufgestiegenen Einsatz", + ach_cry_pull_request = "Lass ://COMMIT den Joker erstellen den es zerstört hat.", + ach_cry_traffic_jam = "Besiege alle Rush Hour Herausforderungen", + ach_cry_ult_full_skip = "Gewinne in einer Runde", + ach_cry_used_crash = "Benutze ://CRASH", + ach_cry_what_have_you_done = "Lösche oder opfere einen Exotischen Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dolchkrieg", + c_cry_onlycard = "Solo Karte", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Stickerbogen", + c_cry_sticker_sheet_plus = "Stickerbogen+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Musik", + cry_set_enable_features = "Wähle features zum Aktivieren (wird beim Neustart angewendet):", + cry_feat_achievements = "Erfolge", + ["cry_feat_antimatter deck"] = "Antimaterie Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Herausforderungen", + ["cry_feat_code cards"] = "Code Karten", + ["cry_feat_misc. decks"] = "Verschiedene Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechaniken", + ["cry_feat_enhanced decks"] = "Verstärkte Karten", + ["cry_feat_epic jokers"] = "Epische Joker", + ["cry_feat_exotic jokers"] = "Exotische Joker", + ["cry_feat_m jokers"] = "M Joker", + cry_feat_menu = "Eigene Main Menu", + ["cry_feat_misc."] = "Verschiedenes", + ["cry_feat_misc. jokers"] = "Verschiedene Joker", + cry_feat_planets = "Planeten", + cry_feat_jokerdisplay = "JokerDisplay (Macht nichts)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Hüllen", + cry_feat_spectrals = "Geisterkarten", + cry_feat_spooky = "Gruseliges Update", + ["cry_feat_more stakes"] = "Einsätze", + cry_feat_vouchers = "Gutscheine", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Packet", + k_cry_meme_pack = "Meme Packet", + + cry_critical_hit_ex = "Kritischer Treffer!", + cry_critical_miss_ex = "Kritisch verfehlt!", + + cry_potion1 = "-1 zu allen Handleveln", + cry_potion2 = "X1.15 Blindgröße", + cry_potion3 = "-1 Hand und Abwurf", + + cry_debuff_oldhouse = "Keine Full Houses", + cry_debuff_oldarm = "Must 4 oder weniger Karten spielen", + cry_debuff_oldpillar = "Keine Straßen", + cry_debuff_oldflint = "Keine Flushes", + cry_debuff_oldmark = "Keine Hände die ein Paar enthalten", + cry_debuff_obsidian_orb = "Hat die Fähigkeiten aller besiegten Bosse", + + k_code = "Code", + k_unique = "Einzigartig", + b_code_cards = "Code Karten", + b_unique_cards = "Einzigartige Karten", + b_pull = "PULL", + cry_hooked_ex = "Verhackt!", + k_end_blind = "Beende Blind", + + cry_code_rank = "RANG EINGEBEN", + cry_code_enh = "VERSTÄRKUNG EINGEBEN", + cry_code_hand = "POKERHAND EINGEBEN", + cry_code_enter_card = "KARTE EINGEBEN", + cry_code_apply = "ANWENDEN", + cry_code_apply_previous = "VORHERIGES ANWENDEN", + cry_code_exploit = "HACKEN", + cry_code_exploit_previous = "VORHERIGES HACKEN", + cry_code_create = "ERSTELLEN", + cry_code_create_previous = "VERHERIGES ERSTELLEN", + cry_code_execute = "AUSFÜHREN", + cry_code_cancel = "ABBRECHEN", + + b_flip = "UMDREHEN", + b_merge = "KOMBINIEREN", + + cry_hand_bulwark = "Bollwerk", + cry_hand_clusterfuck = "Clusterfuck", + cry_hand_ultpair = "Ultimatives Paar", + + cry_again_q = "Nochmal?", + cry_curse = "Verflucht", + cry_curse_ex = "Verflucht!", + cry_sobbing = "Hilf mir...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_good_luck_ex = "Viel Glück!", + cry_sus_ex = "Verräter!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Runde", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "Keine Auslösungen übrig!", + cry_unredeemed = "Einlöung aufgehoben...", + cry_active = "Aktiv", + cry_inactive = "Inaktiv", + + k_disable_music = "Musik deaktivieren", + + cry_epic = "Episch", + cry_exotic = "Exotisch", + cry_candy = "Süßigkeit", + cry_cursed = "Verflucht", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Copyright Anmerkung", + cry_notif_jimball_d1 = "Jimball spielt den Song \"Funkytown\",", + cry_notif_jimball_d2 = "welcher urheberrechtlich geschützt ist und nicht", + cry_notif_jimball_d3 = "für Streams und Videos genutzt werden kann.", + }, + labels = { + food_jokers = "Essen Joker", + banana = "Banane", + code = "Code", + unique = "Einzigartig", + cry_rigged = "Manipuliert", + cry_hooked = "Verhackt", + cry_flickering = "Flackernd", + cry_possessed = "Besezt", + + cry_green_seal = "Grünes Siegel", + cry_azure_seal = "Azurblaues Siegel", + + cry_astral = "Astral", + cry_blur = "Verschwommen", + cry_double_sided = "Doppelseitig", + cry_glass = "Zerbrechlich", + cry_glitched = "Fehlerhaft", + cry_gold = "Golden", + cry_m = "Lustig", + cry_mosaic = "Mosaik", + cry_noisy = "Geräuchvoll", + cry_oversat = "Übersättigt", + + cry_epic = "Episch", + cry_exotic = "Exotisch", + cry_candy = "Süßigkeit", + cry_cursed = "Verflucht", + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Momentan {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Momentan {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Momentan {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Momentan {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Momentan {C:attention}+#1#{C:inactive} Handgröße)" }, + money = { "{C:inactive}(Momentan {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Erstelle {C:attention}#2# Joker{}" }, + make_tarot = { "Erstelle {C:attention}#2#{C:tarot} Tarot{} Karte" }, + make_planet = { "Erstelle {C:attention}#2#{C:planet} Planet{} Karte" }, + make_spectral = { "Erstelle {C:attention}#2#{C:spectral} Spectral{} Karte" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "wenn ein {C:attention}Booster{} geöffnet wird" }, + buying_card = { "wenn eine Karte gekauft wird" }, + selling_self = { "wenn diese Karte verkauft wird" }, + selling_card = { "wenn eine Karte verkauft wird" }, + reroll_shop = { "beim Aktualisieren" }, + ending_shop = { "am Ende des {C:attention}Shops{}" }, + skip_blind = { "wenn ein {C:attention}Blind{} übersprungen wird" }, + skipping_booster = { "wenn ein {C:attention}Booster Packet{} übersprungen wird" }, + playing_card_added = { "wenn eine {C:attention}Spielkarte{} zum Deck hinzugefügt wird" }, + first_hand_drawn = { "wenn die Runde beginnt" }, + setting_blind = { "wenn {C:attention}Blind{} ausgewählt wird" }, + remove_playing_cards = { "wenn eine Karte zerstört wird" }, + using_consumeable = { "wenn ein {C:attention}Verbrauchsgegenstand{} genutzt wird" }, + debuffed_hand = { "wenn gespielte {C:attention}Hand{} nicht erlaubt ist" }, + pre_discard = { "vor jedem Abwurf" }, + discard = { "für jede Abgewurfene Karte" }, + end_of_round = { "am Ende der {C:attention}Runde{}" }, + individual_play = { "für jede gewertete Karte" }, + individual_hand_score = { "für jede Karte in der Hand während der Auswertung" }, + individual_hand_end = { "für jede Karte in der Hand am Ende der {C:attention}Runde{}" }, + repetition_play = { "Löse gespielte Karten erneut aus" }, + repetition_hand = { "Löse alle Karten in der Hand erneut aus" }, + other_joker = { "für jeden {C:attention}Joker{}" }, + before = { "vor jeder {C:attention}Hand{}" }, + after = { "nach jeder {C:attention}Hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "wenn dies ein {C:blue}Gewöhnlicher{} {C:attention}Joker{} ist" }, + buy_uncommon = { "wenn dies ein {C:green}Ungewöhnlicher{} {C:attention}Joker{} ist" }, + tarot = { "wenn die Karte eine {C:tarot}Tarot{} Karte ist" }, + planet = { "wenn die Karte eine {C:planet}Planeten{} Karte" }, + spectral = { "wenn die Karte eine {C:spectral}Geisterkarte{} ist" }, + joker = { "wenn die Karte ein {C:attention}Joker{} ist" }, + suit = { "wenn die Karte ein {V:1}#3#{} ist" }, + rank = { "wenn die Karte ein {C:attention}#3#{} ist" }, + face = { "wenn die Karte eine {C:attention}Bildkarte{} ist" }, + boss = { "wenn {C:attention}Blind{} eine {C:attention}Boss {C:attention}Blind{} ist" }, + non_boss = { "wenn {C:attention}Blind{} eine {C:attention}Nicht-Boss {C:attention}Blind{} ist" }, + small = { "wenn {C:attention}Blind{} eine {C:attention}Small {C:attention}Blind{} ist" }, + big = { "wenn {C:attention}Blind{} eine {C:attention}Big {C:attention}Blind{} ist" }, + first = { "wenn dies die {C:attention}erste {C:attention}Hand{} ist" }, + last = { "wenn dies die {C:attention}letzte {C:attention}Hand{} ist" }, + common = { "wenn es ein {C:blue}Gewöhnicher{} {C:attention}Joker{} ist" }, + uncommon = { "wenn es ein {C:green}Ungewöhnlicher{} {C:attention}Joker{} ist" }, + rare = { "wenn es ein {C:red}Seltener{} {C:attention}Joker{} ist" }, + poker_hand = { "wenn Hand ein {C:attention}#3#{} is" }, + or_more = { "wenn Hand {C:attention}#3#{} oder mehr Karten enthält" }, + or_less = { "wenn Hand {C:attention}#3#{} oder weniger Karten enthält" }, + hands_left = { "wenn am Ende der Runde #3# {C:blue}Hände{} übrig sind" }, + discards_left = { "wenn am Ende der Runde #3# {C:red}Abwürfe{} übrig sind" }, + first_discard = { "wenn es der {C:attention}erste {C:attention}Abwurf{} ist" }, + last_discard = { "wenn es der {C:attention}letzte {C:attention}Abwurf{} ist" }, + odds = { "mit einer Chance von {C:green}#4# {C:green}zu {C:green}#3#{}" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Runde"}, + a_candy = {"+#1# Süßigkeit"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Runde"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Nutze diesen Joker", + "um auf #2##1#", + "#2#Einsatz#3# Schwieringkeit tu gewinnen" + }, + + + cry_art = {"Bild: #1#"}, + cry_code = {"Code: #1#"}, + cry_idea = {"Idee: #1#"}, + }, + v_text = { + ch_c_cry_all_perishable = {"Alle Joker sind {C:eternal}Verderblich{}"}, + ch_c_cry_all_rental = {"Alle Joker sind {C:eternal}Gemietet{}"}, + ch_c_cry_all_pinned = {"Alle Joker sind {C:eternal}Angeheftet{}"}, + ch_c_cry_all_banana = {"Alle Joker sind {C:eternal}Banane{}"}, + ch_c_all_rnj = {"Alle Joker sind {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"Alle kaufbaren Gegenstände haben alle Sticker"}, + ch_c_cry_rush_hour = {"Alle Boss Blinds sind {C:attention}Die Uhr{} oder {C:attention}Lavendelschleife"}, + ch_c_cry_rush_hour_ii = {"Alle Blinds sind {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}Die Uhr{} und {C:attention}Lavendelschleife{} steigen {C:attention}doppelt{} so schnell"}, + ch_c_cry_no_tags = {"Überspringen ist {C:attention}deaktiviert{}"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "KEINE GUTSCHEINE", "FÜR DICH" }, + { "LAPPEN", "DACHTEST DU ICH WÜRDE", "DIR GUTSCHEINE GEBEN?" }, + { "NÖ!", "KEINE GUTSCHEINE HIER!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE GUT GENUG FÜR", "EINEN GUTSCHEIN ZU SEIN" }, + { "JIMBO", "VON DER VERWALTUNG", "HAT VERGESSEN NACHZUFÜLLEN" }, + { "OOPS!", "KEINE GUTSCHEINE", "" }, + { "DU NARR,", "WARUM GUCKST ÜBERHAUPT", "HIER HER? LOL" }, + { "DER GUTSCHEIN", "IST IN EINEM", "ANDEREN SCHLOSS" }, + { "$0", "LEER", "(HASTE GECHEKT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "ALLE GUTSCHEINE", "100% REDUZIERT", "(JEMAND HAT SIE SCHON GEKAUFT)" }, + { "VERSUCHST SPÄTER NOCHMAL", "HINWEIS: DU WIRST NICHT", "GENUG GELD HABEN" }, + { "HÄ?", '"GUTSCHEINE"?', "DAS IST NICHTMAL EIN WORT..." }, + { 'HALTE "R"', "UM ALLE GUTSCHEINE", "NACHZUFÜLLEN" }, + { "WÜSSTEST DU?", "ALT+F4 DRÜCKEN", "GIBT KOSTENLOSE GUTSCHEINE!" }, + { "TUHT MIR LEID,", "ES GIBT KEINE GUTSCHEINE", "WEGEN BUDGETVERKÜRZUNGEN" }, + { "RUFE 1-600-JIMBO AN", "UM DEINE ERFAHRUNG MIR", "GUTSCHEINEN ZU BEWERTEN" }, + { "BESIEGE", "ANTE 39 BOSS BLIND", "ZUM NACHFÜLLEN" }, + { "ZAUBERTRICK", "ICH HABE DIESEN GUTSCHEIN", "VERSCHWINDEN LASSEN" }, + { "WARUM IST EIN", "GUTSCHEIN WIE EIN", "SCHREIBTISCH?" }, + { "WIR HABEN DEINE GUTSCHEINE ZURÜCKGEZOGEN", "DEINE GUTSCHEINE WÄHREN IN", "ANDEREN DURCHLAUFEN BESSER" }, + { "WARUM NENNEN SIE ES GUTSCHEIN", "WENN ER NICHTMAL GUT GENUG IT", "ZU ERSCHEINEN" }, + { "LEIDER WURDE", "DAS GUTSCHEIN NACHFÜLL", "UPDATE ABGEBROCHEN" }, + { "BESIEGE DIE", "BOSS BLIND", "UM NICHTS ZU ÄNDERN" }, + { "DIE VÖGEL ZWITCHERN", "DIE BLUMEN BLÜHEN", "KINDER WIE DU..." }, + { "ES TUHT UNS LEID ZU SAGEN", "ALLE GUTSCHEINE WURDEN ZURÜCKGEZOGEN", "WEGEN EINEN SALMONELLENBEFALL" }, + { "GUTSCHEINE KONNTEN NICHT ANKOMMEN", "WEIL DER SHOP", "200% ÜBER DEM BUDGET IST" }, + { "DU MAGST", "GUTSCHEINE KAUFEN, NICHT WAHR?", "DU BIST EIN GUTSCHEINKÄUFER" }, + { "GUTSCHEINE", "!E", "GUTSCHEIN POOL" }, + { "ES", "GIBT KEINE", "GUTSCHEINE" }, + { "ES GIBT KEINEN", "WEINACHTSMANN", "UND AUCH KEINE GUTSCHEINE" }, + { "", "GUTNEIN", "" }, + { "DU", "HAST GERADE", "DAS SPIEL VERLOREN" }, + { "CAN ICH DIR IN DIESEN", "IN DIESEN SCHWEREN ZEITEN", "EIN SCHÖNES EI ANBIETEN?" }, + { "BERÜHRE ETWAS GRASS", "ANSTATT DIESES", "DECK ZU BENUZEN" }, + { "DU KÖNNTEST JETZT", "GERADE MIT DEM BLAUEN", "DECK SPEILEN" }, + { "KOSTENLOSE EXOTISCHE JOKER", "HOHLE SIE BEVOR", "ES ZU SPÄT IST (ausverkauft)" }, + { "DU KANNST BEWEISEN, DASS", "ICH FALSCH LIEGE INDEM", "DU DEN UNSICHBAREN GUTSCHEIN KAUFST" }, + { "", "keine gutscheine?", "" }, + { "siehst du diese werbung?", "wenn ja, dann funkioniert sie", "und du könntest sie nun für dich selbst haben" }, + { "DU VERPASST HIER", "MINDESTENS 5 GUTSCHEINE", "tonktonktonktonktonk" }, + { "10", "20 KEINE GUTSCHEINE XD", "30 GOTO 10" }, + { "GUTSCHEINE", "DIE SIND EIN PREMIUM FEATURE", "$199.99 JOLLARS ZUM FREISCHALTEN" }, + { "WAHRES GUTSCHEINLOS!?!?", "NUR AUFGESTIEGENER EINSATZ", "SEHR FAIRES DECK" }, + { "GEFÄLLT DIR DEINE", "GUTSCHEIN ERFAHRUNG? BEWERTE UNS MIT", "FÜNF STERNEN AUF JESTELP" }, + { "KOSTENLOSE GUTSCHEINE", "HEISSE GUTSCHEINE IN DEINER GEGEND!", "BEKOMME SCHNELL GUTSCHEINE MIT DIESEM LEICHTEN TRICK" }, + { "JETZT STELLE ICH DIE", "ERSTEN LEVEL 0 GUTSCHEINE VOR!", "(kommt bald in Crypid 1.0)" }, + { "EIN GUTSCHEIN!", "ES IST NUT EINE VORSTELLUNG", "WIR HABEN UNS VORGESTELLT, DASS DU IHN WILLST" }, + { "MACH DEINEN ADBLOCKER AUS!", "WIE SOLLEN WIR OHNE WERBUNG", "GUTSCHEINE VERKAUFEN" }, + { "WENN DU EIN", "PROBLEM HIERMIT HAST", "SIND WIR UNTER NORESPONSE@JMAIL.COM ERREICHBAR" }, + { "NICHT GENUG GELD", "FÜR DIESEN GUTSCHEIN", "ALSO WARUM SOLLTEN WIR IHN HIER HINTUN?" }, + { "WILLST DU EINEN GUTSCHEIN?", "HALT DIE FRESSE", "DU KRIEGST KEINE LOL" }, + { "^$%& KEINE", "GUTSCHEINE ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "EIN GUTSCHEIN (VERTRAU MIR)", "|\\/|", "|/\\|" }, + { + "... --- ...", + "... .--. .. . .-.. . .-. / . -. - ... -.-. .... .-.. ..-- ... ... . .-.. - / -- --- .-. ... . / -.-. --- -.. .", + "..-. ..-- .-. / . .. -. . -. / --. ..- - ... -.-. .... . .. -.", + }, + { "DURCHLAUF > NEU", "STARRE INS NICHTS", "FÜR EINE ODER ZWEI STUNDEN" }, + { "TUHT UNS LEID", "DER LETZTE TYP HAT", "IN DER PANIK ALLE GUTSCHEINE GEKAUFT" }, + { "WIE FÜHLT ES SICH AN", "KEINE GUTSCHEINE", "ZU KAUFEN" }, + { "JIMBO WAR WÜTEND", "UND HAT ALLE GUTSCHEINE", "WEGGEWORFEN" }, + { "VERSUCHE INDEX IN", "FELD 'GUTSCHEIN' ZU FINDEN", "(EIN NIL WERT)" }, + { + "HAST DU WIRKLICH ERWARTET, DASS ALL DIESE TEXT ZU LESEN DIR MÖGLICHERWEISE GUTSCHEINE BRINGEN KÖNNTE", + "TUHT MIR LEID DIR DAS MITTEILEN ZU MÜSSEN, ABER IN DIESEM DECK EXISTIEREN GUTSCHEINE NUNMAL NICHT", + "AUSSERDEM IST DIESER ABNORMAL LANGE TEXT NOCH EXTRA DAFÜR DA DEINE ZEIT BEIM LESEN ZU VERSCHWENDEN", + }, + { "GEH ZU", "https://youtu.be/p7YXXieghto", "FÜR KOSTENLOSE GUTSCHEINE" }, + { "BEDANK DICH BEI FOEGRO", "DASS ER DAS MOD AUF DEUTSCH ÜBERSETZT HAT", "STATT NUTZLOS NACH GUTSCHEINEN ZU SUCHEN" }, + } + } +} diff --git a/Cryptid/localization/en-us.lua b/Cryptid/localization/en-us.lua new file mode 100644 index 0000000..417b00e --- /dev/null +++ b/Cryptid/localization/en-us.lua @@ -0,0 +1,3959 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antimatter Deck", + text = { + "Applies the {C:legendary,E:1}upsides{}", + "of {C:attention}every{} deck", + }, + }, + b_cry_beta = { + name = "Nostalgic Deck", + text = { + "{C:attention}Joker{} and {C:attention}Consumable{}", + "slots are {C:attention}combined", + "{C:attention}Nostalgic{} Blinds replace", + "their updated Blind" + }, + }, + b_cry_blank = { + name = "Blank Deck", + text = { + "{C:inactive,E:1}Does nothing?", + }, + }, + b_cry_bountiful = { + name = "Bountiful Deck", + text = { + "After {C:blue}Play{} or {C:red}Discard{},", + "always draw {C:attention}5{} cards" + }, + }, + b_cry_CCD = { + name = "CCD Deck", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + b_cry_conveyor = { + name = "Conveyor Deck", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + b_cry_critical = { + name = "Critical Deck", + text = { + "After each hand played,", + "{C:green}#1# in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}#1# in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + b_cry_encoded = { + name = "Encoded Deck", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + b_cry_equilibrium = { + name = "Deck of Equilibrium", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}Overstock Plus", + }, + }, + b_cry_glowing = { + name = "Glowing Deck", + text = { + "Multiply the values of", + "all Jokers by {X:dark_edition,C:white} X1.25 {}", + "when Boss Blind is defeated", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Infinite Deck", + text = { + "You can select {C:attention}any", + "number of cards", + "{C:attention}+1{} hand size", + }, + }, + b_cry_misprint = { + name = "Misprint Deck", + text = { + "Values of cards", + "and poker hands", + "are {C:attention}randomized", + }, + }, + b_cry_redeemed = { + name = "Redeemed Deck", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + b_cry_spooky = { + name = "Spooky Deck", + text = { + "Start with an {C:eternal}Eternal{} {C:attention,T:j_cry_chocolate_dice}Chocolate Die", + "After each {C:attention}Ante{}, create a", + "{C:cry_candy}Candy{} or {X:cry_cursed,C:white}Cursed{} Joker", + } + }, + b_cry_very_fair = { + name = "Very Fair Deck", + text = { + "{C:blue}-2{} hands, {C:red}-2{} discards", + "every round", + "{C:attention}Vouchers{} no longer", + "appear in the shop", + }, + }, + b_cry_wormhole = { + name = "Wormhole Deck", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + b_cry_legendary = { + name = "Legendary Deck", + text = { + "Start with a {C:legendary}Legendary{C:legendary} Joker", + "{C:green}1 in 5{} chance to create another", + "when Boss Blind is defeated {C:inactive}(must have room){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "The Box", + text = { + "All Common Jokers", + "are debuffed", + }, + }, + bl_cry_clock = { + name = "The Clock", + text = { + "+0.1X blind requirements every", + "3 seconds spent this ante", + }, + }, + bl_cry_hammer = { + name = "The Hammer", + text = { + "All cards with odd", + "rank are debuffed", + }, + }, + bl_cry_joke = { + name = "The Joke", + text = { + "If score is >2X requirements,", + "set ante to multiple of #1#", + }, + }, + bl_cry_magic = { + name = "The Magic", + text = { + "All cards with even", + "rank are debuffed", + }, + }, + bl_cry_lavender_loop = { + name = "Lavender Loop", + text = { + "1.25X blind requirements every", + "1.5 seconds spent this round", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidian Orb", + text = { + "Applies abilities of", + "all defeated bosses", + }, + }, + bl_cry_oldarm = { + name = "Nostalgic Arm", + text = { + "Must play 4", + "or fewer cards", + }, + }, + bl_cry_oldfish = { + name = "Nostalgic Fish", + text = { + "All hands start", + "with 1 Mult", + }, + }, + bl_cry_oldflint = { + name = "Nostalgic Flint", + text = { + "No Flushes", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgic House", + text = { + "No Full Houses", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgic Manacle", + text = { + "Divide Mult by discards", + }, + }, + bl_cry_oldmark = { + name = "Nostalgic Mark", + text = { + "No hands that", + "contain a Pair", + }, + }, + bl_cry_oldox = { + name = "Nostalgic Ox", + text = { + "All hands start", + "with 0 Chips", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgic Pillar", + text = { + "No Straights", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgic Serpent", + text = { + "Divide Mult by level", + "of played poker hand", + }, + }, + bl_cry_pin = { + name = "The Pin", + text = { + "Jokers with Epic or higher", + "rarity are debuffed", + }, + }, + bl_cry_pinkbow = { + name = "Pink Bow", + text = { + "Randomize rank of cards", + "held in hand on play", + }, + }, + bl_cry_sapphire_stamp = { + name = "Sapphire Stamp", + text = { + "Select an extra card, deselect", + "random card before scoring", + }, + }, + bl_cry_shackle = { + name = "The Shackle", + text = { + "All Negative Jokers", + "are debuffed", + }, + }, + bl_cry_striker = { + name = "The Striker", + text = { + "All Rare Jokers", + "are debuffed", + }, + }, + bl_cry_tax = { + name = "The Tax", + text = { + "Score per hand capped at", + "0.4X blind requirements", + }, + }, + bl_cry_tornado = { + name = "Turquoise Tornado", + text = { + "#1# in #2# chance for", + "played hand to not score", + }, + }, + bl_cry_trick = { + name = "The Trick", + text = { + "After each hand, flip all", + "face-up cards held in hand", + }, + }, + bl_cry_vermillion_virus = { + name = "Vermillion Virus", + text = { + "One random Joker", + "replaced every hand", + }, + }, + bl_cry_windmill = { + name = "The Windmill", + text = { + "All Uncommon Jokers", + "are debuffed", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Convert {C:cry_code}#1#{} selected card", + "to a {C:cry_code}chosen{} enhancement", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}new{} Joker", + "of the {C:cry_code}same rarity", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Don't.", + }, + }, + c_cry_ctrl_v = { + name = "://CTRL+V", + text = { + "Create a {C:cry_code}copy{} of a selected", + "playing card or consumable" + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Permanently{} remove a", + "{C:cry_code}selected{} shop item", + "{C:inactive,s:0.8}Item cannot appear again this run", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "{C:cry_code}Halve{} all listed prices", + "in current shop", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "Any played hand is considered", + "to {C:cry_code}contain{} a {C:cry_code}chosen{} poker hand,", + "resets at end of round", + "{C:inactive,s:0.8}Secret hands must be", + "{C:inactive,s:0.8}discovered to be valid", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Select two Jokers to become {C:cry_code}Hooked", + "{C:inactive,s:0.8}Only works properly if Jokers trigger in the same context,", + "{C:inactive,s:0.8}such as Joker and The Duo (both post-scoring)", + }, + }, + c_cry_inst = { + name = "://INSTANTIATE", + text = { + "Draw a card with selected card's {C:cry_code}rank{}", + "and one with selected card's {C:cry_code}suit{}", + "{C:inactive}(if possible){}", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Add {C:dark_edition}Glitched{} to all", "cards {C:cry_code}held in hand" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Merge a selected {C:cry_code}consumable", + "with a selected {C:cry_code}playing card", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Double{} all values of", + "a selected {C:cry_code}Joker{} until", + "end of round", + }, + }, + c_cry_patch = { + name = "://PATCH", + text = { + "Remove all debuffs and stickers", + "from currently visible items", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Next defeated Blind", + "gives {C:cry_code}X#1#{} interest", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:cry_code}#1#{} extra card and", + "{C:cry_code}#1#{} extra choice", + "{C:inactive}(Currently {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Replenish {C:blue}Hands{} and {C:red}Discards{},", + "return {C:cry_code}all{} cards to deck", + "and draw a {C:cry_code}new{} hand", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Set {C:cry_code}game state{} to", + "start of {C:cry_code}this Ante{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}Rework Tag{} with", + "an {C:cry_code}upgraded{} edition", + "{C:inactive,s:0.8}Upgrades using order in the Collection", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Visit a {C:cry_code}shop", + "during a {C:cry_code}Blind", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Select a Joker", + "or playing card", + "to become {C:cry_code}Rigged", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Ends current non-Boss {C:cry_code}Blind{}", "{C:cry_code}without{} cashing out" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Create a {C:cry_code}Glitched", + "Food Joker", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convert {C:cry_code}#1#{} selected cards", + "to a {C:cry_code}chosen{} rank", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} Mult", + }, + }, + e_cry_blur = { + name = "Blurred", + text = { + "{C:attention}Retrigger{} this", + "card {C:attention}1{} time", + "{C:green}#1# in #2#{} chance", + "to retrigger {C:attention}#3#{}", + "additional time", + }, + }, + e_cry_double_sided = { + name = "Double-Sided", + text = { + "This card can be", + "{C:attention}flipped{} to reveal", + "a different card", + "{C:inactive}(Blank side can be merged", + "{C:inactive}with another card)", + }, + }, + e_cry_glass = { + name = "Fragile", + label = "Fragile", + text = { + "{C:white,X:mult} X#3# {} Mult", + "{C:green}#1# in #2#{} chance this", + "card isn't {C:red}destroyed", + "when triggered", + }, + }, + e_cry_glitched = { + name = "Glitched", + text = { + "All values on this card", + "are {C:dark_edition}randomized{}", + "between {C:attention}X0.1{} and {C:attention}X10{}", + "{C:inactive}(If possible){}", + }, + }, + e_cry_gold = { + name = "Golden", + label = "Golden", + text = { + "Earn {C:money}$#1#{} when used", + "or triggered", + }, + }, + e_cry_m = { + name = "Jolly", + text = { + "{C:mult}+#1#{} Mult", + "This card is feeling", + "rather {C:attention}jolly{}", + }, + }, + e_cry_mosaic = { + name = "Mosaic", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + e_cry_noisy = { + name = "Noisy", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Oversaturated", + text = { + "All values", + "on this card", + "are {C:attention}doubled{}", + "{C:inactive}(If possible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Echo Card", + text = { + "{C:green}#2# in #3#{} chance to", + "{C:attention}retrigger{} #1# additional", + "times when scored", + }, + }, + }, + Joker = { + j_cry_adroit = { + name = "Adroit Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_altgoogol = { + name = "Nostalgic Googol Play Card", + text = { + "Sell this card to create", + "{C:attention}2{} copies of the leftmost {C:attention}Joker{}", + "{C:inactive,s:0.8}Does not copy Nostalgic Googol Play Cards{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Like Antennas to Heaven", + text = { + "This Joker gains", + "{X:chips,C:white} X#1# {} Chips when each", + "played {C:attention}7{} or {C:attention}4{} is scored", + "{C:inactive}(Currently {X:chips,C:white}X#2# {C:inactive} Chips)", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "{X:mult,C:white} X#1# {} Mult against {C:attention}Boss Blinds{}" }, + }, + j_cry_astral_bottle = { + name = "Astral in a Bottle", + text = { + "When sold, apply {C:dark_edition}Astral{}", + "and {C:attention}Perishable{} to", + "a random {C:attention}Joker{}", + } + }, + j_cry_big_cube = { + name = "Big Cube", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_biggestm = { + name = "Huge", + text = { + "{X:mult,C:white} X#1# {} Mult until end", + "of round if {C:attention}poker hand{}", + "is a {C:attention}#2#{}", + "{C:inactive}(Currently {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}not fat, just big boned.", + }, + }, + j_cry_blacklist = { + name = "Blacklist", + text = { + "If a {C:attention}#1#{} is held in hand or played,", + "set {C:chips}Chips{} and {C:mult}Mult{} to 0", + "{C:red,E:2}self destructs{} if no {C:attention}#1#{} in deck", + "{C:inactive,s:0.8}Rank does not change" + } + }, + j_cry_blender = { + name = "Blender", + text = { + "Create a {C:attention}random{}", + "consumable when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_blurred = { + name = "Blurred Joker", + text = { + "Gain {C:blue}+#1#{} hand(s) when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Each {C:attention}Joker{} gives {C:chips}+#1#{} Chips", + "Increase amount by {C:chips}+#2#{} if", + "{C:attention} poker hand{} is a {C:attention}#3#{}", + "{C:inactive,s:0.8}Jolly Jokers give{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips instead{}", + }, + }, + j_cry_bonkers = { + name = "Bonkers Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Bonus{} card to increase", + "{C:attention}Joker{} or {C:attention}Consumable slots", + "by {C:dark_edition}1{} when scored", + "{C:red}Works twice per round", + "{C:inactive,s:0.8}(Equal chance for each){}", + }, + }, + j_cry_booster = { + name = "Booster Joker", + text = { + "{C:attention}+#1#{} Booster Pack slot", + "available in shop", + }, + }, + j_cry_boredom = { + name = "Boredom", + text = { + "{C:green}#1# in #2#{} chance to", + "{C:attention}retrigger{} each {C:attention}Joker{}", + "or {C:attention}played card{}", + "{C:inactive,s:0.8}Does not affect other Boredom{}", + }, + }, + j_cry_brittle = { + name = "Brittle Candy", + text = { + "For the next {C:attention}#1#{} hands,", + "add {C:attention}Stone{}, {C:attention}Gold{}, or {C:attention}Steel{} to", + "the rightmost scoring card" + } + }, + j_cry_bubblem = { + name = "Bubble M", + text = { + "Create a {C:dark_edition}Foil {C:attention}Jolly Joker{}", + "if played hand contains", + "a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_busdriver = { + name = "Bus Driver", + text = { + "{C:green}#1# in #3#{} chance", + "for {C:mult}+#2#{} Mult", + "{C:green}1 in 4{} chance", + "for {C:mult}-#2#{} Mult", + }, + }, + j_cry_candy_basket = { + name = "Candy Basket", + text = { + "Sell this card to create {C:attention}#1#{} {C:cry_candy}Candies", + "{C:attention}+#2#{} {C:cry_candy}Candy{} every {C:attention}2{} Blinds defeated", + "{C:attention}+#3#{} {C:cry_candy}Candies{} when {C:attention}Boss Blind{} defeated" + } + }, + j_cry_candy_buttons = { + name = "Candy Buttons", + text = { + "The next {C:attention}#1#{} rerolls", + "cost {C:money}$1{}", + } + }, + j_cry_candy_cane = { + name = "Candy Cane", + text = { + "For the next {C:attention}#1#{} rounds,", + "playing cards give {C:money}$#2#", + "when {C:attention}retriggered" + } + }, + j_cry_candy_dagger = { + name = "Candy Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "to create a {C:cry_candy}Candy{}", + } + }, + j_cry_candy_sticks = { + name = "Candy Sticks", + text = { + "Next boss blind's effect isn't active", + "until you've played {C:attention}#1#{} hand", + } + }, + j_cry_canvas = { + name = "Canvas", + text = { + "{C:attention}Retrigger{} all {C:attention}Jokers{} to the left", + "once for {C:attention}every{} non-{C:blue}Common{C:attention} Joker{}", + "to the right of this Joker", + }, + }, + j_cry_caramel = { + name = "Caramel", + text = { + "Each played card gives", + "{X:mult,C:white}X#1#{} Mult when scored", + "for the next {C:attention}#2#{} rounds", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Retrigger {C:attention}leftmost{} Joker", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_chili_pepper = { + name = "Chili Pepper", + text = { + "This Joker gains {X:mult,C:white} X#2# {} Mult", + "at end of round,", + "{C:red,E:2}self destructs{} after {C:attention}#3#{} rounds", + "{C:inactive}(Currently{} {X:mult,C:white} X#1# {} {C:inactive}Mult){}", + }, + }, + j_cry_chocolate_dice = { + name = "Chocolate Die", + text = { + "Roll a {C:green}d10{} when", + "{C:attention}Boss Blind{} defeated", + "to start an {C:cry_ascendant,E:1}event", + "{C:inactive}(Currently: #1#)" + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Chips and {X:dark_edition,C:white}^#1#{} Mult", + "if {C:attention}exactly{} #2#", + "hands remaining", + }, + }, + j_cry_circus = { + name = "Circus", + text = { + "{C:red}Rare{} Jokers each give {X:mult,C:white} X#1# {} Mult", + "{C:cry_epic}Epic{} Jokers each give {X:mult,C:white} X#2# {} Mult", + "{C:legendary}Legendary{} Jokers each give {X:mult,C:white} X#3# {} Mult", + "{C:cry_exotic}Exotic{} Jokers each give {X:mult,C:white} X#4# {} Mult", + }, + }, + j_cry_clash = { + name = "The Clash", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "an {C:attention}#2#", + }, + }, + j_cry_CodeJoker = { + name = "Code Joker", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:cry_code}Code Card{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Earn between", + "{C:money}$#1#{} and {C:money}$#2#{} for", + "each Joker {C:attention}sold{}", + }, + }, + j_cry_compound_interest = { + name = "Compound Interest", + text = { + "Earn {C:money}#1#%{} of total money", + "at end of round,", + "increases by {C:money}#2#%{} per", + "consecutive payout", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "When a {C:cry_code}Code{} card is used,", + "{C:green}#1# in #2#{} chance to add a copy", + "to your consumable area", + "{C:inactive}(Must have room)", + }, + }, + j_cry_cotton_candy = { + name = "Cotton Candy", + text = { + "When sold, adjacent", + "{C:attention}Jokers{} become {C:dark_edition}Negative{}" + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "per {C:attention}reroll{} in the shop", + "{C:green}All rerolls are free{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Chain", + text = { + "Sell this card to", + "add {C:money}$#1#{} of {C:attention}sell value{}", + "to every {C:attention}Joker{} card", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob = { + name = "Sob", + text = { + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}run...{}", + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}hide...{}", + "{C:dark_edition,E:1}you cannot escape...{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "for each card {C:attention}purchased{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Cut", + text = { + "This Joker destroys", + "a random {C:cry_code}Code{} card", + "and gains {X:mult,C:white} X#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Delirious Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "Doodle M", + text = { + "Create 2 {C:dark_edition}Negative{} {C:attention}consumables{}", + "when {C:attention}Blind{} is selected", + "Create 1 more {C:attention}consumable", + "for each {C:attention}Jolly Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Scale", + text = { + "Scaling {C:attention}Jokers{}", + "scale {C:attention}quadratically", + "{C:inactive,s:0.8}(ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grows by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult for", + "each played, {C:attention}nonscoring{} {V:1}#2#{} card,", + "suit changes every round", + "{C:inactive}(Currently {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubious Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_duos = { + name = "The Duos", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "This Joker gains {X:mult,C:white} X#2# {} Mult", + "when a {C:attention}Joker{} or", + "playing card is scored", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Draw {C:green}full deck{} to hand", + "when {C:attention}Blind{} is selected", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "When a {C:attention}Tag{} is acquired,", + "create {C:attention}#1#{} copies of it", + "and {C:attention}increase{} the number of", + "copies by {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Jokers appear using the", + "order from the {C:attention}Collection{}", + "Create {C:attention}#1#{} {C:dark_edition}Negative{} Joker(s)", + "when hand is played", + "{C:cry_exotic,s:0.8}Exotic {C:inactive,s:0.8}or better Jokers cannot appear", + "{s:0.8}Last Joker Generated: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Eternal Flame", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "for each card {C:attention}sold{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographic{} cards", + "each give {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "when {X:red,C:white} XMult {} is triggered", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_exposed = { + name = "Exposed", + text = { + "Retrigger all non-{C:attention}face{} cards", + "{C:attention}#1#{} additional time(s)", + "All {C:attention}face{} cards are debuffed", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult if", + "played cards are scored", + "{C:attention}#2#{} or fewer times", + }, + }, + j_cry_filler = { + name = "The Filler", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fractal Fingers", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + j_cry_flip_side = { + name = "On the Flip Side", + text = { + "{C:dark_edition}Double-Sided{} Jokers use", + "their back side for effects", + "instead of the front side", + "{C:attention}Retrigger{} all {C:dark_edition}Double-Sided{} Jokers" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}self destructs{} in {C:attention}#2#{} round(s)", + "Increases by {C:attention}#3#{} round when", + "{C:attention}Jolly Joker{} is {C:attention}sold{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foolhardy = { + name = "Foolhardy Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "an {C:attention}#2#" + } + }, + j_cry_formidiulosus = { + name = "Formidiulosus", + text = { + "When a {X:cry_cursed,C:white}Cursed{} Joker is obtained, destroy it", + "Creates {C:attention}#1#{} {C:dark_edition}Negative {C:cry_candy}Candies{} at end of shop", + "Gains {X:dark_edition,C:white}^#2#{} Mult for each {C:cry_candy}Candy{} in possession", + "{C:inactive}(Currently {X:dark_edition,C:white}^#3#{C:inactive} Mult)", + }, + }, + j_cry_foxy = { + name = "Foxy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "if hand played is {C:attention}not{}", + "most played {C:attention}poker hand{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_fuckedup = { + name = "Fucked-Up Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Earn {C:money}$#1#{} if {C:attention}played hand{}", + "contains an {C:attention}Ace{} and a {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} all values", + "of leftmost {C:attention}Joker", + "at end of round", + }, + }, + j_cry_ghost = { + name = "Ghost", + text = { + "At end of round:", + "{C:green}#1# in #2#{} chance to", + "{C:attention}possess{} a random {C:attention}Joker", + "{C:green}#1# in #3#{} chance to", + "{E:2,C:red}self destruct" + } + }, + j_cry_giggly = { + name = "Absurd Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Earn {C:money}#1#%{} of total", + "money at end of round", + "Payout increases by {C:money}#2#%{}", + "when each played {C:attention}Gold{}", + "card is scored", + }, + }, + j_cry_googol_play = { + name = "Googol Play Card", + text = { + "{C:green}#1# in #2#{} chance for", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Create a random {C:attention}Joker{}", + "at end of round", + "Sell this card to", + "create a random {C:attention}Joker{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_happyhouse = { + name = "Happy House", + text = { + "{X:dark_edition,C:white}^#1#{} Mult only after", + "playing {C:attention}114{} hands{}", + "{C:inactive}(Currently #2#/114){}", + "{C:inactive,s:0.8}There is no place like home!{}", + }, + }, + j_cry_home = { + name = "The Home", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Earn {C:money}$#1#{} when", + "using a {C:attention}consumable{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Retrigger all cards played", + "{C:attention}#2#{} time(s),", + "each played card gives", + "{X:mult,C:white} X#1# {} Mult when scored", + }, + }, + j_cry_jawbreaker = { + name = "Jawbreaker", + text = { + "When {C:attention}Boss Blind{} defeated,", + "{C:attention}double{} values of adjacent Jokers", + "{E:2,C:red}self destructs{}", + } + }, + j_cry_jimball = { + name = "Jimball", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "per {C:attention}consecutive{} hand played", + "while playing your", + "most played {C:attention}poker hand", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Jolly Joker?", + text = { + "Create a {C:dark_edition}Jolly{} Joker", + "when a Joker is {C:attention}sold{}", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{}", + "when a {C:attention}Type Mult{} or", + "{C:attention}Type Chips{} Joker is sold", + }, + }, + j_cry_kooky = { + name = "Kooky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty the Clown", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when", + "each played {C:attention}card{} is scored", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoscope", + text = { + "Add {C:dark_edition}Polychrome{} to", + "a random {C:attention}Joker{} when", + "{C:attention}Boss Blind{} is defeated", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Each played {C:attention}7{} or {C:attention}2{}", + "gives {X:mult,C:white}X#1#{} Mult when scored", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Give future copies of", + "this Joker {X:mult,C:white}X#1#{} Mult", + "at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Retrigger{} all Jokers", + "once for each {C:attention}Jolly{}", + "{C:attention}Joker{} sold this round", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Retrigger(s)){}", + "{C:inactive,s:0.8}There wasn't enough room...{}", + }, + }, + j_cry_lucky_joker = { + name = "Lucky Joker", + text = { + "Earn {C:money}$#1#{} every time a", + "{C:attention}Lucky{} card {C:green}successfully{}", + "triggers", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "All Jokers give", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "when {C:attention}Jolly Joker{} is sold", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:attention}Jolly Joker{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_macabre = { + name = "Macabre Joker", + text = { + "When {C:attention}Blind{} is selected,", + "destroys each {C:attention}Joker{} except", + "{C:legendary}M Jokers{} and {C:attention}Jolly Jokers{}", + "and create 1 {C:attention}Jolly Joker{}", + "for each destroyed card", + }, + }, + j_cry_magnet = { + name = "Fridge Magnet", + text = { + "Earn {C:money}$#1#{} at end of round", + "This earns {X:money,C:white} X#2# {} if there are", + "{C:attention}#3#{} or fewer {C:attention}Joker{} cards", + }, + }, + j_cry_manic = { + name = "Manic Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Retrigger all Jokers", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_mask = { + name = "Mask", + text = { + "Retrigger all {C:attention}face{} cards", + "{C:attention}#1#{} additional time(s)", + "All non-{C:attention}face{} cards are debuffed", + }, + }, + j_cry_maximized = { + name = "Maximized", + text = { + "All {C:attention}face{} cards", + "are considered {C:attention}Kings{},", + "all {C:attention}numbered{} cards", + "are considered {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Labyrinth", + text = { + "All hands are considered the", + "{C:attention}first hand{} of each round,", + "all discards are considered the", + "{C:attention}first discard{} of each round", + }, + }, + j_cry_Megg = { + name = "Megg", + text = { + "Sell this card to create", + "{C:attention}#2#{} Jolly #3#, increase", + "by {C:attention}#1#{} at end of round", + }, + }, + j_cry_mellowcreme = { + name = "Mellowcreme", + text = { + "Sell this card to {C:attention}multiply", + "the sell value of all", + "{C:attention}consumables{} by {C:attention}X#1#" + } + }, + j_cry_membershipcard = { + name = "Membership Card", + text = { + "{X:mult,C:white}X#1#{} Mult for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Old Membership Card", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteor Shower", + text = { + "{C:dark_edition}Foil{} cards each", + "give {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by", + "{C:money}$#1#{} for each {C:attention}Jolly Joker{}", + "or {C:legendary}M Joker{} at", + "end of round", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if no {C:attention}discards{} were", + "used this round", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Monkey Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and permanently add {C:attention}ten times{}", + "its sell value to this {C:chips}Chips{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_monopoly_money = { + name = "Monopoly Money", + text = { + "{C:green}#1# in #2#{} chance to", + "{C:attention}destroy{} purchased items", + "Halves money when {C:attention}sold", + } + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{} when", + "a card with an {C:attention}Edition{} is sold", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Create an {C:legendary}M Joker{} at end of round", + "Each {C:attention}Jolly Joker{} or {C:legendary}M Joker", + "gives {X:dark_edition,C:white}^#1#{} Mult", + "Increase amount by {X:dark_edition,C:white}^#2#{}", + "when a {C:attention}Jolly Joker{} is {C:attention}sold", + "{C:inactive,s:0.8}(Tredecim excluded)", + }, + }, + j_cry_mstack = { + name = "M Stack", + text = { + "Retrigger all cards played", + "once for every", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jolly Jokers{} sold", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} retriggers){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Mult{} card to create", + "a {C:spectral}Cryptid{} card when scored", + "{C:inactive}(Must have room)", + }, + }, + j_cry_necromancer = { + name = "Necromancer", + text = { + "When a Joker is {C:attention}sold{} for more than {C:attention}$0{},", + "gain a {C:attention}random{} Joker {C:attention}sold{} this run", + "with {C:money}$0{} sell value", + }, + }, + j_cry_negative = { + name = "Negative Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips if played hand", + "contains a {C:attention}6{} and a {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Night", + text = { + "{X:dark_edition,C:white}^#1#{} Mult on final", + "hand of round", + "{E:2,C:red}self destructs{} on", + "final hand of round", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Retrigger each played {C:attention}7{}", + "{C:attention:}#1#{} additional time(s)", + }, + }, + j_cry_notebook = { + name = "Notebook", + text = { + "{C:green} #1# in #2#{} chance to gain {C:dark_edition}+1{} Joker", + "slot per {C:attention}reroll{} in the shop", + "{C:green}Always triggers{} if there are", + "{C:attention}#5#{} or more {C:attention}Jolly Jokers{}", + "{C:red}Works once per round{}", + "{C:inactive}(Currently {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Number Blocks", + text = { + "Earn {C:money}$#1#{} at end of round", + "Increase payout by {C:money}$#2#{}", + "for each {C:attention}#3#{} held in hand,", + "rank changes every round", + }, + }, + j_cry_nuts = { + name = "The Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_oil_lamp = { + name = "Oil Lamp", + text = { + "Increase values of {C:attention}Joker{} to the right", + "by {C:attention}X#1#{} at end of round", + }, + }, + j_cry_oldblueprint = { + name = "Old Blueprint", + text = { + "Copies ability of", + "{C:attention}Joker{} to the right", + "{C:green}#1# in #2#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + j_cry_oldcandy = { + name = "Nostalgic Candy", + text = { + "Sell this card to", + "permanently gain", + "{C:attention}+#1#{} hand size", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgic Invisible Joker", + text = { + "{C:attention}Duplicate{} a random", + "{C:attention}Joker{} every {C:attention}4", + "Joker cards sold", + "{s:0.8}Nostalgic Invisible Joker Excluded{}", + "{C:inactive}(Currently #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panopticon", + text = { + "All hands are considered the", + "{C:attention}last hand{} of each round", -- +$4 + }, + }, + j_cry_penetrating = { + name = "Penetrating Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_pickle = { + name = "Pickle", + text = { + "When {C:attention}Blind{} is skipped, create", + "{C:attention}#1#{} Tags, reduced by", + "{C:red}#2#{} when {C:attention}Blind{} is selected", + }, + }, + j_cry_pirate_dagger = { + name = "Pirate Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "and gain {C:attention}one-fourth{} of", + "its sell value as {X:chips,C:white} XChips {}", + "{C:inactive}(Currently {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + j_cry_pity_prize = { + name = "Pity Prize", + text = { + "When you skip a {C:attention}Booster Pack{} gain a random {C:attention}Tag{}" + }, + }, + j_cry_pot_of_jokes = { + name = "Pot of Jokes", + text = { + "{C:attention}#1#{} hand size,", + "increases by", + "{C:blue}#2#{} every round", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "if all cards in played hand are", + "{C:attention}Aces{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, or {C:attention}7s{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "If {C:attention}poker hand{} is a", + "{C:attention}Royal Flush{}, destroy scored", + "{C:attention}Queen{} and create a", + "{C:dark_edition}Negative {}{C:red}Rare{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "The Quintet", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Red Bloon", + text = { + "Earn {C:money}$#1#{} in {C:attention}#2#{} round(s)", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante when", + "{C:money}$#2#{} {C:inactive}($#3#){} spent", + "{s:0.8}Requirements increase", + "{C:attention,s:0.8}exponentially{s:0.8} per use", + "{C:money,s:0.8}Next increase: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "When a {C:attention}Joker{} is sold,", + "add its effects to", + "every other Joker", + "{C:inactive,s:0.8}Does not affect other Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fill all empty Joker slots {C:inactive}(Max 100){}", + "with {C:dark_edition}Holographic{} {C:attention}Jolly Jokers{} if", + "{C:attention}discarded poker hand{} is a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Randomize abilities each {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Create an {C:green}Uncommon{} Joker", + "and 3 {C:attention}Jolly Jokers{} when", + "a {C:spectral}Spectral{} card is used", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sapling", + text = { + "After scoring {C:attention}#2#{} {C:inactive}[#1#]{} Enhanced", + "cards, sell this card to", + "create an {C:cry_epic}Epic{} {C:attention}Joker{}", + "{C:inactive,s:0.8}Will create a {C:red,s:0.8}Rare{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}if {C:cry_epic,s:0.8}Epic{} {C:inactive,s:0.8}Jokers are disabled{}", + }, + }, + j_cry_savvy = { + name = "Savvy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Scaling {C:attention}Jokers{} scale", + "as a degree-{C:attention}#1#{} polynomial", + "raise degree by {C:attention}#2#{}", + "at end of round", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluded)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Tile", + text = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Jolly {C:green}Uncommon{} Joker", + "when hand is played", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Add a {C:attention}random seal{} to each card", + "scored on {C:attention}final hand{} of round", + }, + }, + j_cry_shrewd = { + name = "Shrewd Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Silly Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Tiny", + text = { + "Create a {C:cry_jolly}Double M", + "tag if {C:attention}poker hand{}", + "is a {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker slot", + "{C:attention}+#1#{} Booster Pack slot", + "{C:attention}+#1#{} hand size", + "{C:attention}+#1#{} consumable slot", + "{C:attention}+#1#{} card in shop", + }, + }, + j_cry_fleshpanopticon = { + name = "Flesh Panopticon", + text = { + "{C:red}X#1#{} {C:attention}Boss Blind{} size", + "When {C:attention}Boss Blind{} is defeated,", + "{C:red}self destructs{}, and creates", + "a {C:dark_edition}Negative{} {C:spectral}Gateway{} card", + "{C:inactive,s:0.8}\"This prison... to hold... me?\"" + }, + }, + j_cry_spaceglobe = { + name = "Celestial Globe", + text = { + "This Joker gains {X:chips,C:white}X#2#{} Chips", + "if {C:attention}poker hand{} is a {C:attention}#3#{},", + "Hand changes after increase{}", + "{C:inactive}(Currently{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Creates a {C:dark_edition}Negative{} copy", + "of a random {C:attention}Joker{}", + "at the end of the {C:attention}shop", + "{C:inactive,s:0.8}Does not copy other Speculo{}", + }, + }, + j_cry_spy = { + name = "Spy", + text = { + "{X:mult,C:white} X#2# {} Mult, {C:dark_edition}+1{C:attention} Joker{} slot", + "{C:inactive}That #1# is a spy!", + }, + }, + j_cry_stardust = { + name = "Stardust", + text = { + "{C:dark_edition}Polychrome{} cards", + "each give {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "This Joker destroys a", + "random {C:planet}Planet{} card", + "and gains {X:dark_edition,C:white} ^#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_stronghold = { + name = "The Stronghold", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_subtle = { + name = "Subtle Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Earn {C:money}$#3#{} at", + "end of round", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "At end of round, create", + "a {C:attention}copy{} of a random", + "card {C:attention}held in hand{},", + "destroy all others", + "{C:attention,s:0.8}Kings{s:0.8} of {C:hearts,s:0.8}Hearts{s:0.8} are prioritized", + }, + }, + j_cry_swarm = { + name = "The Swarm", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Catalyst", + text = { + "Balances {C:chips}Chips{} and {C:mult}Mult{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tax_fraud = { + name = "Tax Fraud", + text = { + "Gain {C:attention}$#1#{} per {C:attention}Rental Joker", + "at end of round", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + "Earn {C:money}$#2#{} at end of round", + }, + }, + j_cry_translucent = { + name = "Translucent Joker", + text = { + "Sell this card to create", + "a {C:attention}Banana Perishable{} copy", + "of a random {C:attention}Joker{}", + "{s:0.8,C:inactive}(Copy bypasses perish compat)", + }, + }, + j_cry_treacherous = { + name = "Treacherous Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "an {C:attention}#2#" + } + }, + j_cry_trick_or_treat = { + name = "Trick-or-Treat", + text = { + "When {C:attention}sold{}:", + "{C:green}#1# in #2#{} chance to create {C:attention}2{} {C:cry_candy}Candies", + "Otherwise, create a {X:cry_cursed,C:white}Cursed{} Joker", + "{C:inactive}(Can overflow)" + } + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Triplet Rhythm", + text = { + "{X:mult,C:white} X#1# {} Mult if scoring hand", + "contains {C:attention}exactly{} three {C:attention}3s", + }, + }, + j_cry_tropical_smoothie = { + name = "Tropical Smoothie", + text = { + "Sell this card", + "to {C:attention}multiply{} values", + "of owned jokers by {C:attention}X1.5{}", + }, + }, + j_cry_unity = { + name = "The Unity", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_universe = { + name = "Universe", + text = { + "{C:dark_edition}Astral{} cards", + "each give {X:dark_edition,C:white}^#1#{} Mult", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Poker hands{} gain", + "{X:red,C:white} X#1# {} Mult and {X:blue,C:white} X#1# {} Chips", + "when leveled up", + }, + }, + j_cry_unjust_dagger = { + name = "Unjust Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and gain {C:attention}one-fifth{} of", + "its sell value as {X:mult,C:white} XMult {}", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "This Joker gains {C:money}$#1#{} of {C:attention}sell value{}", + "if {C:attention}poker hand{} contains a {C:attention}#2#{}", + "Sell this card to create a", + "{C:dark_edition}Polychrome{} {C:attention}Jolly Joker{} for", + "every {C:money}$4{} of {C:attention}sell value{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Wacky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "All Jokers give", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "All Jokers give", + "{C:money}$#1#{} when triggered", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "This Joker gains", + "{C:mult}+#2#{} Mult when each played", + "{C:attention}Ace{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "is scored", + "{C:inactive}(Currently {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Retrigger each played {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} additional time(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Wheel of Hope", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when failing", + "a {C:attention}Wheel of Fortune{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if {C:attention}played hand{} contains a", + "{C:attention}2{} and {C:attention}7{} of different suits", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_wrapped = { + name = "Wrapped Candy", + text = { + "Create a random {C:attention}Food Joker{}", + "in {C:attention}#1#{} round(s)", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_wtf = { + name = "The Fuck!?", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Kaikki = { + name = "Kaikki", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutron Star", + text = { + "Upgrade a random", + "poker hand by", + "{C:attention}1{} level for each", + "{C:attention}Neutron Star{} used", + "in this run", + "{C:inactive}(Currently{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# in #2#{} chance to", + "upgrade every", + "{C:legendary,E:1}poker hand{}", + "by {C:attention}1{} level", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_marsmoons = { + name = 'Phobos & Deimos', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){} Level up", + "{C:attention}#2#", + "{C:mult}+#3#{} Mult and", + "{C:chips}+#4#{} chips" + } + }, + c_cry_void = { + name = 'Void', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){} Level up", + "{C:attention}#2#", + "{C:mult}+#3#{} Mult and", + "{C:chips}+#4#{} chips" + } + }, + c_cry_asteroidbelt = { + name = 'Asteroid Belt', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){} Level up", + "{C:attention}#2#", + "{C:mult}+#3#{} Mult and", + "{C:chips}+#4#{} chips" + } + }, + c_cry_universe = { + name = 'The Universe In Its Fucking Entirety', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){} Level up", + "{C:attention}#2#", + "{C:mult}+#3#{} Mult and", + "{C:chips}+#4#{} chips" + } + }, + c_cry_sunplanet = { + name = 'Sol', + text = { + "{S:0.8}({S:0.8,V:1}lvl.#1#{S:0.8}){}", + "Increase power of", + "{C:attention}Ascended{} hands by {X:gold,C:white}0.05{}", + "{C:inactive}(Currently {X:gold,C:white}X(#2#^asc){C:inactive})" + } + }, + }, + Sleeve = { + sleeve_cry_bountiful_sleeve = { + name = "Bountiful Sleeve", + text = { + "After {C:blue}Play{} or {C:red}Discard{},", + "always draw {C:attention}5{} cards" + }, + }, + sleeve_cry_ccd_sleeve = { + name = "CCD Sleeve", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Conveyor Sleeve", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Critical Sleeve", + text = { + "After each hand played,", + "{C:green}1 in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}1 in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Encoded Sleeve", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balanced Sleeve", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}+2 Shop Slots", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unlimited Sleeve", + text = { + "You can select {C:attention}any", + "number of cards", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Misprinted Sleeve", + text = { + "Values of cards", + "are {C:attention}randomized", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Redeemed Sleeve", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + sleeve_cry_spooky_sleeve = { + name = "Spooky Sleeve", + text = { + "Start with an {C:eternal}Eternal{} {C:attention,T:j_cry_chocolate_dice}Chocolate Die", + "After each {C:attention}Ante{}, create a", + "{C:cry_candy}Candy{} or {X:cry_cursed,C:white}Cursed{} Joker", + } + }, + sleeve_cry_wormhole_sleeve = { + name = "Wormhole Sleeve", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + sleeve_cry_legendary_sleeve = { + name = "Legendary Sleeve", + text = { + "Start with an {C:legendary}Legendary{C:legendary} Joker", + "{C:green}1 in 5{} chance to create another", + "when Boss Blind is defeated {C:inactive}(must have room){}", + }, + }, + }, + Spectral = { + c_cry_adversary = { + name = "Adversary", + text = { + "{C:red}All{} of your {C:attention}Jokers{} become {C:dark_edition}Negative{},", + "{C:red}all{} {C:attention}Jokers{} in the shop cost", + "{C:red}double{} for the rest of the run", + }, + }, + c_cry_analog = { + name = "Analog", + text = { + "Create {C:attention}#1#{} copies of a", + "random {C:attention}Joker{}, destroy", + "all other Jokers, {C:attention}+#2#{} Ante", + }, + }, + c_cry_chambered = { + name = "Chambered", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "copies of a", + "{C:attention}random{} consumable", + "{C:inactive,s:0.8}Does not copy Chambered{}" + }, + }, + c_cry_conduit = { + name = "Conduit", + text = { + "Swap the {C:attention}editions{} of", + "{C:attention}2{} selected cards or {C:attention}Jokers{}", + }, + }, + c_cry_gateway = { + name = "Gateway", + text = { + "Create a random", + "{C:cry_exotic,E:1}Exotic{C:attention} Joker{}, destroy", + "all other Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Lock", + text = { + "Remove {C:red}all{} stickers", + "from {C:red}all{} Jokers,", + "then apply {C:purple,E:1}Eternal{}", + "to a random {C:attention}Joker{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Create a card", + "of {C:cry_code}your choice", + "{C:inactive,s:0.8}(Exotic Jokers #1#excluded)", + }, + }, + c_cry_replica = { + name = "Replica", + text = { + "Convert all cards", + "held in hand", + "to a {C:attention}random{}", + "card held in hand", + }, + }, + c_cry_ritual = { + name = "Ritual", + text = { + "Apply {C:dark_edition}Negative{}, {C:dark_edition}Mosaic{},", + "or {C:dark_edition}Astral{} to {C:attention}#1#{}", + "selected card in hand", + }, + }, + c_cry_source = { + name = "Source", + text = { + "Add a {C:cry_code}Green Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_summoning = { + name = "Summoning", + text = { + "Create a random", + "{C:cry_epic}Epic{} {C:joker}Joker{}, destroy", + "one random {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Trade", + text = { + "{C:attention}Lose{} a random Voucher,", + "gain {C:attention}2{} random Vouchers", + }, + }, + c_cry_typhoon = { + name = "Typhoon", + text = { + "Add an {C:cry_azure}Azure Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_vacuum = { + name = "Vacuum", + text = { + "Removes {C:red}all {C:green}modifications{}", + "from {C:red}all{} cards held in hand,", + "Earn {C:money}$#1#{} per {C:green}modification{} removed", + "{C:inactive,s:0.7}(ex. Enhancements, Seals, Editions)", + }, + }, + c_cry_white_hole = { + name = "White Hole", + text = { + "{C:attention}Remove{} all hand levels,", + "upgrade {C:legendary,E:1}most played{} poker hand", + "by {C:attention}3{} for each removed level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pink Stake", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brown Stake", + colour = "Brown", + text = { + "All {C:attention}stickers{} are compatible", + "with each other", + }, + }, + stake_cry_yellow = { + name = "Yellow Stake", + colour = "Yellow", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Jade Stake", + colour = "Jade", + text = { + "Cards can be drawn {C:attention}face down{}", + }, + }, + stake_cry_cyan = { + name = "Cyan Stake", + colour = "Cyan", + text = { + "{C:green}Uncommon{} and {C:red}Rare{} Jokers are", + "less likely to appear", + }, + }, + stake_cry_gray = { + name = "Gray Stake", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Crimson Stake", + colour = "Crimson", + text = { + "Vouchers restock on {C:attention}even{} Antes", + }, + }, + stake_cry_diamond = { + name = "Diamond Stake", + colour = "Diamond", + text = { + "Must beat Ante {C:attention}10{} to win", + }, + }, + stake_cry_amber = { + name = "Amber Stake", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Pack slot", + }, + }, + stake_cry_bronze = { + name = "Bronze Stake", + colour = "Bronze", + text = { + "Vouchers are {C:attention}50%{} more expensive", + }, + }, + stake_cry_quartz = { + name = "Quartz Stake", + colour = "Quartz", + text = { + "Jokers can be {C:attention}Pinned{}", + "{s:0.8,C:inactive}(Stays pinned to the leftmost position){}", + }, + }, + stake_cry_ruby = { + name = "Ruby Stake", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds can become", + "{C:attention}Boss{} Blinds", + }, + }, + stake_cry_glass = { + name = "Glass Stake", + colour = "Glass", + text = { + "Cards can {C:attention}shatter{} when scored", + }, + }, + stake_cry_sapphire = { + name = "Sapphire Stake", + colour = "Sapphire", + text = { + "Lose {C:attention}25%{} of current money", + "at end of Ante", + "{s:0.8,C:inactive}(Up to $10){}", + }, + }, + stake_cry_emerald = { + name = "Emerald Stake", + colour = "Emerald", + text = { + "Cards, packs, and vouchers", + "can be {C:attention}face down{}", + "{s:0.8,C:inactive}(Unable to be viewed until purchased){}", + }, + }, + stake_cry_platinum = { + name = "Platinum Stake", + colour = "Platinum", + text = { + "Small Blinds are {C:attention}removed{}", + }, + }, + stake_cry_twilight = { + name = "Twilight Stake", + colour = "Twilight", + text = { + "Cards can be {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 in 10 chance of being destroyed each round){}", + }, + }, + stake_cry_verdant = { + name = "Verdant Stake", + colour = "Verdant", + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Ember Stake", + colour = "Ember", + text = { + "All items give no money when sold", + }, + }, + stake_cry_dawn = { + name = "Dawn Stake", + colour = "Dawn", + text = { + "Tarots and Spectrals target {C:attention}1", + "fewer card", + "{s:0.8,C:inactive}(Minimum of 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizon Stake", + colour = "Horizon", + text = { + "When blind selected, add a", + "{C:attention}random card{} to deck", + }, + }, + stake_cry_blossom = { + name = "Blossom Stake", + colour = "Blossom", + text = { + "{C:attention}Final{} Boss Blinds can appear", + "in {C:attention}any{} Ante", + }, + }, + stake_cry_azure = { + name = "Azure Stake", + colour = "Azure", + text = { + "Values on Jokers are reduced", + "by {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Ascendant Stake", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop slot", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Banana Tag", + text = { + "Creates {C:attention}#1#", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_bettertop_up = { + name = "Better Top-up Tag", + text = { + "Creates up to {C:attention}#1#", + "{C:green}Uncommon{} Jokers", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_better_voucher = { + name = "Golden Voucher Tag", + text = { + "Adds one Tier {C:attention}#1#{} Voucher", + "to the next shop", + }, + }, + tag_cry_blur = { + name = "Blurred Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Blurred{}", + }, + }, + tag_cry_booster = { + name = "Booster Tag", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:attention}double{} cards and", + "{C:attention}double{} choices", + }, + }, + tag_cry_bundle = { + name = "Bundle Tag", + text = { + "Create a {C:attention}Standard Tag{}, {C:tarot}Charm Tag{},", + "{C:attention}Buffoon Tag{}, and {C:planet}Meteor Tag", + }, + }, + tag_cry_cat = { + name = "Cat Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Console Tag", + text = { + "Gives a free", + "{C:cry_code}Program Pack", + }, + }, + tag_cry_double_m = { + name = "Double M Tag", + text = { + "Shop has a", + "{C:dark_edition}Jolly {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Empowered Tag", + text = { + "Gives a free {C:spectral}Spectral Pack", + "with {C:legendary,E:1}The Soul{} and {C:cry_exotic,E:1}Gateway{}", + }, + }, + tag_cry_epic = { + name = "Epic Tag", + text = { + "Shop has a half-price", + "{C:cry_epic}Epic Joker", + }, + }, + tag_cry_gambler = { + name = "Gambler's Tag", + text = { + "{C:green}#1# in #2#{} chance to create", + "an {C:cry_exotic,E:1}Empowered Tag", + }, + }, + tag_cry_glass = { + name = "Fragile Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Fragile{}", + }, + }, + tag_cry_glitched = { + name = "Glitched Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Glitched{}", + }, + }, + tag_cry_gold = { + name = "Golden Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Golden{}", + }, + }, + tag_cry_gourmand = { + name = "Gourmand Tag", + text = { + "Shop has a free", + "{C:attention}Food Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gives a free", + "{C:cry_ascendant}Meme Pack", + }, + }, + tag_cry_m = { + name = "Jolly Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Jolly{}", + }, + }, + tag_cry_memory = { + name = "Memory Tag", + text = { + "Create {C:attention}#1#{} copies of", + "the last {C:attention}Tag{} used", + "during this run", + "{s:0.8,C:inactive}Copying Tags excluded", + "{s:0.8,C:inactive}Currently: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Mosaic{}", + }, + }, + tag_cry_oversat = { + name = "Oversaturated Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Oversaturated{}", + }, + }, + tag_cry_quadruple = { + name = "Quadruple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_quintuple = { + name = "Quintuple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_rework = { + name = "Rework Tag", + text = { + "Shop has a(n)", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schematic Tag", + text = { + "Shop has a", + "{C:attention}Brainstorm", + }, + }, + tag_cry_scope = { + name = "Scope Tag", + text = { + "{C:attention}+#1# {C:blue}hands{} and", + "{C:red}discards{} next round", + }, + }, + tag_cry_triple = { + name = "Triple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "The Automaton", + text = { + "Creates up to {C:attention}#1#", + "random {C:cry_code}Code{} card", + "{C:inactive}(Must have room)", + }, + }, + c_cry_eclipse = { + name = "The Eclipse", + text = { + "Enhances {C:attention}#1#{} selected card", + "into an {C:attention}Echo Card", + }, + }, + c_cry_meld = { + name = "Meld", + text = { + "Select a {C:attention}Joker{} or", + "{C:attention}playing card{} to", + "become {C:dark_edition}Double-Sided", + }, + }, + c_cry_theblessing = { + name = "The Blessing", + text = { + "Creates {C:attention}1{}", + "random {C:attention}consumable{}", + "{C:inactive}(Must have room){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglyph", + text = { + "Set Ante to {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Blank Canvas", + text = { + "{C:attention}+#1#{} hand size", + }, + }, + v_cry_clone_machine = { + name = "Clone Machine", + text = { + "Double Tags become", + "{C:attention}Quintuple Tags{} and", + "are {C:attention}4X{} as common", + }, + }, + v_cry_command_prompt = { + name = "Command Prompt", + text = { + "{C:cry_code}Code{} cards", + "can appear", + "in the {C:attention}shop{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Double Tags become", + "{C:attention}Triple Tags{} and are", + "{C:attention}2X{} as common", + }, + }, + v_cry_curate = { + name = "Curate", + text = { + "All cards", + "appear with", + "an {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Dexterity", + text = { + "Permanently", + "gain {C:blue}+#1#{} hand(s)", + "each round", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "After every round,", + "{X:dark_edition,C:white} X1.5 {} to all values", + "on the back of", + "{C:dark_edition}Double-Sided{} cards" + }, + }, + v_cry_double_slit = { + name = "Double Slit", + text = { + "{C:attention}Meld{} can appear", + "in the shop and", + "Arcana Packs", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "{C:dark_edition}Double-Sided{} cards appear", + "{C:attention}4X{} more frequently", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker slot(s)", + }, + }, + v_cry_massproduct = { + name = "Mass Production", + text = { + "All cards and packs", + "in shop cost {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Raise the cap on", + "interest earned in", + "each round to {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multistock", + text = { + "{C:attention}+#1#{} card slot(s) and", + "{C:attention}+#1#{} booster pack slot(s)", + "available in shop", + }, + }, + v_cry_pacclimator = { + name = "Planet Acclimator", + text = { + "{C:planet}Planet{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:planet}Planet{}", + "cards are {C:green}free{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Retrigger{} all M Jokers", + "once for every Pair", + "{C:attention}contained{} in played hand", + }, + }, + v_cry_pairing = { + name = "Pairing", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand is a {C:attention}Pair", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computing", + text = { + "{C:cry_code}Code{} cards can spawn", + "with {C:dark_edition}Negative{} edition", + }, + }, + v_cry_repair_man = { + name = "Repair Man", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand contains a {C:attention}Pair", + }, + }, + v_cry_rerollexchange = { + name = "Reroll Exchange", + text = { + "All rerolls", + "cost {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satellite Uplink", + text = { + "{C:cry_code}Code{} cards may", + "appear in any of", + "the {C:attention}Celestial Packs{}", + }, + }, + v_cry_scope = { + name = "Galactic Scope", + text = { + "Create the {C:planet}Planet", + "card for played", + "{C:attention}poker hand{}", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Tarot Acclimator", + text = { + "{C:tarot}Tarot{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:tarot}Tarot{}", + "cards are {C:green}free{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Printer", + text = { + "Double Tags become", + "{C:attention}Quadruple Tags{} and", + "are {C:attention}3X{} as common", + }, + }, + v_cry_threers = { + name = "The 3 Rs", + text = { + "Permanently", + "gain {C:red}+#1#{} discard(s)", + "each round", + }, + }, + v_cry_stickyhand = { + name = "Sticky Hand", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + v_cry_grapplinghook = { + name = "Grappling Hook", + text = { + "{C:attention}+#1#{} card selection limit", + "{C:inactive,s:0.7}You can do a lot more with this than you think.{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperspace Tether", + text = { + "{C:attention}+#1#{} card selection limit", + "All selected cards contribute power", + "to {C:attention}Ascended Hands{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "destroyed each round", + }, + }, + cry_rigged = { + name = "Rigged", + text = { + "All {C:cry_code}listed{} probabilities", + "are {C:cry_code}guaranteed", + }, + }, + cry_hooked = { + name = "Hooked", + text = { + "When this Joker is {C:cry_code}triggered{},", + "trigger {C:cry_code}#1#", + }, + }, + cry_flickering = { + name = "Flickering", + text = { + "Destroyed after", + "{C:attention}#1#{} triggers", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)" + }, + }, + cry_flickering_desc = { --used by choco dice + name = "Flickering", + text = { + "Destroyed after", + "{C:attention}#1#{} triggers", + }, + }, + cry_possessed = { + name = "Possessed", + text = { + "{C:attention}Disables{} and {C:attention}reverses{}", + "effects, if possible", + "Destroyed along with {C:attention}Ghost" + }, + }, + --todo? add candy jokers to list + food_jokers = { + name = "Food Jokers", + text = { + "{s:0.8}Gros Michel, Egg, Ice Cream, Cavendish,", + "{s:0.8}Turtle Bean, Diet Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Pickle, Chili Pepper, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + ev_cry_choco0 = { + name = "", + text = { + "Details of an active", + "{C:cry_ascendant,E:1}event{} will appear here" + } + }, + ev_cry_choco1 = { + name = "1: Possession", + text = { + "{C:attention}Jokers{} and playing cards have a", + "{C:green}1 in 3{} chance of gaining Flickering", + "Create a {C:attention}Ghost", + "{C:inactive,s:0.7}You've been possessed by a ghost, and your", + "{C:inactive,s:0.7}consciousness is flickering in and out." + } + }, + ev_cry_choco2 = { + name = "2: Haunted House", + text = { + "Prevents skipping {C:attention}Blind{}", + "One {C:attention}reroll{} allowed per shop", + "{C:attention}Voucher{} prices are doubled", + "{C:inactive,s:0.7}Spooky spirits have taken over! Don't touch", + "{C:inactive,s:0.7}anything and get out as soon as possible!", + } + }, + ev_cry_choco3 = { + name = "3: Witch's Brews", + text = { + "Create 3 {C:attention}Potions", + "Use one by the end of the {C:attention}Small Blind{},", + "or {C:attention}all{} maluses will be applied this {C:attention}Ante", + "{C:inactive,s:0.7}You have been kidnapped by a witch!", + "{C:inactive,s:0.7}She offers you three potions, watching you closely.", + "{C:inactive,s:0.7}Pick one, lest she makes the decision for you.", + } + }, + ev_cry_choco4 = { + name = "4: Lunar Abyss", + text = { + "Played cards have a {C:green}1 in 4{} chance", + "to turn into a random {C:club}Club{} face card", + "Divide {C:attention}Mult{} by number of played face cards", + "{C:inactive,s:0.7}Even a man who's pure at heart", + "{C:inactive,s:0.7}and says his prayers by night..." + } + }, + ev_cry_choco5 = { + name = "5: Bloodsucker", + text = { + "Remove {C:attention}Enhancements{} from all played cards", + "{C:green}1 in 3{} chance to destroy", + "{C:heart}Heart{} and {C:diamond}Diamond{} cards", + "{C:inactive,s:0.7}Be wary in the dead of night, for", + "{C:inactive,s:0.7,E:1}they in the shadows{C:inactive,s:0.7} seek to quench their thirst..." + } + }, + ev_cry_choco6 = { + name = "6: Please Take One", + text = { + "At {C:attention}end of round{}, open a", + "random {C:attention}Booster{} Pack", + "{C:inactive,s:0.7}As you stroll down the streets, you spot a", + "{C:inactive,s:0.7}box of various Booster Packs. Might as well grab one!" + } + }, + ev_cry_choco7 = { + name = "7: Festive Atmosphere", + text = { + "Create 3 {C:attention}Trick-or-Treat{} and 1 {C:attention}Candy Basket", + "Shops have a {C:attention}Trick-or-Treat{} each round", + "{C:cry_candy}Candies{} give {C:money}$3{} when obtained", + "{C:inactive,s:0.7}The entire neighbourhood is decorated for spooky endeavours,", + "{C:inactive,s:0.7}come indulge in the festive atmosphere!" + } + }, + ev_cry_choco8 = { + name = "8: Candy Rain", + text = { + "When {C:attention}Blind{} defeated, obtain 1 {C:cry_candy}Candy{}", + "per hand remaining; Obtain a {C:attention}Food Joker{}", + "when a {C:cry_candy}Candy{} is generated", + "{C:inactive,s:0.7}Candies rain from the sky! Quick,", + "{C:inactive,s:0.7,E:1}grab as much as you can!" + } + }, + ev_cry_choco9 = { + name = "9: Ghostly Riches", + text = { + "Gain {C:money}$20", + "All {C:money}money{} earned is {C:attention}doubled", + "{C:inactive,s:0.7}The spectre of a long-gone relative of yours", + "{C:inactive,s:0.7}visits you in the middle of the night!", + "{C:inactive,s:0.7}Without a word, they place a bag of money in your hands,", + "{C:inactive,s:0.7}smile warmly, and wave as they fade into the air.", + } + }, + ev_cry_choco10 = { + name = "10: Revered Antique", + text = { + "A {C:legendary}Legendary{} {C:attention}Joker{} appears", + "in the {C:attention}Voucher{} slot for {C:money}$50", + "Only buyable as {C:attention}last{} item in shop", + "{C:inactive,s:0.7}You've attracted the attention of a relic's spirit,", + "{C:inactive,s:0.7}but it won't be easy to quell.", + } + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eternal", + text = { + "All cards in pack", + "are {C:attention}Eternal{}", + }, + }, + cry_perishable_booster = { + name = "Perishable", + text = { + "All cards in pack", + "are {C:attention}Perishable{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "All cards in pack", + "are {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Pinned", + text = { + "All cards in pack", + "are {C:attention}Pinned{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "All cards in pack", + "are {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eternal", + text = { + "Can't be traded", + }, + }, + cry_perishable_voucher = { + name = "Perishable", + text = { + "Debuffed after", + "{C:attention}#1#{} rounds", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)", + }, + }, + cry_rental_voucher = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at", + "end of round", + }, + }, + cry_pinned_voucher = { + name = "Pinned", + text = { + "Remains in shop", + "until redeemed", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "unredeemed each round", + }, + }, + cry_perishable_consumeable = { + name = "Perishable", + text = { + "Debuffed at", + "end of round", + }, + }, + cry_rental_consumeable = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at end of", + "round, and on use", + }, + }, + cry_pinned_consumeable = { + name = "Pinned", + text = { + "Can't use other", + "non-{C:attention}Pinned{} consumables", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance to do", + "nothing on use", + }, + }, + p_cry_code_normal_1 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_normal_2 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_two = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_three = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + undiscovered_code = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + undiscovered_unique = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + cry_green_seal = { + name = "Green Seal", + text = { + "Creates a {C:cry_code}Code{} card", + "when played and unscoring", + "{C:inactive}(Must have room)", + }, + }, + cry_azure_seal = { + name = "Azure Seal", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planets{} for played", + "{C:attention}poker hand{}, then", + "{C:red}destroy{} this card", + }, + }, + blurred_sdm0 = { + name = "a", + text = { + "{C:inactive,s:0.8}\"I hate this card\" - SDM_0, 2024{}", + }, + }, + }, + Unique = { + c_cry_potion = { + name = "Potion", + text = { + "Applies an unknown", + "{C:attention}malus{} when used", + "{C:inactive,s:0.7}Obtained from Chocolate Die" + } + } + } + }, + misc = { + poker_hands = { + ['cry_Bulwark'] = "Bulwark", + ['cry_Clusterfuck'] = "Clusterfuck", + ['cry_UltPair'] = "Ultimate Pair", + ['cry_WholeDeck'] = "The Entire Fucking Deck", + }, + poker_hand_descriptions = { + ['cry_Bulwark'] = { + '5 rankless, suitless cards', + }, + ['cry_Clusterfuck'] = { + 'At least 8 cards that do not', + 'contain a Pair, Flush, or Straight', + }, + ['cry_UltPair'] = { + 'Two Two Pairs, where each', + 'Two Pair is a single suit, for a', + 'total of two suits between them', + }, + ['cry_WholeDeck'] = { + 'A hand that contains every single', + 'card found in a 52-card deck.', + 'Are you insane?', + }, + }, + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Legally Blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Break Infinity", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Home Realtor", + ach_cry_jokes_on_you = "Joke's on You, Pal!", + ach_cry_niw_uoy = "!niW uoY", + ach_cry_now_the_fun_begins = "Now the Fun Begins", + ach_cry_patience_virtue = "Patience is a Virtue", + ach_cry_perfectly_balanced = "Perfectly Balanced", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Traffic Jam", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "We Told You Not To", + ach_cry_what_have_you_done = "WHAT HAVE YOU DONE?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtain Blurred Blurred Joker", + ach_cry_bullet_hell = "Have 15 AP Jokers", + ach_cry_break_infinity = "Score 1.79e308 Chips in a single hand", + ach_cry_cryptid_the_cryptid = "Use Cryptid on Cryptid", + ach_cry_exodia = "Have 5 Exotic Jokers", + ach_cry_freak_house = "Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice", + ach_cry_googol_play_pass = "Rig a Googol Play Card", + ach_cry_haxxor = "Use a cheat code", + ach_cry_home_realtor = "Activate Happy House before Ante 8 (without DoE/Antimatter)", + ach_cry_jokes_on_you = "Trigger The Joke's effect on Ante 1 and win the run", + ach_cry_niw_uoy = "Reach Ante -8", + ach_cry_now_the_fun_begins = "Obtain Canvas", + ach_cry_patience_virtue = "Wait out Lavender Loop for 2 minutes before playing first hand and beat the blind", + ach_cry_perfectly_balanced = "Beat Very Fair Deck on Ascendant Stake", + ach_cry_pull_request = "Have ://COMMIT spawn the same Joker that it destroyed", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Win in 1 round", + ach_cry_used_crash = "Use ://CRASH", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Music", + cry_set_enable_features = "Select features to enable (applies on game restart):", + cry_feat_achievements = "Achievements", + ["cry_feat_antimatter deck"] = "Antimatter Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Code Cards", + ["cry_feat_misc. decks"] = "Misc. Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechanics", + ["cry_feat_enhanced decks"] = "Enhanced Decks", + ["cry_feat_epic jokers"] = "Epic Jokers", + ["cry_feat_exotic jokers"] = "Exotic Jokers", + ["cry_feat_m jokers"] = "M Jokers", + cry_feat_menu = "Custom Main Menu", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Misc. Jokers", + cry_feat_planets = "Planets", + cry_feat_jokerdisplay = "JokerDisplay (Does Nothing)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Sleeves", + cry_feat_spectrals = "Spectrals", + cry_feat_spooky = "Spooky Update", + ["cry_feat_more stakes"] = "Stakes", + cry_feat_vouchers = "Vouchers", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Pack", + k_cry_meme_pack = "Meme Pack", + + cry_critical_hit_ex = "Critical Hit!", + cry_critical_miss_ex = "Critical Miss!", + + cry_potion1 = "-1 to all Hand Levels", + cry_potion2 = "X1.15 Blind size", + cry_potion3 = "-1 Hand and Discard", + + cry_debuff_oldhouse = "No Full Houses", + cry_debuff_oldarm = "Must play 4 or fewer cards", + cry_debuff_oldpillar = "No Straights", + cry_debuff_oldflint = "No Flushes", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applies abilities of all defeated bosses", + + k_code = "Code", + k_unique = "Unique", + b_code_cards = "Code Cards", + b_unique_cards = "Unique Cards", + b_pull = "PULL", + cry_hooked_ex = "Hooked!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTER RANK", + cry_code_enh = "ENTER ENHANCEMENT", + cry_code_hand = "ENTER POKER HAND", + cry_code_enter_card = "ENTER A CARD", + cry_code_apply = "APPLY", + cry_code_apply_previous = "APPLY PREVIOUS", + cry_code_exploit = "EXPLOIT", + cry_code_exploit_previous = "EXPLOIT PREVIOUS", + cry_code_create = "CREATE", + cry_code_create_previous = "CREATE PREVIOUS", + cry_code_execute = "EXECUTE", + cry_code_cancel = "CANCEL", + + b_flip = "FLIP", + b_merge = "MERGE", + + cry_hand_bulwark = "Bulwark", + cry_hand_clusterfuck = "Clusterfuck", + cry_hand_ultpair = "Ultimate Pair", + + cry_asc_hands = "Asc. Hands", + cry_p_star = "Star", + + cry_again_q = "Again?", + cry_curse = "Curse", + cry_curse_ex = "Curse!", + cry_sobbing = "Help me...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_good_luck_ex = "Good luck!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Round", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "No triggers left!", + cry_unredeemed = "Unredeemed...", + cry_active = "Active", + cry_inactive = "Inactive", + + k_disable_music = "Disable Music", + + k_cry_epic = "Epic", + k_cry_exotic = "Exotic", + k_cry_candy = "Candy", + k_cry_cursed = "Cursed", + k_planet_disc = "Circumstellar Disc", + k_planet_satellite = "Natural Satellites", + k_planet_universe = "The Actual Fucking Universe", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Copyright Notice", + cry_notif_jimball_d1 = "Jimball plays the song \"Funkytown\",", + cry_notif_jimball_d2 = "which is copyrighted and can't be", + cry_notif_jimball_d3 = "used for streams and videos.", + }, + labels = { + food_jokers = "Food Jokers", + banana = "Banana", + code = "Code", + unique = "Unique", + cry_rigged = "Rigged", + cry_hooked = "Hooked", + cry_flickering = "Flickering", + cry_possessed = "Possessed", + + cry_green_seal = "Green Seal", + cry_azure_seal = "Azure Seal", + + cry_astral = "Astral", + cry_blur = "Blurred", + cry_double_sided = "Double-Sided", + cry_glass = "Fragile", + cry_glitched = "Glitched", + cry_gold = "Golden", + cry_m = "Jolly", + cry_mosaic = "Mosaic", + cry_noisy = "Noisy", + cry_oversat = "Oversaturated", + + k_cry_epic = "Epic", + k_cry_exotic = "Exotic", + k_cry_candy = "Candy", + k_cry_cursed = "Cursed", + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_candy = {"+#1# Candy"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + + cry_art = {"Art: #1#"}, + cry_code = {"Code: #1#"}, + cry_idea = {"Idea: #1#"} + }, + v_text = { + ch_c_cry_all_perishable = {"All Jokers are {C:eternal}Perishable{}"}, + ch_c_cry_all_rental = {"All Jokers are {C:eternal}Rental{}"}, + ch_c_cry_all_pinned = {"All Jokers are {C:eternal}Pinned{}"}, + ch_c_cry_all_banana = {"All Jokers are {C:eternal}Banana{}"}, + ch_c_all_rnj = {"All Jokers are {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"All purchasable items have all stickers"}, + ch_c_cry_rush_hour = {"All Boss Blinds are {C:attention}The Clock{} or {C:attention}Lavender Loop"}, + ch_c_cry_rush_hour_ii = {"All Blinds are {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}The Clock{} and {C:attention}Lavender Loop{} scale {C:attention}twice{} as fast"}, + ch_c_cry_no_tags = {"Skipping is {C:attention}disabled{}"}, + ch_c_cry_no_vouchers = {"{C:attention}Vouchers{} no longer appear in the shop"}, + ch_c_cry_no_boosters = {"{C:attention}Booster Packs{} no longer appear in the shop"}, + ch_c_cry_no_rerolls = {"Rerolling is {C:attention}disabled{}"}, + ch_c_cry_no_consumables = {"{C:attention}Consumables{} no longer appear"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/es_419.lua b/Cryptid/localization/es_419.lua new file mode 100644 index 0000000..86330f2 --- /dev/null +++ b/Cryptid/localization/es_419.lua @@ -0,0 +1,3854 @@ +-- temporary replacement for mexican spanish +-- until someone does it, the other version should do +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Baraja de antimateria", + text = { + "Aplica las {C:legendary,E:1}ventajas{}", + "de {C:attention}todas{} las barajas", + }, + }, + b_cry_beta = { + name = "Baraja nostálgica", + text = { + "Las ranuras de {C:attention}comodín{} y", + "{C:attention}consumibles{} se {C:attention}combinan", + "Las ciegas {C:attention}nostálgicas{} reemplazan", + "sus ciegas actualizadas" + }, + }, + b_cry_blank = { + name = "Baraja en blanco", + text = { + "{C:inactive}¿No hace nada?", + }, + }, + b_cry_bountiful = { + name = "Baraja abundante", + text = { + "Siempre saca 5 cartas después", + "de cada {C:attention}mano jugada{} o {C:attention}descarte{}", + }, + }, + b_cry_CCD = { + name = "Baraja CCD", + text = { + "Todas las cartas también son", + "un consumible {C:attention}aleatorio{}", + }, + }, + b_cry_conveyor = { + name = "Baraja transportadora", + text = { + "Los comodines {C:attention}no{} se pueden mover", + "Al principio de la runda,", + "{C:attention}duplica{} el comodín del extremo derecho", + "y {C:attention}destruye{} el comodín del extremo izquierdo", + }, + }, + b_cry_critical = { + name = "Baraja crítica", + text = { + "Después de cada mano jugada,", + "{C:green}#1# en 4{} probabilidades para {X:dark_edition,C:white} ^2 {} multi", + "{C:green}#1# en 8{} probabilidades para {X:dark_edition,C:white} ^0.5 {} multi", + }, + }, + b_cry_encoded = { + name = "Baraja codificada", + text = { + "Comienza con un {C:cry_code,T:j_cry_CodeJoker}Comodín de código{}", + "y {C:cry_code,T:j_cry_copypaste}Copiar y pegar{}", + "Sólo aparecen {C:cry_code}cartas de código{} en la tienda", + }, + }, + b_cry_equilibrium = { + name = "Baraja de equilibrio", + text = { + "Todas las cartas tienen la ", + "{C:attention}misma probabilidad{} de", + "aparecer en las tiendas,", + "comienza la partida con", + "{C:attention,T:v_overstock_plus}Excedente plus", + }, + }, + b_cry_glowing = { + name = "Baraja brillante", + text = { + "Multiplica los valores de", + "todos los comodines por {X:dark_edition,C:white} X1.25 {}", + "cuando se derrota a la {C:attention}ciega jefe{}", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Baraja infinita", + text = { + "Puedes seleccionar {C:attention}cualquier", + "cantidad de cartas", + "{C:attention}+1{} tamaño de mano", + }, + }, + b_cry_misprint = { + name = "Baraja de errata", + text = { + "Los valures de cartas", + "y manos de póker", + "se {C:attention}aleatorizan", + }, + }, + b_cry_redeemed = { + name = "Baraja redimida", + text = { + "Cuando se compra un {C:attention}vale{},", + "obtén sus {C:attention}niveles extras", + }, + }, + b_cry_spooky = { + name = "Baraja espeluznante", + text = { + "Comienza con un {C:attention,T:j_cry_chocolate_dice}Dado de chocolate {C:eternal}eterno{}", + "Después de cada {C:attention}apuesta{}, crea un comodín", + "de {C:cry_candy}dulce{} o {X:cry_cursed,C:white}maldito{}", + } + }, + b_cry_very_fair = { + name = "Baraja Muy Justa", + text = { + "{C:blue}-2{} manos, {C:red}-2{} descartes", + "en cada ronda", + "Los {C:attention}vales{} ya no", + "aparecen en la tienda", + }, + }, + b_cry_wormhole = { + name = "Baraja de agujero de gusano", + text = { + "Comienza con un comodín {C:cry_exotic}exótico{C:attention}", + "Los comodines son {C:attention}20X{} más", + "probables de ser {C:dark_edition}Negativos", + "{C:attention}-2{} ranuras de comodín", + }, + }, + b_cry_legendary = { + name = "Baraja legendaria", + text = { + "Comienza con un comodín {C:legendary}legendario{C:legendary}", + "{C:green}1 en 5{} probabilidades para crear otro", + "cuando se derrota a la ciega jefe", + "{C:inactive}(debe haber espacio){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "La caja", + text = { + "Todos los comodines comunes", + "se debilitan", + }, + }, + bl_cry_clock = { + name = "El reloj", + text = { + "+0.1X requisitos de ciega por cada", + "3 segundos pasados en esta apuesta", + }, + }, + bl_cry_hammer = { + name = "El martillo", + text = { + "Todas las cartas con rango", + "impar se debilitan", + }, + }, + bl_cry_joke = { + name = "La broma", + text = { + "Si la puntuación excede 2X de los requisitos,", + "establece la apuesta a un múltiplo de #1#", + }, + }, + bl_cry_magic = { + name = "La magia", + text = { + "Todas las cartas con rango", + "par se debilitan", + }, + }, + bl_cry_lavender_loop = { + name = "Ciclo lavanda", + text = { + "1.25X requisitos de ciega por cada", + "1.5 segundos pasados en esta ronda", + }, + }, + bl_cry_obsidian_orb = { + name = "Orbe obsidiana", + text = { + "Aplica las habilidades de", + "todos los jefes derrotados", + }, + }, + bl_cry_oldarm = { + name = "El brazo nostálgico", + text = { + "Debes jugar 4", + "o menos cartas", + }, + }, + bl_cry_oldfish = { + name = "El pez nostálgico", + text = { + "Todas las manos empiezan", + "con 1 multi", + }, + }, + bl_cry_oldflint = { + name = "El pedernal nostálgico", + text = { + "Sin colores", + }, + }, + bl_cry_oldhouse = { + name = "La casa nostálgica", + text = { + "Sin fulls", + }, + }, + bl_cry_oldmanacle = { + name = "El grillete nostálgico", + text = { + "Divide multi por descartes", + }, + }, + bl_cry_oldmark = { + name = "La marca nostálgica", + text = { + "Sin manos que", + "contengan una Pareja", + }, + }, + bl_cry_oldox = { + name = "El buey nostálgico", + text = { + "Todas las manos empiezan", + "con 0 fichas", + }, + }, + bl_cry_oldpillar = { + name = "El pilar nostálgico", + text = { + "Sin escaleras", + }, + }, + bl_cry_oldserpent = { + name = "La serpiente nostálgica", + text = { + "Divide multi por el nivel", + "de la mano de póker jugada", + }, + }, + bl_cry_pin = { + name = "El alfiler", + text = { + "Los comodines épicos o de mayor", + "rareza se debilitan", + }, + }, + bl_cry_pinkbow = { + name = "Moño rosado", + text = { + "Aleatoriza la categoría de las cartas", + "en tu mano al jugar", + }, + }, + bl_cry_sapphire_stamp = { + name = "Estampilla safiro", + text = { + "Selecciona una carta extra, deselecciona", + "una carta aleatoria antes de puntuar", + }, + }, + bl_cry_shackle = { + name = "El eslabón", + text = { + "Todos los comodines negativos", + "se debilitan", + }, + }, + bl_cry_striker = { + name = "El ariete", + text = { + "Todos los comodines raros", + "se debilitan", + }, + }, + bl_cry_tax = { + name = "El impuesto", + text = { + "Puntuación por mano limitado a", + "0.4X requisitos de ciega", + }, + }, + bl_cry_tornado = { + name = "Tornado turquesa", + text = { + "#1# en #2# probabilidades para", + "que la mano jugada no puntúe", + }, + }, + bl_cry_trick = { + name = "El truco", + text = { + "Después de cada mano, voltea todas", + "las cartas boca-arriba en tu mano", + }, + }, + bl_cry_vermillion_virus = { + name = "Virus bermellón", + text = { + "Se reemplaza un comodín", + "al azar en cada mano", + }, + }, + bl_cry_windmill = { + name = "El molino", + text = { + "Todos los comodines inusuales", + "se debilitan", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASE", + text = { + "Convierte {C:cry_code}#1#{} carta seleccionada", + "a una mejora {C:cry_code}a elección{}", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destruye un comodín {C:cry_code}seleccionado{},", + "crea un comodín {C:cry_code}nuevo{}", + "de la {C:cry_code}misma rareza", + }, + }, + c_cry_crash = { + name = "://CHOQUE", + text = { + "{C:cry_code,E:1}No.", + }, + }, + c_cry_ctrl_v = { + name = "://CTRL+V", + text = { + "Crea una {C:cry_code}copia{} de un {C:cry_code}comodín{},", + "carta de juego, o consumible" + }, + }, + c_cry_delete = { + name = "://ELIMINAR", + text = { + "Remueve {C:cry_code}permanentemente{} un", + "objeto {C:cry_code}seleccionado{} de la tienda", + "{C:inactive,s:0.8}El objeto no puede aparecer otra vez en esta partida", + }, + }, + c_cry_divide = { + name = "://DIVIDIR", + text = { + "{C:cry_code}Divide en 2{} todos los precios", + "listados en la tienda actual", + }, + }, + c_cry_exploit = { + name = "://EXPLOTAR", + text = { + "Cualquier mano jugada se considera que", + "{C:cry_code}contiene{} una mano de póker {C:cry_code}a elección{},", + "se reinicia al final de la ronda", + "{C:inactive,s:0.8}Las manos secretas deben ser", + "{C:inactive,s:0.8}descubiertas para ser válidas", + }, + }, + c_cry_hook = { + name = "ENGANCHAR://", + text = { + "Selecciona dos comodines para {C:cry_code}engancharlos", + "{C:inactive,s:0.8}Sólo funciona correctamente si los comodines se activan en el mismo contexto,", + "{C:inactive,s:0.8}(ej. Comodín y El dúo (ambos después de puntuar))", + }, + }, + c_cry_machinecode = { + name = "://CÓDIGOMÁQUINA", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { + "Añade {C:dark_edition}Erróneo{} a todas", + "las cartas {C:cry_code}en tu mano" }, + }, + c_cry_merge = { + name = "://FUNDIR", + text = { + "Combina un {C:cry_code}consumible{} seleccionado", + "con una {C:cry_code}carta de juego{} seleccionada", + }, + }, + c_cry_multiply = { + name = "://MULTIPLICAR", + text = { + "{C:cry_code}Duplica{} todos los valores de", + "un {C:cry_code}comodín{} seleccionado hasta", + "el final de la ronda", + }, + }, + c_cry_patch = { + name = "://PATCH", + text = { + "Rehabilita y remueve todas las pegatinas", + "de todos los objetos visibles", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "La siguiente ciega derrotada", + "entrega {C:cry_code}X#1#{} interés", + }, + }, + c_cry_oboe = { + name = "://PORUNPASO", + text = { + "El siguiente {C:cry_code}paquete potenciador{} contiene", + "{C:cry_code}#1#{} carta adicional y", + "{C:cry_code}#1#{} opción adicional", + "{C:inactive}(Actual: {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REINICIAR", + text = { + "Reobtiene tus {C:blue}manos{} y {C:red}descartes{},", + "devuelve {C:cry_code}todas{} las cartas a la baraja", + "y saca una {C:cry_code}nueva{} mano", + }, + }, + c_cry_revert = { + name = "://REVERTIR", + text = { + "Establese el {C:cry_code}estado de juego{} al", + "inicio de {C:cry_code}esta apuesta{}", + }, + }, + c_cry_rework = { + name = "://REHACER", + text = { + "Destruye un comodín {C:cry_code}seleccionado{},", + "crea una {C:cry_code}Etiqueta de retrabajo{} con", + "una edición {C:cry_code}mejorada{}", + "{C:inactive,s:0.8}Se mejora usando el orden en la colección", + }, + }, + c_cry_run = { + name = "://CORRER", + text = { + "Visita una {C:cry_code}tienda", + "durante una {C:cry_code}ciega", + }, + }, + c_cry_seed = { + name = "://SEMILLA", + text = { + "Selecciona un comodín", + "o carta de juego", + "para volverla {C:cry_code}amañada", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { + "Termina la {C:cry_code}ciega{} no-jefe actual", + "{C:cry_code}sin{} obtener dinero" + }, + }, + c_cry_spaghetti = { + name = "://ESPAGUETI", + text = { + "Crea un comodín de comida", + "{C:cry_code}erróneo", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convierte {C:cry_code}#1#{} cartas seleccionadas", + "a una categoría {C:cry_code}a elección{}", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} multi", + }, + }, + e_cry_blur = { + name = "Borrosa", + text = { + "{C:attention}Reactiva{} esta", + "carta {C:attention}1{} vez", + "{C:green}#1# en #2#{} probabilidades", + "para reactivar {C:attention}#3#{}", + "vez más", + }, + }, + e_cry_double_sided = { + name = "Doble cara", + text = { + "Esta carta puede ser", + "{C:attention}volteada{} para revelar", + "una carta diferente", + "{C:inactive}(El lado en blanco puede", + "{C:inactive}combinarse con otra carta)", + }, + }, + e_cry_glass = { + name = "Frágil", + label = "Frágil", + text = { + "{C:white,X:mult} X#3# {} multi", + "{C:green}#1# en #2#{} probabilidades que", + "esta carta no sea {C:red}destruida", + "al activarse", + }, + }, + e_cry_glitched = { + name = "Errónea", + text = { + "Todos los valores en esta carta", + "son {C:dark_edition}aleatorizados{}", + "entre {C:attention}X0.1{} y {C:attention}X10{}", + "{C:inactive}(si es posible){}", + }, + }, + e_cry_gold = { + name = "Dorada", + label = "Dorada", + text = { + "Gana {C:money}$#1#{} cuando se usa", + "o activa", + }, + }, + e_cry_m = { + name = "Contenta", + text = { + "{C:mult}+#1#{} multi", + "Esta carta se siente", + "bastante {C:attention}contenta{}", + }, + }, + e_cry_mosaic = { + name = "Mosaico", + text = { + "{X:chips,C:white} X#1# {} fichas", + }, + }, + e_cry_noisy = { + name = "Ruidosa", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Sobresaturada", + text = { + "Todos los valores", + "en esta carta", + "son {C:attention}duplicados{}", + "{C:inactive}(si es posible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Carta de eco", + text = { + "{C:green}#2# en #3#{} probabilidades de", + "{C:attention}reactivar{} #1# veces", + "adicionales al puntuar", + }, + }, + }, + Joker = { + j_cry_adroit = { + name = "Comodín hábil", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_altgoogol = { + name = "Carta de Googol Play nostálgica", + text = { + "Vende esta carta para crear", + "{C:attention}2{} copias del {C:attention}Joker{} del extremo izquierdo", + "{C:inactive,s:0.8}No copia otras Cartas de Googol Play nostálgicas{}", + }, + }, + j_cry_antennastoheaven = { + name = "...como antenas al cielo", + text = { + "Este comodín consigue", + "{X:chips,C:white} X#1# {} fichas cuando cada", + "{C:attention}7{} o {C:attention}4{} jugado puntúa", + "{C:inactive}(Actual: {X:chips,C:white}X#2# {C:inactive} fichas)", + }, + }, + j_cry_apjoker = { + name = "Comodín AP", + text = { "{X:mult,C:white} X#1# {} multi contra las {C:attention}ciegas jefe{}" }, + }, + j_cry_astral_bottle = { + name = "Astral en una botella", + text = { + "Al venderse, aplica {C:dark_edition}Astral{}", + "y {C:attention}Perecedero{} a", + "un {C:attention}comodín{} aleatorio", + } + }, + + j_cry_big_cube = { + name = "Cubo grande", + text = { + "{X:chips,C:white} X#1# {} fichas", + }, + }, + j_cry_biggestm = { + name = "Gigante", + text = { + "{X:mult,C:white} X#1# {} multi hasta el final", + "de la ronda si la {C:attention}mano de póker{}", + "es una {C:attention}#2#{}", + "{C:inactive}(Actual: {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}no es obeso, sólo de huesos grandes.", + }, + }, + j_cry_blacklist = { + name = "Lista negra", + text = { + "Si un {C:attention}#1#{} se juega o está en tu mano,", + "establece {C:chips}fichas{} y {C:mult}multi{} a 0", + "{C:red,E:2}se autodestruye{} si no hay un {C:attention}#1#{} en la baraja", + "{C:inactive,s:0.8}La categoría no cambia" + } + }, + j_cry_blender = { + name = "Blender", + text = { + "Crea un consumible", + "{C:attention}aleatorio{} cuando una", + "carta de {C:cry_code}código{} es usada", + "{C:inactive}(debe haber espacio){}", + }, + }, + j_cry_blurred = { + name = "Comodín borroso", + text = { + "Obtén {C:blue}+#1#{} mano(s) al", + "seleccionar una {C:attention}ciega{}", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Cada {C:attention}comodín{} da {C:chips}+#1#{} fichas", + "aumenta la cantidad por {C:chips}+#2#{} si la ", + "{C:attention}mano de póker{} es una {C:attention}#3#{}", + "{C:inactive,s:0.8}Los comodines contentos dan{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}fichas{}", + }, + }, + j_cry_bonkers = { + name = "Comodín trastornado", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_bonusjoker = { + name = "Comodín adicional", + text = { + "{C:green}#1# en #2#{} probabilidades por cada", + "carta {C:attention}adicional{} jugada para aumentar", + "ranuras de {C:attention}comodín{} o {C:attention}consumible", + "por {C:dark_edition}1{} al puntuar", + "{C:red}Funciona 2 veces por runda", + "{C:inactive,s:0.8}(probabilidad igual por cada uno){}", + }, + }, + j_cry_booster = { + name = "Comodín potenciador", + text = { + "{C:attention}+#1#{} ranura de paquete potenciador", + "disponible en la tienda", + }, + }, + j_cry_boredom = { + name = "Aburrimiento", + text = { + "{C:green}#1# en #2#{} probabilidades para", + "{C:attention}reactivar{} cada {C:attention}comodín{}", + "o {C:attention}carta jugada{}", + "{C:inactive,s:0.8}No afecta otros Aburrimientos{}", + }, + }, + j_cry_brittle = { + name = "Dulce frágil", + text = { + "Por las siguientes {C:attention}#1#{} manos,", + "añade {C:attention}Piedra{}, {C:attention}Oro{}, o {C:attention}Acero{} a", + "la carta del extremo derecho que puntúa" + } + }, + j_cry_bubblem = { + name = "Burbuja M", + text = { + "Crea un {C:attention}Comodín contento{} {C:dark_edition}laminado", + "si la mano jugada contiene", + "un {C:attention}#1#{}", + "{C:red,E:2}se autodestruye{}", + }, + }, + j_cry_busdriver = { + name = "Conductor de bus", + text = { + "{C:green}#1# en #3#{} probabilidades", + "para {C:mult}+#2#{} multi", + "{C:green}1 en 4{} probabilidades", + "para {C:mult}-#2#{} multi", + }, + }, + j_cry_candy_basket = { + name = "Cesta de dulces", + text = { + "Vende esta carta para crear {C:attention}#1#{} {C:cry_candy}dulces", + "{C:attention}+#2#{} {C:cry_candy}dulce{} cada {C:attention}2{} ciegas derrotadas", + "{C:attention}+#3#{} {C:cry_candy}dulces{} al derrotar la {C:attention}ciega jefe{}" + } + }, + j_cry_candy_buttons = { + name = "Botones de dulce", + text = { + "Las siguientes {C:attention}#1#{} renovaciones", + "cuestan {C:money}$1{}", + } + }, + j_cry_candy_cane = { + name = "Bastón de dulce", + text = { + "Por las siguientes {C:attention}#1#{} rondas,", + "las cartas de juego otorgan {C:money}$#2#", + "cuando se {C:attention}reactivan" + } + }, + j_cry_candy_dagger = { + name = "Daga de dulce", + text = { + "Cuando se selecciona la {C:attention}ciega{},", + "destruye al comodín de la derecha", + "para crear un {C:cry_candy}dulce{}", + } + }, + j_cry_candy_sticks = { + name = "Palos de dulce", + text = { + "El efecto de la siguiente ciega jefe se desactiva", + "hasta que hayas jugado {C:attention}#1#{} msnos", + } + }, + j_cry_canvas = { + name = "Lienzo", + text = { + "{C:attention}Reactiva{} todos los {C:attention}comodines{} a la izquierda", + "una vez por {C:attention}todos{} los {C:attention}comodines{} no-{C:blue}comunes", + "a la derecha de este comodín", + }, + }, + j_cry_caramel = { + name = "Caramelo", + text = { + "Cada carta jugada otorga", + "{X:mult,C:white}X#1#{} multi cuando puntúa", + "por las siguientes {C:attention}#2#{} rondas", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Reactiva el comodín del {C:attention}extremo izquierdo{}", + "{C:attention}#1#{} veces adicionales", + }, + }, + j_cry_chili_pepper = { + name = "Ají picante", + text = { + "Este comodín consigue {X:mult,C:white} X#2# {} multi", + "al final de la ronda,", + "{C:red,E:2}se autodestruye{} después de {C:attention}#3#{} rondas", + "{C:inactive}(Actual:{} {X:mult,C:white} X#1# {} {C:inactive}multi){}", + }, + }, + j_cry_chocolate_dice = { + name = "Dado de chocolate", + text = { + "Gira un {C:green}d10{} al derrotar", + "la {C:attention}ciega jefe{} para", + "empezar un {C:cry_ascendant,E:1}evento", + "{C:inactive}(Actual: #1#)" + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} fichas y {X:dark_edition,C:white}^#1#{} multi", + "si tienes {C:attention}exactamente{} #2#", + "manos restantes", + }, + }, + j_cry_circus = { + name = "Circo", + text = { + "Comodines {C:red}raros{} otorgan {X:mult,C:white} X#1# {} multi", + "Comodines {C:cry_epic}épicos{} otorgan {X:mult,C:white} X#2# {} multi", + "Comodines {C:legendary}legendarios{} otorgan {X:mult,C:white} X#3# {} multi", + "Comodines {C:cry_exotic}exóticos{} otorgan {X:mult,C:white} X#4# {} multi", + }, + }, + j_cry_clash = { + name = "El conflicto", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "una {C:attention}#2#", + }, + }, + + j_cry_CodeJoker = { + name = "Comodín de código", + text = { + "Crea una {C:cry_code}carta de código{}", + "{C:dark_edition}negativa{} cuando se", + "selecciona una {C:attention}ciega{}", + }, + }, + j_cry_coin = { + name = "Moneda crypto", + text = { + "Gana entre", + "{C:money}$#1#{} y {C:money}$#2#{} por", + "cada comodín {C:attention}vendido{}", + }, + }, + j_cry_compound_interest = { + name = "Interés compuesto", + text = { + "Gana {C:money}#1#%{} de tu dinero total", + "al final de la ronda,", + "aumenta por {C:money}#2#%{} por cada", + "pago consecutivo", + }, + }, + j_cry_copypaste = { + name = "Copiar y pegar", + text = { + "Cuando una carta {C:cry_code}código{} se usa,", + "{C:green}#1# en #2#{} probabilidades para añadir", + "una copia a tus consumibles", + "{C:inactive}(debe haber espacio)", + }, + }, + j_cry_cotton_candy = { + name = "Algodón de azúcar", + text = { + "Al venderse, los {C:attention}comodines{}", + "adyacentes se vuelven {C:dark_edition}negativos{}" + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "Este comodín gana {C:chips}+#2#{} fichas", + "por cada {C:attention}renovación{} en la tienda", + "{C:green}Todas las renovaciones son gratis{}", + "{C:inactive}(Actual: {C:chips}+#1#{C:inactive} fichas)", + }, + }, + j_cry_cryptidmoment = { + name = "Cadena M", + text = { + "Vende esta carta para", + "añadir {C:money}$#1#{} de {C:attention}valor de venta{}", + "a cada {C:attention}comodín{}", + }, + }, + j_cry_cube = { + name = "Cubo", + text = { + "{C:chips}+#1#{} fichas", + }, + }, + j_cry_curse_sob = { + name = "Sollozo", + text = { + "{C:edition,E:1}no puedes{} {C:cry_ascendant,E:1}correr...{}", + "{C:edition,E:1}no puedes{} {C:cry_ascendant,E:1}esconderte...{}", + "{C:dark_edition,E:1}no puedes escapar...{}", + "{C:inactive}(debe haber espacio){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "Este comodín gana {C:chips}+#2#{} fichas", + "por cada carta {C:attention}comprada{}", + "{C:inactive}(Actual: {C:chips}+#1#{C:inactive} fichas)", + }, + }, + j_cry_cut = { + name = "Cortar", + text = { + "Este comodín destruye una", + "carta de {C:cry_code}código{} al azar,", + "y gana {X:mult,C:white} X#1# {} multi", + "al final de la {C:attention}tienda{}", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_delirious = { + name = "Comodín delirante", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_discreet = { + name = "Comodín discreto", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_doodlem = { + name = "Garabato M", + text = { + "Crea 2 {C:attention}consumibles{} {C:dark_edition}negativos{}", + "cuando se selecciona una {C:attention}ciega{}", + "Crea 1 {C:attention}consumibles{} adicional", + "por cada {C:attention}comodín contento{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Escala doble", + text = { + "Los {C:attention}comodines{} escaladores", + "aumentan {C:attention}cuadráticamente", + "{C:inactive,s:0.8}(ej. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(crece por by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Tiro al blanco", + text = { + "Este comodín consigue {X:mult,C:white} X#1# {} multi por", + "cada carta de {V:1}#2#{} jugada que no puntúa,", + "el palo cambia en cada ronda", + "{C:inactive}(Actual: {X:mult,C:white} X#3# {C:inactive} multi)", + }, + }, + j_cry_dubious = { + name = "Comodín dudoso", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "una {C:attention}#2#", + } + }, + j_cry_duos = { + name = "Los dúos", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "una {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Este comodín gana {X:mult,C:white} X#2# {} multi", + "cuando un {C:attention}comodín{} o", + "carta de juego puntúa", + "{C:inactive}(Actual: {X:mult,C:white} X#1# {C:inactive} multi)", + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Saca tu {C:green}baraja completa{} a tu mano", + "al seleccionar una {C:attention}ciega{}", + "{C:inactive,s:0.8}\"Si no puedes manejarme a mi 1x,", + "{C:inactive,s:0.8}no me mereces a mi 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "Cuando obtienes una {C:attention}etiqueta{},", + "crea {C:attention}#1#{} copias de ésta,", + "y {C:attention}aumenta{} la cantidad de", + "copias por {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Comodines aparecen usando el", + "orden de la {C:attention}colección{}", + "Crea {C:attention}#1#{} comodín(es) {C:dark_edition}negativos{}", + "al jugar una mano", + "{C:inactive,s:0.8}Comodines {C:cry_exotic,s:0.8}exóticos {C:inactive,s:0.8}o mejores no pueden aparecer", + "{s:0.8}Último comodín generado: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Llama eterna", + text = { + "Este comodín gana {X:mult,C:white} X#1# {} multi", + "por cada carta {C:attention}vendida{}", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_exoplanet = { + name = "Exoplaneta", + text = { + "Las cartas {C:dark_edition}holográficas{}", + "otorgan {C:mult}+#1#{} multi", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "Este comodín gana {X:dark_edition,C:white} ^#1# {} multi", + "cuando se activa {X:red,C:white}Xmulti{}", + "{C:inactive}(Actual: {X:dark_edition,C:white} ^#2# {C:inactive} multi)", + }, + }, + j_cry_exposed = { + name = "Expuesto", + text = { + "Reactiva todas las cartas {C:attention}numéricas{}", + "{C:attention}#1#{} veces adicionales", + "Todas las cartas de {C:attention}figura{} se debilitan", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} multi si", + "las cartas jugadas puntúan", + "{C:attention}#2#{} o menos veces", + }, + }, + j_cry_filler = { + name = "El relleno", + text={ + "{X:mult,C:white} X#1#{} multi", + "si la mano contiene", + "una {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Dedos fractales", + text = { + "{C:attention}+#1#{} límite de selección de cartas", + }, + }, + j_cry_flip_side = { + name = "Por el otro lado", + text = { + "Los comodines {C:dark_edition}doble cara{} usan", + "su lado trasero para sus efectos", + "en vez de su lado delantero", + "{C:attention}Reactiva{} todos los comodines {C:dark_edition}doble cara{}" + }, + }, + j_cry_foodm = { + name = "M de comida rápida", + text = { + "{C:mult}+#1#{} multi", + "{C:red,E:2}se autodestruye{} en {C:attention}#2#{} ronda(s)", + "Aumenta por {C:attention}#3#{} ronda cuando un", + "{C:attention}comodín contento{} es {C:attention}vendido{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foolhardy = { + name = "Comodín temerario", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_formidiulosus = { + name = "Formidiulosus", + text = { + "Cuando un comodín {X:cry_cursed,C:white}maldito{} es obtenido, destrúyelo", + "Crea {C:attention}#1#{} {C:cry_candy}dulces {C:dark_edition}negativos{} al final de la tienda", + "Gana {X:dark_edition,C:white}^#2#{} multi por cada {C:cry_candy}dulce{} en tu posesión", + "{C:inactive}(Actual: {X:dark_edition,C:white}^#3#{C:inactive} multi)", + }, + }, + j_cry_foxy = { + name = "Comodín astuto", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "Este comodín gana {C:chips}+#2#{} fichas", + "si la mano jugada {C:attention}no es{}", + "la {C:attention}mano de póker{} más jugada", + "{C:inactive}(Actual: {C:chips}+#1#{C:inactive} fichas)", + }, + }, + j_cry_fuckedup = { + name = "Comodín jodido", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_gardenfork = { + name = "El jardín de las bifurcaciones", + text = { + "Gana {C:money}$#1#{} si la {C:attention}mano jugada{}", + "contiene un {C:attention}As{} y un {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Duplica{} todos los valores", + "del {C:attention}Joker{} del extremo izquierdo", + "al final de la ronda", + }, + }, + j_cry_ghost = { + name = "Fantasma", + text = { + "Al final de la ronda:", + "{C:green}#1# en #2#{} probabilidades para", + "{C:attention}poseer{} un {C:attention}comodín{} aleatorio", + "{C:green}#1# en #3#{} probabilidades para", + "{E:2,C:red}autodestruirse" + } + }, + j_cry_giggly = { + name = "Comodín absurdo", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "una {C:attention}#2#", + } + }, + j_cry_goldjoker = { + name = "Comodín de oro", + text = { + "Gana {C:money}#1#%{} de tu dinero", + "total al final de la ronda", + "El pago aumenta por {C:money}#2#%{}", + "cuando cada carta de {C:attention}oro{}", + "jugada puntúa", + }, + }, + j_cry_googol_play = { + name = "Carta de Googol Play", + text = { + "{C:green}#1# en #2#{} probabilidades de", + "{X:red,C:white} X#3# {} multi", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Crea un {C:attention}comodín{} aleatorio", + "al final de la ronda", + "Vende esta carta para", + "crear un {C:attention}comodín{} aleatorio", + "{C:inactive}(debe haber espacio){}", + }, + }, + j_cry_happyhouse = { + name = "Casa feliz", + text = { + "{X:dark_edition,C:white}^#1#{} multi sólo después de", + "jugar {C:attention}114{} manos{}", + "{C:inactive}(Actual: #2#/114){}", + "{C:inactive,s:0.8}¡No hay un lugar como el hugar!{}", + }, + }, + j_cry_home = { + name = "El hogar", + text={ + "{X:mult,C:white} X#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consum-ible", + text = { + "Gana {C:money}$#1#{} cuando", + "usas un {C:attention}consumible{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Reactiva todas las cartas jugadas", + "{C:attention}#2#{} vez,", + "cada carta jugada otorga", + "{X:mult,C:white} X#1# {} multi cuando puntúa", + }, + }, + j_cry_jawbreaker = { + name = "Jawbreaker", + text = { + "Al derrotar la {C:attention}ciega jefe,", + "{C:attention}duplica{} los valores de los comodines adyacentes", + "{E:2,C:red}se autodestruye{}", + } + }, + j_cry_jimball = { + name = "Jimball", + text = { + "Este comodín gana {X:mult,C:white} X#1# {} multi", + "por cada mano jugada {C:attention}consecutiva{}", + "mientras juegas tu", + "{C:attention}mano de póker{} más jugada", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_jollysus = { + name = "¿Comodín contento?", + text = { + "Crea un comodín {C:dark_edition}contento{}", + "cuando un comodín es {C:attention}vendido{}", + "{C:red}Funciona una vez por ronda{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Parece legítimo...{}", + }, + }, + j_cry_kidnap = { + name = "Secuestro", + text = { + "Gana {C:money}$#2#{} al final de la ronda", + "Aumenta el pago por {C:money}$#1#{}", + "cuando un comodín de {C:attention}tipo multi{}", + "o {C:attention}tipo fichas{} es vendido", + }, + }, + j_cry_kooky = { + name = "Comodín raro", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_krustytheclown = { + name = "Krusty el Payaso", + text = { + "Este comodín gana", + "{X:mult,C:white} X#1# {} multi cuando", + "cada {C:attention}carta{} jugada puntúa", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_kscope = { + name = "Caleidoscopio", + text = { + "Agrega {C:dark_edition}policromía{} a", + "un {C:attention}comodín{} aleatorio al", + "derrotar la {C:attention}ciega jefe{}", + }, + }, + j_cry_lightupthenight = { + name = "Ilumina la noche", + text = { + "Cada {C:attention}7{} o {C:attention}2{} jugado", + "otorga {X:mult,C:white}X#1#{} multi cuando puntúa", + }, + }, + j_cry_longboi = { + name = "Monstruo", + text = { + "Otorga futuras copias de", + "este comodín {X:mult,C:white}X#1#{} multi", + "al final de la ronda", + "{C:inactive}(Actual: {X:mult,C:white}X#2#{C:inactive} multi){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Reactiva{} todos los comodines", + "una vez por cada {C:attention}comodín{}", + "{C:attention}contento{} vendido esta ronda", + "{C:inactive}(Actual:{}{C:attention:} #1#{}{C:inactive} reactivaciones){}", + "{C:inactive,s:0.8}No había suficiente espacio...{}", + }, + }, + j_cry_lucky_joker = { + name = "Comodín de la suerte", + text = { + "Gana {C:money}$#1#{} cada vez que", + "una carta {C:attention}de la suerte{} se active", + "{C:green}con éxito{}", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "Todos los comodines otorgan", + "{X:chips,C:white} X#1# {} fichas", + }, + }, + j_cry_m = { + name = "m", + text = { + "Este comodín gana {X:mult,C:white} X#1# {} multi", + "cuando un {C:attention}Comodín contento{} es vendido", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Crea un {C:attention}Comodín contento{} ", + "{C:dark_edition}negativo{} cuando se", + "selecciona una {C:attention}ciega{}", + }, + }, + j_cry_macabre = { + name = "Comodín macabro", + text = { + "Al seleccionar una {C:attention}ciega{},", + "destruye todos los {C:attention}comodines{} excepto", + "{C:legendary}comodines M{} y {C:attention}comodines contentos{}", + "y crea 1 {C:attention}comodín contento{}", + "por cada carta destruida", + }, + }, + j_cry_magnet = { + name = "Imán de refrigerador", + text = { + "Gana {C:money}$#1#{} al final de la ronda", + "Obtienes {X:money,C:white} X#2# {} si hay", + "{C:attention}#3#{} o menos {C:attention}comodines{}", + }, + }, + j_cry_manic = { + name = "Comodín maníaco", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "una {C:attention}#2#", + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Reactiva todos los comodines", + "{C:attention}#1#{} veces adicionales", + }, + }, + j_cry_mask = { + name = "Máscara", + text = { + "Reactiva todas las cartas de {C:attention}figura{}", + "{C:attention}#1#{} veces adicionales", + "Todas las cartas {C:attention}numéricas{} se debilitan", + }, + }, + + j_cry_maximized = { + name = "Maximizado", + text = { + "Todas las cartas de {C:attention}figura{}", + "son consideradas {C:attention}Reyes{},", + "todas las cartas de {C:attention}número{}", + "son consideradas {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Laberinto", + text = { + "Todas las manos son consideradas la", + "{C:attention}primera mano{} de cada ronda,", + "todos los descartes son considerados el", + "{C:attention}primer descarte{} de cada ronda", + }, + }, + j_cry_Megg = { + name = "Muevo", + text = { + "Vende esta carta para crear", + "{C:attention}#2#{} Comodines contentos, aumenta", + "por {C:attention}#1#{} al final de la ronda", + }, + }, + j_cry_mellowcreme = { + name = "Mellowcreme", + text = { + "Vende esta carta para {C:attention}multiplicar", + "el valor de venta de todos los", + "{C:attention}consumibles{} por {C:attention}X#1#" + } + }, + j_cry_membershipcard = { + name = "Carta de afiliación", + text = { + "{X:mult,C:white}X#1#{} multi por cada miembro", + "en el {C:attention}Discord de Cryptid{}", + "{C:inactive}(Actual: {X:mult,C:white}X#2#{C:inactive} multi)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Carta de miembro antigua", --renamed it for making it distinct + text = { + "{C:chips}+#1#{} fichas por cada miembro", + "en el {C:attention}Discord de Cryptid{}", + "{C:inactive}(Actual: {C:chips}+#2#{C:inactive} fichas)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Lluvia de meteoros", + text = { + "Las cartas {C:dark_edition}laminadas{}", + "otorgan {C:chips}+#1#{} fichas", + }, + }, + j_cry_mneon = { + name = "M neón", + text = { + "Gana {C:money}$#2#{} al final de la ronda", + "Aumenta el pago por", + "{C:money}$#1#{} por cada {C:attention}Comodín contento{}", + "o {C:legendary}comodín M{} al", + "final de la ronda", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "Este comodín consigue {X:mult,C:white}X#1#{} multi", + "si no se usaron {C:attention}descartes{}", + "en esta ronda", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_monkey_dagger = { + name = "Daga de mono", + text = { + "Cuando se selecciona la {C:attention}ciega{},", + "destruye al comodín de la izquierda", + "y agrega para siempre {C:attention}10 veces", + "de valor de venta a estas {C:chips}fichas{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_monopoly_money = { + name = "Dinero de monopolio", + text = { + "{C:green}#1# en #2#{} probabilidades para", + "{C:attention}destruir{} objetos comprados", + "Divide tu dinero en 2 cuando se {C:attention}vende", + } + }, + j_cry_morse = { + name = "Código morse", + text = { + "Gana {C:money}$#2#{} al final de la ronda", + "Aumenta el pago por {C:money}$#1#{} al", + "vender una carta con una {C:attention}edición{}", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Crea un {C:legendary}comodín M{} al final de la ronda", + "Cada {C:attention}Comodín contento{} o {C:legendary}comodín M", + "otorga {X:dark_edition,C:white}^#1#{} multi", + "Aumenta el valor por {X:dark_edition,C:white}^#2#{}", + "cuando un {C:attention}Comodín contento{} es {C:attention}vendido", + "{C:inactive,s:0.8}(Tredecim excluido)", + }, + }, + j_cry_mstack = { + name = "Pila de Ms", + text = { + "Reactiva todas las cartas jugadas", + "una vez por cada", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}comodines contentos{} vendidos", + "{C:inactive}(Actual:{}{C:attention:} #1#{}{C:inactive} reactivaciones){}", + }, + }, + j_cry_multjoker = { + name = "Comodín multi", + text = { + "{C:green}#1# en #2#{} probabilidades por cada", + "carta {C:attention}multi{} jugada para que cree", + "una carta {C:spectral}Críptido{} al anotar", + "{C:inactive}(Debe haber espacio)", + }, + }, + j_cry_necromancer = { + name = "Nigromante", + text = { + "Cuando un comodín es {C:attention}vendido{} por un precio mayor que {C:attention}$0{},", + "gana un comodín {C:attention}aleatorio{} {C:attention}vendido{} en esta partida", + "y establece su {C:attention}valor de venta{} a {C:attention}$0{}", + }, + }, + j_cry_negative = { + name = "Comodín negativo", + text = { + "{C:dark_edition}+#1#{} ranuras de {C:attention}comodín", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} fichas si la mano jugada", + "contiene un {C:attention}6{} y un {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Noche", + text = { + "{X:dark_edition,C:white}^#1#{} multi en la", + "última mano de la ronda", + "{E:2,C:red}se autodestruye{} en", + "la última mano de la ronda", + }, + }, + j_cry_nosound = { + name = "Sin sonido, sin memoria", + text = { + "Reactiva cada {C:attention}7{} jugado", + "{C:attention:}#1#{} veces adicionales", + }, + }, + j_cry_notebook = { + name = "Cuaderno", + text = { + "{C:green} #1# en #2#{} probabilidades para conseguir {C:dark_edition}+1{} ranura", + "de comodín por cada {C:attention}renovación{} en la tienda", + "{C:green}Siempre se activa{} si hay", + "{C:attention}#5#{} o más {C:attention}Comodines contentos{}", + "{C:red}Funciona una vez por ronda{}", + "{C:inactive}(Actual: {C:dark_edition}+#3#{}{C:inactive} y #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Bloques numerados", + text = { + "Gana {C:money}$#1#{} al final de la ronda", + "Aumenta el pago por {C:money}$#2#{}", + "por cada {C:attention}#3#{} en tu mano,", + "la categoría cambia cada ronda", + }, + }, + j_cry_nuts = { + name = "El loco", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "una {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Comodín loco", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_oil_lamp = { + name = "Lámpara de aceite", + text = { + "Al final de la ronda, aumenta los valores", + "del comodín del {C:attention}extremo derecho{} por {C:attention}x#1#{}" + }, + }, + + j_cry_oldblueprint = { + name = "Plano viejo", + text = { + "Copia la habilidad del", + "{C:attention}comodín{} de la derecha", + "{C:green}#1# en #2#{} probabilidades", + "de que la carta se destruya", + "al final de la ronda", + }, + }, + j_cry_oldcandy = { + name = "Dulce nostálgico", + text = { + "Vende esta carta para ganar", + "{C:attention}+#1#{} tamaño de mano", + "de forma permanente", + }, + }, + j_cry_oldinvisible = { + name = "Comodín invisible nostálgico", + text = { + "{C:attention}Duplioca{} un {C:attention}comodín{}", + "aleatorio cada {C:attention}4", + "cartas de comodín vendidas", + "{s:0.8}Comodín invisible nostálgico excluido{}", + "{C:inactive}(Actual: #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panóptico", + text = { + "Todas las manos son consideradas la", + "{C:attention}última mano{} de cada ronda", -- +4 dabloons + }, + }, + j_cry_penetrating = { + name = "Comodín penetrante", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_pickle = { --holy shit dethklok reference + name = "Pepinillo", + text = { + "Al saltar una {C:attention}ciega{}, crea", + "{C:attention}#1#{} etiquetas, disminuye por", + "{C:red}#2#{} cuando se selecciona una {C:attention}ciega{}", + }, + }, + j_cry_pirate_dagger = { + name = "Daga de pirata", + text = { + "Cuando se selecciona la {C:attention}ciega{},", + "destruye al comodín de la derecha", + "y agraga {C:attention}un cuarto{} del", + "valor de venta a {X:chips,C:white} Xfichas {}", + "{C:inactive}(Actual: {X:chips,C:white} X#1# {C:inactive} fichas)", + }, + }, + j_cry_pot_of_jokes = { + name = "Olla de las bromas", + text = { + "{C:attention}#1#{} tamaño de mano,", + "aumenta por", + "{C:blue}#2#{} cada runda", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "Este comodín gana {X:dark_edition,C:white} ^#1# {} multi", + "si todas las cartas en la mano jugada son", + "{C:attention}Ases{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, o {C:attention}7s{}", + "{C:inactive}(Actual: {X:dark_edition,C:white} ^#2# {C:inactive} multi)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "Este comodín gana", + "{X:mult,C:white} X#1# {} multi cuando una", + "carta de {C:cry_code}código{} se usa", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_queens_gambit = { + name = "Gambito de dama", + text = { + "Si {C:attention}la mano de póker{} es una", + "{C:attention}Escalera real{}, destruye la", + "{C:attention}reina{} puntuada y crea un", + "{C:attention}comodín{} {C:red}raro{} {C:dark_edition}negativo{}", + }, + }, + j_cry_quintet = { + name = "El quinteto", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Bloon rojo", + text = { + "Gana {C:money}$#1#{} en {C:attention}#2#{} ronda(s)", + "{C:red,E:2}se autodestruye{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} apuesta al gastar", + "{C:money}$#2#{} {C:inactive}($#3#){}", + "{s:0.8}Requisitos aumentan", + "{C:attention,s:0.8}exponencialmente{s:0.8} por cada uso", + "{C:money,s:0.8}Siguiente aumento: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "Cuando un {C:attention}comodín{} es vendido,", + "añade sus efectos a", + "todos los otros comodines", + "{C:inactive,s:0.8}No afecta a otros Rescribere{}" + } + }, + j_cry_reverse = { + name = "Carta de reversa", + text = { + "Rellena todos las ranuras de comodín vacías {C:inactive}(Max 100){}", + "con {C:attention}Comodines contentos{} {C:dark_edition}holográficos{} si", + "la {C:attention}mano de póker descartada{} es una {C:attention}#1#{}", + "{C:red,E:2}se autodestruye{}", + "{C:inactive,s:0.8}¡El regreso SUPREMO!{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Aleatoriza abilidades por cada {C:attention}apuesta{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrificio", + text = { + "Crea un comodín {C:green}inusual{}", + "y 3 {C:attention}Comodines contentos{} al", + "usar una carta {C:spectral}espectral{}", + "{C:red}Funciona una vez por ronda{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Brote", + text = { + "Después de puntuar {C:attention}#2#{} {C:inactive}[#1#]{} cartas", + "mejoradas, vende esta carta para", + "crear un {C:attention}comodín{} {C:cry_epic}épico{} ", + "{C:inactive,s:0.8}Creará un {C:attention,s:0.8}comodín{} {C:red,s:0.8}raro{} si los", + "{C:inactive,s:0.8}comodines {C:cry_epic,s:0.8}épicos{} {C:inactive,s:0.8}están desactivados{}", + }, + }, + j_cry_savvy = { + name = "Comodín inteligente", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Los {C:attention}comodines{} escalan", + "como un polinomio grado-{C:attention}#1#{},", + "aumenta el grado por {C:attention}#2#{}", + "al final de la ronda", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluido)", + }, + }, + j_cry_scrabble = { + name = "Teja de Scrabble", + text = { + "{C:green}#1# en #2#{} probabilidades de crear", + "un comodín {C:green}inusual{} {C:dark_edition}contento", + "al jugar una mano", + }, + }, + j_cry_seal_the_deal = { + name = "Sellar el acuerdo", + text = { + "Añade un {C:attention}sello al azar{} a cada carta", + "puntuada en la {C:attention}última mano{} de la ronda", + }, + }, + j_cry_shrewd = { + name = "Comodín perspicaz", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_silly = { + name = "Comodín bobo", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_smallestm = { + name = "Diminuto", + text = { + "Crea una {C:cry_jolly}Etiqueta MM", + "si la {C:attention}mano de póker{} jugada", + "es una {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so básicamente soy muy peque", + }, + }, + j_cry_soccer = { + name = "Uno para todos", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} ranura de comodín", + "{C:attention}+#1#{} ranura de paquete potenciador", + "{C:attention}+#1#{} tamaño de mano", + "{C:attention}+#1#{} ranura de consumible", + "{C:attention}+#1#{} ranura de carta en la tienda", + }, + }, + j_cry_spaceglobe = { + name = "Esfera celestial", + text = { + "Este comodín gana {X:chips,C:white}X#2#{} fichas", + "si la {C:attention}mano de póker{} es un(a) {C:attention}#3#{},", + "mano cambia después de aumentar{}", + "{C:inactive}(Actual:{} {X:chips,C:white}X#1#{} {C:inactive}fichas){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Crea una copia {C:dark_edition}negativa{}", + "de un {C:attention}comodín{} aleatorio", + "al final de la {C:attention}tienda", + "{C:inactive,s:0.8}No copia otros Speculo{}", + }, + }, + j_cry_spy = { + name = "Espía", + text = { + "{X:mult,C:white} X#2# {} multi, {C:dark_edition}+1{} ranura de {C:attention}comodín{}", + "{C:inactive}¡Ese #1# es un espía!", + }, + }, + j_cry_stardust = { + name = "Polvo de estrellas", + text = { + "Las cartas {C:dark_edition}policromas{}", + "otorgan {X:mult,C:white}X#1#{} multi", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "Este comodín destruye una", + "carta de {C:planet}planeta{} al azar", + "y gana {X:dark_edition,C:white} ^#1# {} multi", + "al final de la {C:attention}tienda{}", + "{C:inactive}(Actual: {X:dark_edition,C:white} ^#2# {C:inactive} multi)", + }, + }, + j_cry_stronghold = { + name = "La fortaleza", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + + j_cry_subtle = { + name = "Comodín sutil", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} fichas, {C:mult}+#1#{} multi,", + "{X:chips,C:white}X#2#{} fichas, {X:mult,C:white}X#2#{} multi", + "Gana {C:money}$#3#{} al", + "final de la ronda", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "Al final de la ronda, crea", + "una {C:attention}copia{} de una carta", + "aleatoria {C:attention}en tu mano{},", + "destruye las demás", + "{C:attention,s:0.8}Reyes{s:0.8} de {C:hearts,s:0.8}corazones{s:0.8} son priorizados", + }, + }, + j_cry_swarm = { + name = "El enjambre", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Catalizador de sincronización", + text = { + "Equilibra las {C:blue}fichas{} y el {C:red}multi{}", + "{C:inactive,s:0.8}¡Hey! ¡Yo he visto esto antes!", + }, + }, + j_cry_tax_fraud = { + name = "Evasión fiscal", + text = { + "Al final de la ronda, gana", + "{C:attention}$#1#{} por cada comodín {C:attention}de alquiler", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{} ranuras de {C:attention}comodín{}", + "Gana {C:money}$#2#{} al final de la ronda", + }, + }, + j_cry_translucent = { + name = "Comodín translúcido", + text = { + "Vende esta carta para crear", + "una copia {C:attention}Banana Perecedera{}", + "de un {C:attention}comodín{} aleatorio", + "{s:0.8,C:inactive}(La copia evita compat. de perecedero)", + }, + }, + j_cry_treacherous = { + name = "Comodín traicionero", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_trick_or_treat = { + name = "Dulce o truco", + text = { + "Al {C:attention}venderse{}:", + "{C:green}#1# en #2#{} probabilidades de crear {C:attention}2{} {C:cry_candy}dulces", + "Si no, crea un comodín {X:cry_cursed,C:white}maldito{}", + "{C:inactive}(puede desbordarse)" + } + }, + j_cry_tricksy = { + name = "Comodín tramposo", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "una {C:attention}#2#", + } + }, + j_cry_triplet_rhythm = { + name = "Ritmo triple", + text = { + "{X:mult,C:white} X#1# {} multi si la mano", + "contiene {C:attention}exactamente{} tres {C:attention}3s", + }, + }, + j_cry_tropical_smoothie = { + name = "Zalamero tropical", + text = { + "Vende esta carta para", + "{C:attention}multiplicar{} los valores de los", + "comodines conseguidos por {C:attention}X1.5{}", + }, + }, + + j_cry_unity = { + name = "La unidad", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + j_cry_universe = { + name = "Universo", + text = { + "Las cartas {C:dark_edition}astrales{}", + "otorgan {X:dark_edition,C:white}^#1#{} multi", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "Las {C:attention}manos de póker{} ganan", + "{X:red,C:white} X#1# {} multi y {X:blue,C:white} X#1# {} fichas", + "al subir de nivel", + }, + }, + j_cry_unjust_dagger = { + name = "Daga injusta", + text = { + "Cuando se selecciona la {C:attention}ciega{},", + "destruye al comodín de la izquierda", + "y agraga {C:attention}un quinto{} del", + "valor de venta a {X:mult,C:white} Xmulti {}", + "{C:inactive}(Actual: {X:mult,C:white} X#1# {C:inactive} multi)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "Cuando cualquier probabilidad", + "es activada {C:green}con éxito{},", + "este comodín gana {X:red,C:white}Xmulti{}", + "igual a sus {C:attention}probabilidades{} listadas", + "{C:inactive}(Actual: {X:mult,C:white} X#1# {C:inactive} multi)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "Este comodín gana {C:money}$#1#{} de {C:attention}valor de venta{}", + "si la {C:attention}mano de póker{} contiene una {C:attention}#2#{}", + "Vende esta carta para crear un", + "{C:attention}comodín contento{} {C:dark_edition}policroma{} por", + "cada {C:money}$4{} de {C:attention}valor de venta{} {C:inactive}(min. 1){}", + }, + }, + j_cry_wacky = { + name = "Comodín chalado", --too many freaking name collisions + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "Todos los comodines otorgan", + "{X:mult,C:white} X#1# {} multi", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "Todos los comodines otorgan", + "{C:money}$#1#{} al activarse", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "Este comodín gana", + "{C:mult}+#2#{} multi cuando cada", + "{C:attention}As{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, o {C:attention}8{}", + "jugado puntúa", + "{C:inactive}(Actual: {C:mult}+#1#{C:inactive} multi)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Reactica cada {C:attention}2{} jugado", --wee gaming + "{C:attention:}#1#{} veces adicionales", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", --wee gaming :) + }, + }, + j_cry_wheelhope = { + name = "La rueda de la esperanza", + text = { + "Este comodín gana", + "{X:mult,C:white} X#1# {} multi al fallar", + "una {C:attention}Rueda de la fortuna{}", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_whip = { + name = "El LÁTIGO", + text = { + "Este comodín gana {X:mult,C:white}X#1#{} multi", + "si la {C:attention}mano jugada{} contiene un", + "{C:attention}2{} y {C:attention}7{} de diferentes palos", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_wrapped = { + name = "Dulce envuelto", + text = { + "Crea un {C:attention}comodín de comida{}", + "en {C:attention}#1#{} ronda(s)", + "{C:red,E:2}se autodestruye{}", + }, + }, + j_cry_wtf = { + name = "¿¡Qué rayos?!", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}nvl.#4#{})({V:2}nvl.#5#{})({V:3}nvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}nvl.#4#{})({V:2}nvl.#5#{})({V:3}nvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_Kaikki = { + name = "Kaikki", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Estrella de neutrones", + text = { + "Mejora una mano de póker", + "al azar por", + "{C:attention}1{} nivel por cada", + "{C:attention}estrella de neutrones{} usada", + "en esta partida", + "{C:inactive}(Actual:{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planeta.lua", + text = { + "{C:green}#1# en #2#{} probabilidades para", + "aumentar todas las", + "{C:legendary,E:1}manos de póker{}", + "por {C:attention}1{} nivel", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}nvl.#4#{})({V:2}nvl.#5#{})({V:3}nvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}nvl.#4#{})({V:2}nvl.#5#{})({V:3}nvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "Manga CCD", + text = { + "Todas las cartas también son", + "un consumible {C:attention}aleatorio{}", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Manga transportadora", + text = { + "Los comodines {C:attention}no{} se pueden mover", + "Al principio de la runda,", + "{C:attention}duplica{} el comodín del extremo derecho", + "y {C:attention}destruye{} el comodín del extremo izquierdo", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Manga crítica", + text = { + "Después de cada mano jugada,", + "{C:green}#1# en 4{} probabilidades para {X:dark_edition,C:white} ^2 {} multi", + "{C:green}#1# en 8{} probabilidades para {X:dark_edition,C:white} ^0.5 {} multi", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Manga codificada", + text = { + "Comienza con un {C:cry_code,T:j_cry_CodeJoker}Comodín de código{}", + "y {C:cry_code,T:j_cry_copypaste}Copiar y pegar{}", + "Sólo aparecen {C:cry_code}cartas de código{} en la tienda", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Manga balanceada", + text = { + "Todas las cartas tienen la ", + "{C:attention}misma probabilidad{} de", + "aparecer en las tiendas,", + "comienza la partida con", + "{C:attention,T:v_overstock_plus}+2 ranuras de carta", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Baraja infinita", + text = { + "Puedes seleccionar {C:attention}cualquier", + "cantidad de cartas", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Manga de errata", + text = { + "Los valures de cartas", + "se {C:attention}aleatorizan", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Manga redimida", + text = { + "Cuando se compra un {C:attention}vale{},", + "obtén sus {C:attention}niveles extras", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Manga de agujero de gusano", + text = { + "Comienza con un comodín {C:cry_exotic}exótico{C:attention}", + "Los comodines son {C:attention}20X{} más", + "probables de ser {C:dark_edition}Negativos", + "{C:attention}-2{} ranuras de comodín", + }, + }, + sleeve_cry_legendary_sleeve = { + name = "Manga legendaria<", + text = { + "Comienza con un comodín {C:legendary}legendario{C:legendary}", + "{C:green}1 en 5{} probabilidades para crear otro", + "cuando se derrota a la ciega jefe", + "{C:inactive}(debe haber espacio){}", + }, + }, + }, + Spectral = { + c_cry_adversary = { + name = "Adversario", + text = { + "{C:red}Todos{} tus {C:attention}comodines{} se vuelven {C:dark_edition}negativos{},", + "{C:red}todos{} los {C:attention}comodines{} en la tienda cuestan", + "el {C:red}doble{} por el resto de la partida", + }, + }, + c_cry_analog = { + name = "Análogo", + text = { + "Crea {C:attention}#1#{} copias de un", + "{C:attention}comodín{} aleatorio, destruye", + "los demás, {C:attention}+#2#{} apuesta", + }, + }, + c_cry_chambered = { + name = "Recámara", + text = { + "Crea {C:attention}#1#{} copias", + "{C:dark_edition}negativas{} de un", + "consumible {C:attention}al azar{}", + "{C:inactive,s:0.8}No copia otros __{}" + }, + }, + c_cry_conduit = { + name = "Conducto", + text = { + "Intercambia las {C:attention}ediciones{} de", + "{C:attention}2{} cartas o {C:attention}comodines{} seleccionados", + }, + }, + + c_cry_gateway = { + name = "Portal", + text = { + "Crea un {C:attention}comodín", + "{C:cry_exotic,E:1}exótico{}, destruye", + "los demás", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Aplica {C:attention}consumibles{} aleatorios", + "como si fueran {C:dark_edition}mejoras{}", + "a las cartas en la mano", + }, + }, + c_cry_lock = { + name = "Cerradura", + text = { + "Remueve {C:red}todas{} las pegatinas", + "de {C:red}todos{} los comodines,", + "y aplica {C:purple,E:1}Eterno{}", + "a un {C:attention}comodín{} aleatorio", + }, + }, + c_cry_pointer = { + name = "PUNTERO://", + text = { + "Crea una carta", + "de {C:cry_code}tu elección", + "{C:inactive,s:0.8}(Comodines exóticos #1#excluidos)", + }, + }, + c_cry_replica = { + name = "Réplica", + text = { + "Convierte todas las cartas", + "en tu mano", + "a una carta {C:attention}aleatoria{}", + "en tu mano", + }, + }, + c_cry_ritual = { + name = "Ritual", + text = { + "Aplica {C:dark_edition}Negativo{}, {C:dark_edition}Mosaico{},", + "o {C:dark_edition}Astral{} a {C:attention}#1#{}", + "carta de tu mano seleccionada", + }, + }, + c_cry_source = { + name = "Origen", + text = { + "Agrega un {C:cry_code}sello verde{}", + "a {C:attention}1{} carta seleccionada", + "de tu mano al azar", + }, + }, + c_cry_summoning = { + name = "Evocación", + text = { + "Crea un {C:joker}comodín{}", + "{C:cry_epic}épico{} aleatorio, destruye", + "un {C:joker}comodín{} al azar", + }, + }, + c_cry_trade = { + name = "Intercambio", + text = { + "{C:attention}Pierde{} un vale aleatorio,", + "gana {C:attention}2{} vales aleatorios", + }, + }, + c_cry_typhoon = { + name = "Tifón", + text = { + "Agrega un {C:cry_azure}sello azur{}", + "a {C:attention}1{} carta seleccionada", + "de tu mano al azar", + }, + }, + c_cry_vacuum = { + name = "Vacío", + text = { + "Remueve {C:red}todas{} las {C:green}modificaciones{}", + "de {C:red}todas{} las cartas en tu mano,", + "gana {C:money}$#1#{} por cada {C:green}modificación{} removida", + "{C:inactive,s:0.7}(ej. mejoras, sellos, ediciones)", + }, + }, + c_cry_white_hole = { + name = "Agujero blanco", + text = { + "{C:attention}Remueve{} todos los niveles de mano,", + "mejora la mano de póker {C:legendary,E:1}más jugada{}", + "por {C:attention}3{} por cada nivel removido", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pozo rosa", + colour = "Rosa", --this is used for auto-generated sticker localization + text = { + "Escalas de puntos requeridas más rápidas", + "para cada {C:attention}apuesta inicial", + }, + }, + stake_cry_brown = { + name = "Pozo marrón", + colour = "Marrón", + text = { + "Todas las {C:attention}pegatinas{} son", + "compatibles entre sí", + }, + }, + stake_cry_yellow = { + name = "Pozo amarillo", + colour = "Amarillo", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Pozo jade", + colour = "Jade", + text = { + "Las cartas pueden sacarse {C:attention}boca abajo{}", + }, + }, + stake_cry_cyan = { + name = "Pozo celeste", + colour = "Celeste", + text = { + "Comodines {C:green}inusuales{} y {C:red}raros{} son", + "menos probables de aparecer", + }, + }, + stake_cry_gray = { + name = "Pozo gris", + colour = "Gris", + text = { + "Las renovaciones aumentan por {C:attention}$2{} cada una", + }, + }, + stake_cry_crimson = { + name = "Pozo carmesí", + colour = "Carmesí", + text = { + "Los vales reaparecen en apuestas {C:attention}pares{}", + }, + }, + stake_cry_diamond = { + name = "Pozo diamante", + colour = "Diamante", + text = { + "Debes vencer la apuesta {C:attention}10{} para ganar", + }, + }, + stake_cry_amber = { + name = "Pozo ámbar", + colour = "Ámbar", + text = { + "{C:attention}-1{} ranuras de paquete potenciador", + }, + }, + stake_cry_bronze = { + name = "Pozo bronce", + colour = "Bronce", + text = { + "Los vales son {C:attention}50%{} más caros", + }, + }, + stake_cry_quartz = { + name = "Pozo cuarzo", + colour = "Cuarzo", + text = { + "Los comodines pueden ser {C:attention}Fijados{}", + "{s:0.8,C:inactive}(Se queda fijado al extremo izquierdo){}", + }, + }, + stake_cry_ruby = { + name = "Pozo rubí", + colour = "Rubí", + text = { + "Ciegas {C:attention}grandes{} pueden ser", + "ciegas {C:attention}jefes{}", + }, + }, + stake_cry_glass = { + name = "Pozo de vidrio", + colour = "Vidrio", + text = { + "Las cartas pueden {C:attention}destruirse{} al puntuar", + }, + }, + stake_cry_sapphire = { + name = "Pozo safiro", + colour = "Safiro", + text = { + "Pierde {C:attention}25%{} del dinero actual", + "al final de la apuesta", + "{s:0.8,C:inactive}(máx. $10){}", + }, + }, + stake_cry_emerald = { + name = "Pozo esmeralda", + colour = "Esmeralda", + text = { + "Cartas, paquetes y vales", + "pueden estar {C:attention}boca abajo{}", + "{s:0.8,C:inactive}(No se pueden ver hasta ser comprados){}", + }, + }, + stake_cry_platinum = { + name = "Pozo platino", + colour = "Platino", + text = { + "Las ciegas pequeñas son {C:attention}removidas{}", + }, + }, + stake_cry_twilight = { + name = "Pozo crepúsculo", + colour = "Crepúsculo", + text = { + "Las cartas pueden ser {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 en 10 probabilidades de ser destruidas cada ronda){}", + }, + }, + stake_cry_verdant = { + name = "Pozo verdoso", + colour = "Verdoso", + text = { + "Escalas de puntos requeridas más rápidas", + "para cada {C:attention}apuesta inicial", + }, + }, + stake_cry_ember = { + name = "Pozo ascua", + colour = "Ascua", + text = { + "Todos los objetos no entregan dinero al venderse", + }, + }, + stake_cry_dawn = { + name = "Pozo alba", + colour = "Alba", + text = { + "Cartas tarot y espectrales seleccionan {C:attention}1", + "carta menos", + "{s:0.8,C:inactive}(mín. 1){}", + }, + }, + stake_cry_horizon = { + name = "Pozo del horizonte", + colour = "Horizonte", + text = { + "Agrega una {C:attention}carta al azar{}", + "a tu baraja al", + "seleccionar una {C:attention}ciega{}", + }, + }, + stake_cry_blossom = { + name = "Pozo florido", + colour = "Florido", + text = { + "Las ciegas {C:attention}finales{} pueden aparecer", + "en {C:attention}cualquier{} apuesta", + }, + }, + stake_cry_azure = { + name = "Pozo azur", + colour = "Azur", + text = { + "Los valores en comodines se reducen", + "por {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Pozo ascendente", + colour = "Ascendente", + text = { + "{C:attention}-1{} ranura de carta en la tienda", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Etiqueta astral", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Etiqueta banana", + text = { + "Crea {C:attention}#1#", + "{C:inactive}(Debe haber espacio){}", + }, + }, + tag_cry_bettertop_up = { + name = "Mejor etiqueta de recarga", + text = { + "Genera hasta {C:attention}#1#", + "comodines {C:green}inusuales{}", + "{C:inactive}(Debe haber espacio)", + }, + }, + tag_cry_better_voucher = { + name = "Etiqueta de vale dorado", + text = { + "Agrega un {C:voucher}vale{} de nivel {C:attention}#1#{}", + "en la siguiente tienda", + }, + }, + tag_cry_blur = { + name = "Etiqueta borrosa", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Borroso{}", + }, + }, + tag_cry_booster = { + name = "Etiqueta potenciadora", + text = { + "El siguiente {C:cry_code}paquete potenciador{} contiene", + "el {C:attention}doble{} de cartas y", + "el {C:attention}doble{} de opciones", + }, + }, + tag_cry_bundle = { + name = "Etiqueta de manojo", + text = { + "Crea una {C:attention}Etiqueta estándar{}, {C:tarot}Etiqueta encantada{},", + "{C:attention}Etiqueta de bufón{}, y {C:planet}Etiqueta de meteoro", + }, + }, + tag_cry_cat = { + name = "Etiqueta de gato", + text = { "miau :3", "{C:inactive}Nivel {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Etiqueta de consola", + text = { + "Otorga gratis un", + "{C:cry_code}paquete de programa", + }, + }, + tag_cry_double_m = { + name = "Etiqueta MM", + text = { + "En la tienda hay", + "un {C:legendary}comodín M{} {C:dark_edition}contento", + }, + }, + tag_cry_empowered = { + name = "Etiqueta empoderada", + text = { + "Otorga un {C:spectral}paquete espectral gratis", + "con {C:legendary,E:1}El alma{} y {C:cry_exotic,E:1}Portal{}", + }, + }, + tag_cry_epic = { + name = "Etiqueta épica", + text = { + "En la tienda hay un {C:cry_epic}comodín épico{}", + "a mitad de precio", + }, + }, + tag_cry_gambler = { + name = "Etiqueta del jugador", + text = { + "{C:green}#1# en #2#{} probabilidades de crear", + "una {C:cry_exotic,E:1}Etiqueta empoderada", + }, + }, + tag_cry_glass = { + name = "Etiqueta frágil", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Frágil{}", + }, + }, + tag_cry_glitched = { + name = "Etiqueta errónea", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Erróneo{}", + }, + }, + tag_cry_gold = { + name = "Etiqueta dorada", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Dorado{}", + }, + }, + tag_cry_gourmand = { + name = "Etiqueta gourmand", + text = { + "En la tienda hay un", + "{C:attention}comodín de comida{} gratis", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Otorga gratis", + "un {C:cry_ascendant}Paquete meme", + }, + }, + tag_cry_m = { + name = "Etiqueta contenta", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Contento{}", + }, + }, + tag_cry_memory = { + name = "Etiqueta de memoria", + text = { + "Crea {C:attention}#1#{} copias de", + "la última {C:attention}etiqueta{} usada", + "durante esta partida", + "{s:0.8,C:inactive}Etiquetas de copia excluidas", + "{s:0.8,C:inactive}Actual: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Etiqueta mosaico", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Mosaico{}", + }, + }, + tag_cry_oversat = { + name = "Etiqueta sobresaturada", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Sobresaturado{}", + }, + }, + tag_cry_quadruple = { + name = "Etiqueta cuádruple", + text = { + "Otorga {C:attention}#1#{} copias de la", + "siguiente {C:attention}Tag{} seleccionada", + "{s:0.8,C:inactive}Etiquetas de copia excluidas", + }, + }, + tag_cry_quintuple = { + name = "Etiqueta quíntuple", + text = { + "Otorga {C:attention}#1#{} copias de la", + "siguiente {C:attention}Tag{} seleccionada", + "{s:0.8,C:inactive}Etiquetas de copia excluidas", + }, + }, + tag_cry_rework = { + name = "Etiqueta de retrabajo", + text = { + "La tienda tiene un", + "{C:cry_code}#2#{} {C:dark_edition}#1#", + }, + }, + tag_cry_schematic = { + name = "Etiqueta de esquemáticas", + text = { + "La tienda tiene una", + "{C:attention}Lluvia de ideas", + }, + }, + tag_cry_scope = { + name = "Etiqueta de alcance", + text = { + "{C:attention}+#1# {C:blue}manos{} y {C:red}descartes{}", + "en la siguiente ronda", + }, + }, + tag_cry_triple = { + name = "Etiqueta triple", + text = { + "Otorga {C:attention}#1#{} copias de la", + "siguiente {C:attention}Tag{} seleccionada", + "{s:0.8,C:inactive}Etiquetas de copia excluidas", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "El autómata", + text = { + "Genera hasta {C:attention}#1#", + "cartas de {C:cry_code}código{} al azar", + "{C:inactive}(Debe haber espacio)", + }, + }, + c_cry_eclipse = { + name = "El eclipse", + text = { + "Mejora {C:attention}#1#{} carta", + "seleccionada a", + "una {C:attention}Carta de eco", + }, + }, + c_cry_meld = { + name = "Fusionar", + text = { + "Selecciona un {C:attention}comodín{} o", + "{C:attention}carta de juego{} para", + "hacerla {C:dark_edition}Doble cara", + }, + }, + c_cry_theblessing = { + name = "La bendición", + text = { + "Crea {C:attention}1{}", + "{C:attention}consumible{} aleatorio", + "{C:inactive}(Debe haber espacio){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglífico", + text = { + "Establece la apuesta a {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Lienzo en blanco", + text = { + "{C:attention}+#1#{} tamaño de mano", + }, + }, + v_cry_clone_machine = { + name = "Máquina de clonar", + text = { + "Las etiqueta dobles se vuelven", + "{C:attention}Etiquetas quíntuples{} y", + "son {C:attention}4X{} más comunes", + }, + }, + v_cry_command_prompt = { + name = "Símbolo del sistema", + text = { + "Las cartas de {C:cry_code}código{}", + "pueden aparecer", + "en la {C:attention}tienda{}", + }, + }, + v_cry_copies = { + name = "Copias", + text = { + "Las etiqueta dobles se vuelven", + "{C:attention}Etiquetas triples{} y son", + "son {C:attention}2X{} más comunes", + }, + }, + v_cry_curate = { + name = "Cura", + text = { + "Todas las cartas", + "aparecen con", + "una {C:dark_edition}edición{}", + }, + }, + v_cry_dexterity = { + name = "Destreza", + text = { + "Gana para siempre", + "{C:blue}+#1#{} mano(s)", + "por ronda", + }, + }, + v_cry_double_down = { + name = "Doble de apuesta", + text = { + "Después de cada ronda,", + "{X:dark_edition,C:white} X1.5 {} a todos los valores", + "en la parte trasera de las", + "cartas {C:dark_edition}doble cara{}" + }, + }, + v_cry_double_slit = { + name = "Abertura doble", + text = { + "{C:attention}Fusionar{} puede aparecer", + "en la tienda y", + "paquetes arcanos", + }, + }, + v_cry_double_vision = { + name = "Doble visión", + text = { + "Cartas {C:dark_edition}doble cara{} aparecen", + "{C:attention}4X{} más frecuentemente", + }, + }, + v_cry_fabric = { + name = "Fábrica universal", + text = { + "{C:dark_edition}+#1#{} ranura(s) de comodín", + }, + }, + v_cry_massproduct = { + name = "Producción en masa", + text = { + "Todas las cartas y paquetes", + "en la tienca cuestan {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Aumenta el límite", + "del interés obtenido en cada ronda", + "hasta {C:money}#1#${}", + }, + }, + v_cry_overstock_multi = { + name = "Multicapital", + text = { + "{C:attention}+#1#{} ranura(s) de carta y", + "{C:attention}+#1#{} ranura(s) de paquetes potenciadores", + "disponibles en la tienda", + }, + }, + v_cry_pacclimator = { + name = "Aclamador de planetas", + text = { + "Las cartas de {C:planet}planeta{} aparecen", + "{C:attention}#1# X{} veces más seguido", + "en la tienda", + "Todas las cartas de {C:planet}planeta{}", + "futuras son {C:green}gratis{}", + }, + }, + v_cry_pairamount_plus = { + name = "Parejamiento plus", --this is the best translation you get + text = { + "{C:attention}Reactiva{} todos los comodines M", + "ona vez por cada Pareja", + "{C:attention}contenida{} en la mano jugada", + }, + }, + v_cry_pairing = { + name = "Parejamiento", + text = { + "{C:attention}Reactiva{} todos los comodines M", + "si la mano jugada es una {C:attention}Pareja", + }, + }, + v_cry_quantum_computing = { + name = "Computación cuántica", + text = { + "Las cartas de {C:cry_code}código{} pueden aparecer", + "con edición {C:dark_edition}negativa{}", + }, + }, + v_cry_repair_man = { + name = "Reparejador", --ditto + text = { + "{C:attention}Reactiva{} todos los comodines M", + "si la mano jugada contiene una {C:attention}Pareja", + }, + }, + v_cry_rerollexchange = { + name = "Intercambio de renovaciones", + text = { + "Todas las renovaciones", + "cuestan {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Enlace satelital", + text = { + "Las cartas de {C:cry_code}código{} pueden", + "aparecer en cualquier", + "{C:attention}paquete celestial", + }, + }, + v_cry_scope = { + name = "Alcance galáctico", + text = { + "Crea la carta de {C:planet}planeta", + "por la {C:attention}mano de póker{} jugada", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Aclamador de tarot", + text = { + "Las cartas de {C:tarot}tarot{} aparecen", + "{C:attention}#1# X{} veces más seguido", + "en la tienda", + "Todas las cartas de {C:tarot}tarot{}", + "futuras son {C:green}gratis{}", + }, + }, + v_cry_tag_printer = { + name = "Impresor de etiquetas", + text = { + "Las etiqueta dobles se vuelven", + "{C:attention}Etiquetas cuadrúples{} y son", + "son {C:attention}3X{} más comunes", + }, + }, + v_cry_threers = { + name = "Las 3 Rs", + text={ + "Consigue {C:red}+#1#{}", + "descartes en cada ronda", + "de forma permanente", + }, + }, + v_cry_stickyhand = { + name = "Mano pegajosa", + text = { + "{C:attention}+#1#{} límite de", + "selección de cartas", + }, + }, + v_cry_grapplinghook = { + name = "Gancho de agarre", + text = { + "{C:attention}+#1#{} límite de", + "selección de cartas", + "{C:inactive,s:0.7}Puedes hacer mucho más con esto de lo que tú crees.{}", + + }, + }, + v_cry_hyperspacetether = { + name = "Cuerda hiperespacial", + text = { + "{C:attention}+#1#{} límite de ", + "selección de cartas", + "{C:inactive,s:0.7}NOTA: Tendrá más funcionalidades después{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# en #2#{} probabilidades de ser", + "destruida en cada ronda", + }, + }, + cry_rigged = { + name = "Amañada", + text = { + "Todas las probabilidades {C:cry_code}enumeradas{}", + "son {C:cry_code}garantizadas", + }, + }, + cry_hooked = { + name = "Enganchada", + text = { + "Cuando este comodín se {C:cry_code}activa{},", + "activa {C:cry_code}#1#", + }, + }, + cry_flickering = { + name = "Parpadeante", + text = { + "Destruido después de", + "{C:attention}#1#{} activaciones", + "{C:inactive}({C:attention}#2#{C:inactive} restantes)" + }, + }, + cry_flickering_desc = { --used by choco dice + name = "Parpadeante", + text = { + "Destruido después de", + "{C:attention}#1#{} activaciones", + }, + }, + cry_possessed = { + name = "Poseída", + text = { + "{C:attention}Deshabilita{} e {C:attention}invierte{}", + "los efectos, si es posible", + "Se destruye junto con {C:attention}Fantasma" + }, + }, + food_jokers = { + name = "Comodines de comida", + text = { + "{s:0.8}Banano, Huevo, Helado, Cavendish, Habichuela negra,", + "{s:0.8}Cola sin azúcar, Palomitas de maíz, Ramen,", + "{s:0.8}Agua con gas, Pickle, Ají picante, Caramelo,", + "{s:0.8}Dulce nostálgico, M de comida rápida, etc.", + }, + }, + ev_cry_choco0 = { + name = "", + text = { + "Detalles de un {C:cry_ascendant,E:1}evento{}", + "activo aparecerán aquí" + } + }, + ev_cry_choco1 = { + name = "1: Posesión", + text = { + "{C:attention}Comodines{} y cartas de juego tienen", + "{C:green}1 en 3{} probabilidades of ser Parpadeantes", + "Crea un {C:attention}Fantasma", + "{C:inactive,s:0.7}Has sido poseído por un fantasma, y tu", + "{C:inactive,s:0.7}consciencia está parpadeando." + } + }, + ev_cry_choco2 = { + name = "2: Casa embrujada", + text = { + "No puedes saltar {C:attention}ciegas{}", + "Sólo una {C:attention}renovación{} permitida por tienda", + "Los precios de {C:attention}vales{} se duplican", + "{C:inactive,s:0.7}¡Los espíritus espeluznantes han tomado el control!", + "{C:inactive,s:0.7}¡No toques nada y sale lo más rápido que puedas!", + } + }, + ev_cry_choco3 = { + name = "3: Brebajes de bruja", + text = { + "Crea 3 {C:attention}Pociones", + "Usa una antes del final de la {C:attention}ciega pequeña{},", + "o {C:attention}todos{} los efectos malos se aplicarán en esta {C:attention}apuesta", + "{C:inactive,s:0.7}¡Has sido secuestrado por una bruja!", + "{C:inactive,s:0.7}Ella te ofrece 3 pociones, mirándote de cerca.", + "{C:inactive,s:0.7}Escoje una, para que ella no escoja por tí.", + } + }, + ev_cry_choco4 = { + name = "4: Abismo lunar", + text = { + "Las cartas jugadas tienen una {C:green}1 en 4{} probabilidades", + "de convertirse en una figura de {C:club}tréboles{} al azar", + "Divide {C:attention}multi{} por la cantidad de cartas de figura jugadas", + "{C:inactive,s:0.7}Hasta un hombre que es puro al corazón", + "{C:inactive,s:0.7}y dice sus oraciones a la noche..." + } + }, + ev_cry_choco5 = { + name = "5: Chupasangre", + text = { + "Remueve {C:attention}mejoras{} de todas las cartas jugadas", + "{C:green}1 en 3{} chance de destruir", + "cartas de {C:heart}corazones{} y {C:diamond}diamantes{}", + "{C:inactive,s:0.7}Ten cuidado en la oscuridad de la noche, por", + "{C:inactive,s:0.7,E:1}ellos en las sombras{C:inactive,s:0.7} buscan saciar su sed..." + } + }, + ev_cry_choco6 = { + name = "6: Por favor toma uno", + text = { + "Al {C:attention}terminar una ronda{}, abre un", + "paquete {C:attention}potenciador{} al azar", + "{C:inactive,s:0.7}Mientras paseas por las calles, ves una", + "{C:inactive,s:0.7}caja de varios paquetes potenciadores. ¡Mejor agarrar uno!" + } + }, + ev_cry_choco7 = { + name = "7: Ambiente festivo", + text = { + "Crea 3 {C:attention}Dulce o truco{} y 1 {C:attention}Cesto de dulce", + "Las tiendas tienen un {C:attention}Dulce o truco{} cada ronda", + "Los {C:cry_candy}dulces{} otorgan {C:money}$3{} al obtenerse", + "{C:inactive,s:0.7}Toda la vecindad está decorada por el empeño espeluznante,", + "{C:inactive,s:0.7}¡ven a disfrutar del ambiente festivo!" + } + }, + ev_cry_choco8 = { + name = "8: Lluvia de dulces", + text = { + "Al derrotar una {C:attention}ciega{}, consigue 1 {C:cry_candy}dulce{}", + "por cada mano restante; obtén un {C:attention}comodín de comida{}", + "cuando un {C:cry_candy}dulce{} es generado", + "{C:inactive,s:0.7}¡Los dulces llueven del cielo! Rápido,", + "{C:inactive,s:0.7,E:1}¡agarra lo más que puedas!" + } + }, + ev_cry_choco9 = { + name = "9: Riquezas fantasmales", + text = { + "Gana {C:money}$20", + "Todo el {C:money}dinero{} conseguido {C:attention}duplicado", + "{C:inactive,s:0.7}¡El espectro de un pariente desaparecido tuyo", + "{C:inactive,s:0.7}te visita en el medio de la noche!", + "{C:inactive,s:0.7}Sin una palabra, colocan una bolsa de dinero en tus manos,", + "{C:inactive,s:0.7}sonríen cálidamente, y se despiden mientras se desvanecen en el aire.", + } + }, + ev_cry_choco10 = { + name = "10: Antigüedad venerada", + text = { + "Un {C:attention}comodín{} {C:legendary}legendario{} aparece", + "en la ranura de {C:attention}vales{} por {C:money}$50", + "Sólo comprable como el {C:attention}último{} objeto en la tienda", + "{C:inactive,s:0.7}Has atraído la atención del espíritu de una rélica,", + "{C:inactive,s:0.7}pero no va a ser fácil de calmar.", + } + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Actualizaciones{s:0.7} están deshabilitadas por defecto ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eterno", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Eternos{}", + }, + }, + cry_perishable_booster = { + name = "Perecedero", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Perecederos{}", + }, + }, + cry_rental_booster = { + name = "De alquiler", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Fijado", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Fijados{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eterno", + text = { + "No puede ser intercambiado", + }, + }, + cry_perishable_voucher = { + name = "Perecedero", + text = { + "Se agota al cabo de ", + "{C:attention}#1#{} rondas", + "{C:inactive}({C:attention}#2#{C:inactive} restantes)", + }, + }, + cry_rental_voucher = { + name = "De alquiler", + text = { + "Pierdes {C:money}#1#${}", + "al final de la ronda", + }, + }, + cry_pinned_voucher = { + name = "Fijado", + text = { + "Se mantiene en la tienda", + "hasta que sea redimido", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# en #2#{} probabilidades de ser", + "perdido en cada ronda", + }, + }, + cry_perishable_consumeable = { + name = "Perecedero", + text = { + "Se agota al", + "final de la ronda", + }, + }, + cry_rental_consumeable = { + name = "De alquiler", + text = { + "Pierdes {C:money}#1#${}", + "al final de la ronda, y en uso", + }, + }, + cry_pinned_consumeable = { + name = "Fijado", + text = { + "No puedes usar consumibles", + "no-{C:attention}fijados{}", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# en #2#{} probabilidades de", + "no hacer nada en uso", + }, + }, + p_cry_code_normal_1 = { + name = "Paquete de programa", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas{} de {C:cry_code}código{}", + }, + }, + p_cry_code_normal_2 = { + name = "Paquete de programa", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas{} de {C:cry_code}código{}", + }, + }, + p_cry_code_jumbo_1 = { + name = "Paquete de programa jumbo", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas{} de {C:cry_code}código{}", + }, + }, + p_cry_code_mega_1 = { + name = "Paquete de programa mega", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas{} de {C:cry_code}código{}", + }, + }, + p_cry_empowered = { + name = "Paquete espectral [Etiqueta empoderada]", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas {C:spectral}espectrales{}", + "{s:0.8,C:inactive}(Generado por Etiqueta empoderada)", + }, + }, + p_cry_meme_1 = { + name = "Paquete meme", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas de {C:joker}comodín meme{}", + }, + }, + p_cry_meme_two = { + name = "Paquete meme", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas de {C:joker}comodín meme{}", + }, + }, + p_cry_meme_three = { + name = "Paquete meme", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas de {C:joker}comodín meme{}", + }, + }, + undiscovered_code = { + name = "Por descubrir", + text = { + "Compra o usa", + "esta carta", + "en una partida sin códigos", + "para saber lo que hace", + }, + }, + undiscovered_unique = { + name = "Por descubrir", + text = { + "Compra o usa", + "esta carta", + "en una partida sin códigos", + "para saber lo que hace", + } + }, + cry_green_seal = { + name = "Sello verde", + text = { + "Crea una carta de {C:cry_code}código{}", + "cuando se juega y no puntúa", + "{C:inactive}(Debe haver espacio)", + }, + }, + cry_azure_seal = { + name = "Sello azur", + text = { + "Crea {C:attention}#1#{} {C:planet}planetas{}", + "{C:dark_edition}negativas{} por la {C:attention}mano de póker{}", + "jugada, y {C:red}destruye{} esta carta", + }, + }, + }, + Unique = { + c_cry_potion = { + name = "Poción", + text = { + "Aplica un {C:attention}efecto malo{}", + "desconocido al usarse", + "{C:inactive,s:0.7}Obtenido por Dado de chocolate" + } + } + } + }, + misc = { + achievement_names = { + ach_cry_ace_in_crash = "Un ACE En Mi Bolsillo", + ach_cry_blurred_blurred_joker = "Legalmente Ciego", + ach_cry_bullet_hell = "Shooter Maníaco", + ach_cry_break_infinity = "Rompe El Infinito", + ach_cry_cryptid_the_cryptid = "Criptida El Críptido", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Pase De Googol Play", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Corredor De Bienes Raíces", + ach_cry_jokes_on_you = "La Broma Es Para Tí, Amigo!", + ach_cry_niw_uoy = "!ETSANAG¡", + ach_cry_now_the_fun_begins = "Ahora Se Pone Lo Bueno", + ach_cry_patience_virtue = "La Paciencia Es Una Virtud", + ach_cry_perfectly_balanced = "Perfectamente Balanceado", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Atasco De Tráfico", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "Te Dijimos Que No Lo Hagas", + ach_cry_what_have_you_done = "¡¿QUÉ ACABAS DE HACER?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Consigue Comodín Borroso borroso", + ach_cry_bullet_hell = "Consigue 15 comodines AP", + ach_cry_break_infinity = "Puntúa 1.79e308 fichas en una sola mano", + ach_cry_cryptid_the_cryptid = "Usa Críptido en Críptido", + ach_cry_exodia = "Consigue 5 comodines exóticos", + ach_cry_freak_house = "Juega un Full de color consistiendo de 6s y 9s de corazones mientras tienes a Nice", + ach_cry_googol_play_pass = "Amaña una Carta de Googol Play", + ach_cry_haxxor = "Usa un código de trampas", + ach_cry_home_realtor = "Activa Casa Feliz antes de la apuesta 8 (sin BdE/Antimateria)", + ach_cry_jokes_on_you = "Activa el efecto de La Broma en la apuesta 1 y gana la partida", + ach_cry_niw_uoy = "Llega a la apuesta -8", + ach_cry_now_the_fun_begins = "Consigue a Lienzo", + ach_cry_patience_virtue = "Espera a Ciclo Lavanda por 2 minutos antes de jugar la primera mano y vence la ciega", + ach_cry_perfectly_balanced = "Completa la Baraja Muy Justa en Ascendant Stake", + ach_cry_pull_request = "Haz que ://COMMIT haga aparecer el mismo comodín que destruyó", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Gana en 1 ronda", + ach_cry_used_crash = "Usa ://CHOQUE", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Características", + cry_set_music = "Música", + cry_set_enable_features = "Selecciona las características para activar (se aplica al reiniciar):", + cry_feat_achievements = "Logros", + ["cry_feat_antimatter deck"] = "Baraja de antimateria", + cry_feat_blinds = "Ciegas", + cry_feat_challenges = "Desafíos", + ["cry_feat_code cards"] = "Cartas de código", + ["cry_feat_misc. decks"] = "Barajas misceláneas", + ["cry_feat_https module"] = "Módulo HTTPS", + ["cry_feat_timer mechanics"] = "Mecánicas de temporizador", + ["cry_feat_enhanced decks"] = "Barajas mejoradas", + ["cry_feat_epic jokers"] = "Comodines épicos", + ["cry_feat_exotic jokers"] = "Comodines exóticos", + ["cry_feat_m jokers"] = "Comodines M", + cry_feat_menu = "Menú principal personalizado", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Comodines misceláneos", + cry_feat_planets = "Planetas", + cry_feat_jokerdisplay = "JokerDisplay (no hace nada)", + cry_feat_tags = "Etiquetas", + cry_feat_sleeves = "Mangas", + cry_feat_spectrals = "Espectrales", + cry_feat_spooky = "Actualización espeluznante", + ["cry_feat_more stakes"] = "Pozos", + cry_feat_vouchers = "Vales", + cry_mus_jimball = "Jimball (Funkytown por Lipps Inc. - Copyrighted)", + cry_mus_code = "Cartas de código (://LETS_BREAK_THE_GAME por HexaCryonic)", + cry_mus_exotic = "Comodines exóticos (Joker in Latin por AlexZGreat)", + cry_mus_high_score = "Alto puntaje (Final Boss [For Your Computer] por AlexZGreat)", + + k_cry_program_pack = "Paquete de programa", + k_cry_meme_pack = "Paquete meme", + + cry_critical_hit_ex = "¡Golpe crítico!", + cry_critical_miss_ex = "Fallo crítico...", + + cry_potion1 = "-1 a todos los niveles de mano", + cry_potion2 = "X1.15 tamaño de ciega", + cry_potion3 = "-1 mano y descarte", + + cry_debuff_oldhouse = "Sin fulls", + cry_debuff_oldarm = "Debes jugar 4 o menos cartas", + cry_debuff_oldpillar = "Sin escaleras", + cry_debuff_oldflint = "Sin colores", + cry_debuff_oldmark = "Sin manos que contengan una Pareja", + cry_debuff_obsidian_orb = "Aplica las habilidades de todos los jefes derrotados", + + k_code = "Código", + k_unique = "Único", + b_code_cards = "Cartas de código", + b_unique_cards = "Cartas únicas", + b_pull = "TIRAR", + cry_hooked_ex = "Enganchada!", + k_end_blind = "terminar_ciega", + + cry_code_rank = "INGRESAR CATEGORÍA", + cry_code_enh = "INGRESAR MEJORA", + cry_code_hand = "INGRESAR MANO DE PÓKER", + cry_code_enter_card = "INGRESAR CARTA", + cry_code_apply = "APLICAR", + cry_code_apply_previous = "APLICAR ANTERIOR", + cry_code_exploit = "EXPLOTAR", + cry_code_exploit_previous = "EXPLOTAR ANTERIOR", + cry_code_create = "CREAR", + cry_code_create_previous = "CREAR ANTERIOR", + cry_code_execute = "EJECUTAR", + cry_code_cancel = "CANCELAR", + + b_flip = "VOLTEAR", + b_merge = "COMBINAR", + + cry_hand_bulwark = "Baluarte", + cry_hand_clusterfuck = "Lío de mierda", + cry_hand_ultpair = "Pareja suprema", + + cry_again_q = "¿Otra vez?", + cry_curse = "Maldición", + cry_curse_ex = "¡Maldición!", + cry_sobbing = "Ayúdame...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 ronda", + cry_plus_cryptid = "+1 críptido", + cry_no_triggers = "Sin activaciones restantes!", + cry_unredeemed = "Desredimido...", + cry_active = "Activo", + cry_inactive = "Inactivo", + + k_disable_music = "Desactivar música", + + k_cry_epic = "Épico", + k_cry_exotic = "Exótico", + k_cry_candy = "Dulce", + k_cry_cursed = "Maldito", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Nota de copyright", + cry_notif_jimball_d1 = "Jimball reproduce la canción \"Funkytown\",", + cry_notif_jimball_d2 = "la cual tiene copyright y no puede ser", + cry_notif_jimball_d3 = "usada por streams y videos.", + }, + labels = { + food_jokers = "Comodines de comida", + banana = "Banana", + code = "Código", + unique = "Único", + cry_rigged = "Amañada", + cry_hooked = "Enganchada", + cry_flickering = "Parpadeante", + cry_possessed = "Poseída", + + cry_green_seal = "Sello verde", + cry_azure_seal = "Sello azur", + + cry_astral = "Astral", + cry_blur = "Borroso", + cry_double_sided = "Doble cara", + cry_glass = "Frágil", + cry_glitched = "Errónea", + cry_gold = "Dorada", + cry_m = "Contenta", + cry_mosaic = "Mosaico", + cry_noisy = "Ruidosa", + cry_oversat = "Sobresaturada", + + k_cry_epic = "Épico", + k_cry_exotic = "Exótico", + k_cry_candy = "Dulce", + k_cry_cursed = "Maldito", + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} multi" }, + plus_chips = { "{C:blue}+#2#{} fichas" }, + x_mult = { "{X:red,C:white} X#2#{} multi" }, + x_chips = { "{X:blue,C:white} X#2#{} fichas" }, + h_size = { "{C:attention}+#2#{} tamaño de mano" }, + money = { "{C:money}+$#2#{} al pago" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Actual: {C:red}+#1#{C:inactive} multi)" }, + plus_chips = { "{C:inactive}(Actual: {C:blue}+#1#{C:inactive} fichas)" }, + x_mult = { "{C:inactive}(Actual: {X:red,C:white} X#1# {C:inactive} multi)" }, + x_chips = { "{C:inactive}(Actual: {X:blue,C:white} X#1# {C:inactive} fichas)" }, + h_size = { "{C:inactive}(Actual: {C:attention}+#1#{C:inactive} tamaño de mano)" }, + money = { "{C:inactive}(Actual: {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Crea {C:attention}#2# comodín(es{}" }, + make_tarot = { "Crea {C:attention}#2#{} carta(s) de {C:tarot}tarot{}" }, + make_planet = { "Crea {C:attention}#2#{} carta(s) de {C:planet}planeta{}" }, + make_spectral = { "Crea {C:attention}#2#{} carta(s) {C:spectral}espectrales{}" }, + add_dollars = { "Gana {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "al abrir un {C:attention}Paquete potenciador{}" }, + buying_card = { "al comprar una carta" }, + selling_self = { "al vender esta carta" }, + selling_card = { "al vender una carta" }, + reroll_shop = { "al renovar" }, + ending_shop = { "al final de la {C:attention}tienda{}" }, + skip_blind = { "al saltar una {C:attention}ciega{}" }, + skipping_booster = { "al saltar un {C:attention}Paquete potenciador{}" }, + playing_card_added = { "al añadir una {C:attention}carta{} a tu baraja" }, + first_hand_drawn = { "al empezar la ronda" }, + setting_blind = { "al seleccionar una {C:attention}ciega{}" }, + remove_playing_cards = { "al destruir una carta" }, + using_consumeable = { "al usar un {C:attention}consumible{}" }, + debuffed_hand = { "si la {C:attention}mano{} no está permitida" }, + pre_discard = { "antes de descartar" }, + discard = { "por cada carta descartada" }, + end_of_round = { "al final de la {C:attention}ronda{}" }, + individual_play = { "por cada carta puntuada" }, + individual_hand_score = { "por cada carta en tu mano al puntuar" }, + individual_hand_end = { "por cada carta en tu mano al terminar la {C:attention}ronda{}" }, + repetition_play = { "Reactiva las cartas puntuadas" }, + repetition_hand = { "Reactiva las cartas en tu mano" }, + other_joker = { "por cada {C:attention}comodín{}" }, + before = { "antes de cada {C:attention}mano{}" }, + after = { "después de cada {C:attention}mano{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "si es un {C:attention}comodín{} {C:blue}común{}" }, + buy_uncommon = { "si es un {C:attention}comodín{} {C:green}inusual{}" }, + tarot = { "si la carta es una carta de {C:tarot}tarot{}" }, + planet = { "si la carta es una carta de {C:planet}planeta{}" }, + spectral = { "si la carta es una carta {C:spectral}espectral{}" }, + joker = { "si la carta es un {C:attention}comodín{}" }, + suit = { "si la carta es de {V:1}#3#{}" }, + rank = { "si la carta es un {C:attention}#3#{}" }, + face = { "si la carta es una carta de {C:attention}figura{}" }, + boss = { "si la {C:attention}ciega{} es una {C:attention}ciega jefe{}" }, + non_boss = { "si la {C:attention}ciega{} es una {C:attention}ciega no-jefe{}" }, + small = { "si la {C:attention}ciega{} es una {C:attention}ciega pequeña{}" }, + big = { "si la {C:attention}ciega{} es una {C:attention}ciega grande{}" }, + first = { "si es la {C:attention}primera mano{}" }, + last = { "si es la {C:attention}última mano{}" }, + common = { "si es un {C:attention}comodín{} {C:blue}común{}" }, + uncommon = { "si es un {C:attention}comodín{} {C:green}inusual{}" }, + rare = { "si es un {C:attention}comodín{} {C:red}raro{}" }, + poker_hand = { "si la mano es un(a) {C:attention}#3#{}" }, + or_more = { "si la mano contiene {C:attention}#3#{} o más cartas" }, + or_less = { "si la mano contiene {C:attention}#3#{} o menos cartas" }, + hands_left = { "si tienes #3# {C:blue}manos{} restantes al final de la ronda" }, + discards_left = { "si tienes #3# {C:red}descartes{} restantes al final de la ronda" }, + first_discard = { "si es el {C:attention}primer descarte{}" }, + last_discard = { "si es el {C:attention}último descarte{}" }, + odds = { "con {C:green}#4# {C:green}en {C:green}#3#{} probabilidades" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# fichas"}, + a_powmult = {"^#1# multi"}, + a_powchips = {"^#1# fichas"}, + a_powmultchips = {"^#1# multi+fichas"}, + a_round = {"+#1# ronda"}, + a_candy = {"+#1# Candy"}, + a_xchips_minus = {"-X#1# fichas"}, + a_powmult_minus = {"-^#1# multi"}, + a_powchips_minus = {"-^#1# fichas"}, + a_powmultchips_minus = {"-^#1# multi+fichas"}, + a_round_minus = {"-#1# ronda"}, + + a_tag = {"#1# etiqueta"}, + a_tags = {"#1# etiquetas"}, + + cry_sticker_name = {"Pegatina #1#"}, + cry_sticker_desc = { + "Usaste este comodín", + "para ganaer en la dificultad #2##1#", + "#2#Pozo#3#" + }, + + cry_art = {"Arte: #1#"}, + cry_code = {"Código: #1#"}, + cry_idea = {"Idea: #1#"} + + }, + v_text = { + ch_c_cry_all_perishable = {"Todos los comodines son {C:eternal}Perecederos{}"}, + ch_c_cry_all_rental = {"Todos los comodines son {C:eternal}de Alquiler{}"}, + ch_c_cry_all_pinned = {"Todos los comodines son {C:eternal}Fijados{}"}, + ch_c_cry_all_banana = {"Todos los comodines son {C:eternal}Banana{}"}, + ch_c_all_rnj = {"Todos los comodines son {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"Todos los objetos comprables tienen todas las pegatinas"}, + ch_c_cry_rush_hour = {"Todas las ciegas jefe son {C:attention}El reloj{} or {C:attention}Ciclo lavanda"}, + ch_c_cry_rush_hour_ii = {"Todas las ciegas son {C:attention}ciegas jefe{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}El reloj{} y {C:attention}Ciclo lavanda{} aumentan el {C:attention}doble{} de rápido"}, + ch_c_cry_no_tags = {"Saltar ciegas no está {C:attention}permitido{}"}, + ch_c_cry_no_vouchers = {"Los {C:attention}vales{} ya no aparecen en la tienda"}, + ch_c_cry_no_boosters = {"Los {C:attention}paquetes potenciadores{} ya no aparecen en la tienda"}, + ch_c_cry_no_rerolls = {"Las renovaciones no están {C:attention}permitidas{}"}, + ch_c_cry_no_consumables = {"Los {C:attention}consumibles{} ya no aparecen"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/es_ES.lua b/Cryptid/localization/es_ES.lua new file mode 100644 index 0000000..f7e693b --- /dev/null +++ b/Cryptid/localization/es_ES.lua @@ -0,0 +1,3955 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Baraja de antimateria", + text = { + "Aplica las {C:legendary,E:1}ventajas{}", + "de {C:attention}todas{} las barajas", + }, + }, + b_cry_beta = { + name = "Baraja nostálgica", + text = { + "Las ranuras de {C:attention}comodín{} y", + "{C:attention}consumibles{} se {C:attention}combinan", + "Las ciegas {C:attention}nostálgicas{} reemplazan", + "sus ciegas actualizadas" + }, + }, + b_cry_blank = { + name = "Baraja en blanco", + text = { + "{C:inactive}¿No hace nada?", + }, + }, + b_cry_bountiful = { + name = "Baraja abundante", + text = { + "Siempre saca 5 cartas después", + "de cada {C:attention}mano jugada{} o {C:attention}descarte{}", + }, + }, + b_cry_CCD = { + name = "Baraja CCD", + text = { + "Todas las cartas también son", + "un consumible {C:attention}aleatorio{}", + }, + }, + b_cry_conveyor = { + name = "Baraja transportadora", + text = { + "Los comodines {C:attention}no{} se pueden mover", + "Al principio de la runda,", + "{C:attention}duplica{} el comodín del extremo derecho", + "y {C:attention}destruye{} el comodín del extremo izquierdo", + }, + }, + b_cry_critical = { + name = "Baraja crítica", + text = { + "Después de cada mano jugada,", + "{C:green}#1# en 4{} probabilidades para {X:dark_edition,C:white} ^2 {} multi", + "{C:green}#1# en 8{} probabilidades para {X:dark_edition,C:white} ^0.5 {} multi", + }, + }, + b_cry_encoded = { + name = "Baraja codificada", + text = { + "Comienza con un {C:cry_code,T:j_cry_CodeJoker}Comodín de código{}", + "y {C:cry_code,T:j_cry_copypaste}Copiar y pegar{}", + "Sólo aparecen {C:cry_code}cartas de código{} en la tienda", + }, + }, + b_cry_equilibrium = { + name = "Baraja de equilibrio", + text = { + "Todas las cartas tienen la ", + "{C:attention}misma probabilidad{} de", + "aparecer en las tiendas,", + "comienza la partida con", + "{C:attention,T:v_overstock_plus}Excedente plus", + }, + }, + b_cry_glowing = { + name = "Baraja brillante", + text = { + "Multiplica los valores de", + "todos los comodines por {X:dark_edition,C:white} X1.25 {}", + "cuando se derrota a la {C:attention}ciega jefe{}", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Baraja infinita", + text = { + "Puedes seleccionar {C:attention}cualquier", + "cantidad de cartas", + "{C:attention}+1{} tamaño de mano", + }, + }, + b_cry_misprint = { + name = "Baraja de errata", + text = { + "Los valures de cartas", + "y manos de póker", + "se {C:attention}aleatorizan", + }, + }, + b_cry_redeemed = { + name = "Baraja redimida", + text = { + "Cuando se compra un {C:attention}vale{},", + "obtén sus {C:attention}niveles extras", + }, + }, + b_cry_spooky = { + name = "Baraja espeluznante", + text = { + "Comienza con un {C:attention,T:j_cry_chocolate_dice}Dado de chocolate {C:eternal}eterno{}", + "Después de cada {C:attention}apuesta{}, crea un comodín", + "de {C:cry_candy}dulce{} o {X:cry_cursed,C:white}maldito{}", + } + }, + b_cry_very_fair = { + name = "Baraja Muy Justa", + text = { + "{C:blue}-2{} manos, {C:red}-2{} descartes", + "en cada ronda", + "Los {C:attention}vales{} ya no", + "aparecen en la tienda", + }, + }, + b_cry_wormhole = { + name = "Baraja de agujero de gusano", + text = { + "Comienza con un comodín {C:cry_exotic}exótico{C:attention}", + "Los comodines son {C:attention}20X{} más", + "probables de ser {C:dark_edition}Negativos", + "{C:attention}-2{} ranuras de comodín", + }, + }, + b_cry_legendary = { + name = "Baraja legendaria", + text = { + "Comienza con un comodín {C:legendary}legendario{C:legendary}", + "{C:green}1 en 5{} probabilidades para crear otro", + "cuando se derrota a la ciega jefe", + "{C:inactive}(debe haber espacio){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "La caja", + text = { + "Todos los comodines comunes", + "se debilitan", + }, + }, + bl_cry_clock = { + name = "El reloj", + text = { + "+0.1X requisitos de ciega por cada", + "3 segundos pasados en esta apuesta", + }, + }, + bl_cry_hammer = { + name = "El martillo", + text = { + "Todas las cartas con rango", + "impar se debilitan", + }, + }, + bl_cry_joke = { + name = "La broma", + text = { + "Si la puntuación excede 2X de los requisitos,", + "establece la apuesta a un múltiplo de #1#", + }, + }, + bl_cry_magic = { + name = "La magia", + text = { + "Todas las cartas con rango", + "par se debilitan", + }, + }, + bl_cry_lavender_loop = { + name = "Ciclo lavanda", + text = { + "1.25X requisitos de ciega por cada", + "1.5 segundos pasados en esta ronda", + }, + }, + bl_cry_obsidian_orb = { + name = "Orbe obsidiana", + text = { + "Aplica las habilidades de", + "todos los jefes derrotados", + }, + }, + bl_cry_oldarm = { + name = "El brazo nostálgico", + text = { + "Debes jugar 4", + "o menos cartas", + }, + }, + bl_cry_oldfish = { + name = "El pez nostálgico", + text = { + "Todas las manos empiezan", + "con 1 multi", + }, + }, + bl_cry_oldflint = { + name = "El pedernal nostálgico", + text = { + "Sin colores", + }, + }, + bl_cry_oldhouse = { + name = "La casa nostálgica", + text = { + "Sin fulls", + }, + }, + bl_cry_oldmanacle = { + name = "El grillete nostálgico", + text = { + "Divide multi por descartes", + }, + }, + bl_cry_oldmark = { + name = "La marca nostálgica", + text = { + "Sin manos que", + "contengan una Pareja", + }, + }, + bl_cry_oldox = { + name = "El buey nostálgico", + text = { + "Todas las manos empiezan", + "con 0 fichas", + }, + }, + bl_cry_oldpillar = { + name = "El pilar nostálgico", + text = { + "Sin escaleras", + }, + }, + bl_cry_oldserpent = { + name = "La serpiente nostálgica", + text = { + "Divide multi por el nivel", + "de la mano de póker jugada", + }, + }, + bl_cry_pin = { + name = "El alfiler", + text = { + "Los comodines épicos o de mayor", + "rareza se debilitan", + }, + }, + bl_cry_pinkbow = { + name = "Moño rosado", + text = { + "Aleatoriza la categoría de las cartas", + "en tu mano al jugar", + }, + }, + bl_cry_sapphire_stamp = { + name = "Estampilla safiro", + text = { + "Selecciona una carta extra, deselecciona", + "una carta aleatoria antes de puntuar", + }, + }, + bl_cry_shackle = { + name = "El eslabón", + text = { + "Todos los comodines negativos", + "se debilitan", + }, + }, + bl_cry_striker = { + name = "El ariete", + text = { + "Todos los comodines raros", + "se debilitan", + }, + }, + bl_cry_tax = { + name = "El impuesto", + text = { + "Puntuación por mano limitado a", + "0.4X requisitos de ciega", + }, + }, + bl_cry_tornado = { + name = "Tornado turquesa", + text = { + "#1# en #2# probabilidades para", + "que la mano jugada no puntúe", + }, + }, + bl_cry_trick = { + name = "El truco", + text = { + "Después de cada mano, voltea todas", + "las cartas boca-arriba en tu mano", + }, + }, + bl_cry_vermillion_virus = { + name = "Virus bermellón", + text = { + "Se reemplaza un comodín", + "al azar en cada mano", + }, + }, + bl_cry_windmill = { + name = "El molino", + text = { + "Todos los comodines inusuales", + "se debilitan", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASE", + text = { + "Convierte {C:cry_code}#1#{} carta seleccionada", + "a una mejora {C:cry_code}a elección{}", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destruye un comodín {C:cry_code}seleccionado{},", + "crea un comodín {C:cry_code}nuevo{}", + "de la {C:cry_code}misma rareza", + }, + }, + c_cry_crash = { + name = "://CHOQUE", + text = { + "{C:cry_code,E:1}No.", + }, + }, + c_cry_ctrl_v = { + name = "://CTRL+V", + text = { + "Crea una {C:cry_code}copia{} de un {C:cry_code}comodín{},", + "carta de juego, o consumible seleccionado" + }, + }, + c_cry_delete = { + name = "://ELIMINAR", + text = { + "Remueve {C:cry_code}permanentemente{} un", + "objeto {C:cry_code}seleccionado{} de la tienda", + "{C:inactive,s:0.8}El objeto no puede aparecer otra vez en esta partida", + }, + }, + c_cry_divide = { + name = "://DIVIDIR", + text = { + "{C:cry_code}Divide en 2{} todos los precios", + "listados en la tienda actual", + }, + }, + c_cry_exploit = { + name = "://EXPLOTAR", + text = { + "Cualquier mano jugada se considera que", + "{C:cry_code}contiene{} una mano de póker {C:cry_code}a elección{},", + "se reinicia al final de la ronda", + "{C:inactive,s:0.8}Las manos secretas deben ser", + "{C:inactive,s:0.8}descubiertas para ser válidas", + }, + }, + c_cry_hook = { + name = "ENGANCHAR://", + text = { + "Selecciona dos comodines para {C:cry_code}engancharlos", + "{C:inactive,s:0.8}Sólo funciona correctamente si los", + "{C:inactive,s:0.8}comodines se activan en el mismo contexto,", + "{C:inactive,s:0.8}(ej. Comodín y El dúo (ambos después de puntuar))", + }, + }, + c_cry_inst = { + name = "://INSTANCIAR", + text = { + "Saca una carta con una {C:cry_code}categoría{} seleccionada", + "y otra carta con un {C:cry_code}palo{} seleccionado", + "{C:inactive}(si es posible){}", + }, + }, + + c_cry_machinecode = { + name = "://CÓDIGOMÁQUINA", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { + "Añade {C:dark_edition}Erróneo{} a todas", + "las cartas {C:cry_code}en tu mano" }, + }, + c_cry_merge = { + name = "://FUNDIR", + text = { + "Combina un {C:cry_code}consumible{} seleccionado", + "con una {C:cry_code}carta de juego{} seleccionada", + }, + }, + c_cry_multiply = { + name = "://MULTIPLICAR", + text = { + "{C:cry_code}Duplica{} todos los valores de", + "un {C:cry_code}comodín{} seleccionado hasta", + "el final de la ronda", + }, + }, + c_cry_patch = { + name = "://PATCH", + text = { + "Rehabilita y remueve todas las pegatinas", + "de todos los objetos visibles", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "La siguiente ciega derrotada", + "entrega {C:cry_code}X#1#{} interés", + }, + }, + c_cry_oboe = { + name = "://PORUNPASO", + text = { + "El siguiente {C:cry_code}paquete potenciador{} contiene", + "{C:cry_code}#1#{} carta adicional y", + "{C:cry_code}#1#{} opción adicional", + "{C:inactive}(Actual: {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REINICIAR", + text = { + "Reobtiene tus {C:blue}manos{} y {C:red}descartes{},", + "devuelve {C:cry_code}todas{} las cartas a la baraja", + "y saca una {C:cry_code}nueva{} mano", + }, + }, + c_cry_revert = { + name = "://REVERTIR", + text = { + "Establece el {C:cry_code}estado de juego{} al", + "inicio de {C:cry_code}esta apuesta{}", + }, + }, + c_cry_rework = { + name = "://REHACER", + text = { + "Destruye un comodín {C:cry_code}seleccionado{},", + "crea una {C:cry_code}Etiqueta de retrabajo{} con", + "una edición {C:cry_code}mejorada{}", + "{C:inactive,s:0.8}Se mejora usando el orden en la colección", + }, + }, + c_cry_run = { + name = "://CORRER", + text = { + "Visita una {C:cry_code}tienda", + "durante una {C:cry_code}ciega", + }, + }, + c_cry_seed = { + name = "://SEMILLA", + text = { + "Selecciona un comodín", + "o carta de juego", + "para volverla {C:cry_code}amañada", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { + "Termina la {C:cry_code}ciega{} no-jefe actual", + "{C:cry_code}sin{} obtener dinero" + }, + }, + c_cry_spaghetti = { + name = "://ESPAGUETI", + text = { + "Crea un comodín de comida", + "{C:cry_code}erróneo", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convierte {C:cry_code}#1#{} cartas seleccionadas", + "a una categoría {C:cry_code}a elección{}", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} multi", + }, + }, + e_cry_blur = { + name = "Borrosa", + text = { + "{C:attention}Reactiva{} esta", + "carta {C:attention}1{} vez", + "{C:green}#1# en #2#{} probabilidades", + "para reactivar {C:attention}#3#{}", + "vez más", + }, + }, + e_cry_double_sided = { + name = "Doble cara", + text = { + "Esta carta puede ser", + "{C:attention}volteada{} para revelar", + "una carta diferente", + "{C:inactive}(El lado en blanco puede", + "{C:inactive}combinarse con otra carta)", + }, + }, + e_cry_glass = { + name = "Frágil", + label = "Frágil", + text = { + "{C:white,X:mult} X#3# {} multi", + "{C:green}#1# en #2#{} probabilidades que", + "esta carta no sea {C:red}destruida", + "al activarse", + }, + }, + e_cry_glitched = { + name = "Errónea", + text = { + "Todos los valores en esta carta", + "son {C:dark_edition}aleatorizados{}", + "entre {C:attention}X0.1{} y {C:attention}X10{}", + "{C:inactive}(si es posible){}", + }, + }, + e_cry_gold = { + name = "Dorada", + label = "Dorada", + text = { + "Gana {C:money}$#1#{} cuando se usa", + "o activa", + }, + }, + e_cry_m = { + name = "Contenta", + text = { + "{C:mult}+#1#{} multi", + "Esta carta se siente", + "bastante {C:attention}contenta{}", + }, + }, + e_cry_mosaic = { + name = "Mosaico", + text = { + "{X:chips,C:white} X#1# {} fichas", + }, + }, + e_cry_noisy = { + name = "Ruidosa", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Sobresaturada", + text = { + "Todos los valores", + "en esta carta", + "son {C:attention}duplicados{}", + "{C:inactive}(si es posible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Carta de eco", + text = { + "{C:green}#2# en #3#{} probabilidades de", + "{C:attention}reactivar{} #1# veces", + "adicionales al puntuar", + }, + }, + }, + Joker = { + j_cry_adroit = { + name = "Comodín hábil", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_altgoogol = { + name = "Carta de Googol Play nostálgica", + text = { + "Vende esta carta para crear", + "{C:attention}2{} copias del {C:attention}Joker{} del extremo izquierdo", + "{C:inactive,s:0.8}No copia otras Cartas de Googol Play nostálgicas{}", + }, + }, + j_cry_antennastoheaven = { + name = "...como antenas al cielo", + text = { + "Este comodín consigue", + "{X:chips,C:white} X#1# {} fichas cuando cada", + "{C:attention}7{} o {C:attention}4{} jugado puntúa", + "{C:inactive}(Actual: {X:chips,C:white}X#2# {C:inactive} fichas)", + }, + }, + j_cry_apjoker = { + name = "Comodín AP", + text = { "{X:mult,C:white} X#1# {} multi contra las {C:attention}ciegas jefe{}" }, + }, + j_cry_astral_bottle = { + name = "Astral en una botella", + text = { + "Al venderse, aplica {C:dark_edition}Astral{}", + "y {C:attention}Perecedero{} a", + "un {C:attention}comodín{} aleatorio", + } + }, + + j_cry_big_cube = { + name = "Cubo grande", + text = { + "{X:chips,C:white} X#1# {} fichas", + }, + }, + j_cry_biggestm = { + name = "Gigante", + text = { + "{X:mult,C:white} X#1# {} multi hasta el final", + "de la ronda si la {C:attention}mano de póker{}", + "es una {C:attention}#2#{}", + "{C:inactive}(Actual: {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}no es obeso, sólo de huesos grandes.", + }, + }, + j_cry_blacklist = { + name = "Lista negra", + text = { + "Si un {C:attention}#1#{} se juega o está en tu mano,", + "establece {C:chips}fichas{} y {C:mult}multi{} a 0", + "{C:red,E:2}se autodestruye{} si no hay un {C:attention}#1#{} en la baraja", + "{C:inactive,s:0.8}La categoría no cambia" + } + }, + j_cry_blender = { + name = "Blender", + text = { + "Crea un consumible", + "{C:attention}aleatorio{} cuando una", + "carta de {C:cry_code}código{} es usada", + "{C:inactive}(debe haber espacio){}", + }, + }, + j_cry_blurred = { + name = "Comodín borroso", + text = { + "Obtén {C:blue}+#1#{} mano(s) al", + "seleccionar una {C:attention}ciega{}", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Cada {C:attention}comodín{} da {C:chips}+#1#{} fichas", + "aumenta la cantidad por {C:chips}+#2#{} si la ", + "{C:attention}mano de póker{} es una {C:attention}#3#{}", + "{C:inactive,s:0.8}Los comodines contentos dan{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}fichas{}", + }, + }, + j_cry_bonkers = { + name = "Comodín trastornado", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_bonusjoker = { + name = "Comodín adicional", + text = { + "{C:green}#1# en #2#{} probabilidades por cada", + "carta {C:attention}adicional{} jugada para aumentar", + "ranuras de {C:attention}comodín{} o {C:attention}consumible", + "por {C:dark_edition}1{} al puntuar", + "{C:red}Funciona 2 veces por runda", + "{C:inactive,s:0.8}(probabilidad igual por cada uno){}", + }, + }, + j_cry_booster = { + name = "Comodín potenciador", + text = { + "{C:attention}+#1#{} ranura de paquete potenciador", + "disponible en la tienda", + }, + }, + j_cry_boredom = { + name = "Aburrimiento", + text = { + "{C:green}#1# en #2#{} probabilidades para", + "{C:attention}reactivar{} cada {C:attention}comodín{}", + "o {C:attention}carta jugada{}", + "{C:inactive,s:0.8}No afecta otros Aburrimientos{}", + }, + }, + j_cry_brittle = { + name = "Dulce frágil", + text = { + "Por las siguientes {C:attention}#1#{} manos,", + "añade {C:attention}Piedra{}, {C:attention}Oro{}, o {C:attention}Acero{} a", + "la carta del extremo derecho que puntúa" + } + }, + j_cry_bubblem = { + name = "Burbuja M", + text = { + "Crea un {C:attention}Comodín contento{} {C:dark_edition}laminado", + "si la mano jugada contiene", + "un {C:attention}#1#{}", + "{C:red,E:2}se autodestruye{}", + }, + }, + j_cry_busdriver = { + name = "Conductor de bus", + text = { + "{C:green}#1# en #3#{} probabilidades", + "para {C:mult}+#2#{} multi", + "{C:green}1 en 4{} probabilidades", + "para {C:mult}-#2#{} multi", + }, + }, + j_cry_candy_basket = { + name = "Cesta de dulces", + text = { + "Vende esta carta para crear {C:attention}#1#{} {C:cry_candy}dulces", + "{C:attention}+#2#{} {C:cry_candy}dulce{} cada {C:attention}2{} ciegas derrotadas", + "{C:attention}+#3#{} {C:cry_candy}dulces{} al derrotar la {C:attention}ciega jefe{}" + } + }, + j_cry_candy_buttons = { + name = "Botones de dulce", + text = { + "Las siguientes {C:attention}#1#{} renovaciones", + "cuestan {C:money}$1{}", + } + }, + j_cry_candy_cane = { + name = "Bastón de dulce", + text = { + "Por las siguientes {C:attention}#1#{} rondas,", + "las cartas de juego otorgan {C:money}$#2#", + "cuando se {C:attention}reactivan" + } + }, + j_cry_candy_dagger = { + name = "Daga de dulce", + text = { + "Cuando se selecciona la {C:attention}ciega{},", + "destruye al comodín de la derecha", + "para crear un {C:cry_candy}dulce{}", + } + }, + j_cry_candy_sticks = { + name = "Palos de dulce", + text = { + "El efecto de la siguiente ciega jefe se desactiva", + "hasta que hayas jugado {C:attention}#1#{} msnos", + } + }, + j_cry_canvas = { + name = "Lienzo", + text = { + "{C:attention}Reactiva{} todos los {C:attention}comodines{} a la izquierda", + "una vez por {C:attention}todos{} los {C:attention}comodines{} no-{C:blue}comunes", + "a la derecha de este comodín", + }, + }, + j_cry_caramel = { + name = "Caramelo", + text = { + "Cada carta jugada otorga", + "{X:mult,C:white}X#1#{} multi cuando puntúa", + "por las siguientes {C:attention}#2#{} rondas", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Reactiva el comodín del {C:attention}extremo izquierdo{}", + "{C:attention}#1#{} veces adicionales", + }, + }, + j_cry_chili_pepper = { + name = "Ají picante", + text = { + "Este comodín consigue {X:mult,C:white} X#2# {} multi", + "al final de la ronda,", + "{C:red,E:2}se autodestruye{} después de {C:attention}#3#{} rondas", + "{C:inactive}(Actual:{} {X:mult,C:white} X#1# {} {C:inactive}multi){}", + }, + }, + j_cry_chocolate_dice = { + name = "Dado de chocolate", + text = { + "Gira un {C:green}d10{} al derrotar", + "la {C:attention}ciega jefe{} para", + "empezar un {C:cry_ascendant,E:1}evento", + "{C:inactive}(Actual: #1#)" + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} fichas y {X:dark_edition,C:white}^#1#{} multi", + "si tienes {C:attention}exactamente{} #2#", + "manos restantes", + }, + }, + j_cry_circus = { + name = "Circo", + text = { + "Comodines {C:red}raros{} otorgan {X:mult,C:white} X#1# {} multi", + "Comodines {C:cry_epic}épicos{} otorgan {X:mult,C:white} X#2# {} multi", + "Comodines {C:legendary}legendarios{} otorgan {X:mult,C:white} X#3# {} multi", + "Comodines {C:cry_exotic}exóticos{} otorgan {X:mult,C:white} X#4# {} multi", + }, + }, + j_cry_clash = { + name = "El conflicto", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "una {C:attention}#2#", + }, + }, + + j_cry_CodeJoker = { + name = "Comodín de código", + text = { + "Crea una {C:cry_code}carta de código{}", + "{C:dark_edition}negativa{} cuando se", + "selecciona una {C:attention}ciega{}", + }, + }, + j_cry_coin = { + name = "Moneda crypto", + text = { + "Gana entre", + "{C:money}$#1#{} y {C:money}$#2#{} por", + "cada comodín {C:attention}vendido{}", + }, + }, + j_cry_compound_interest = { + name = "Interés compuesto", + text = { + "Gana {C:money}#1#%{} de tu dinero total", + "al final de la ronda,", + "aumenta por {C:money}#2#%{} por cada", + "pago consecutivo", + }, + }, + j_cry_copypaste = { + name = "Copiar y pegar", + text = { + "Cuando una carta {C:cry_code}código{} se usa,", + "{C:green}#1# en #2#{} probabilidades para añadir", + "una copia a tus consumibles", + "{C:inactive}(debe haber espacio)", + }, + }, + j_cry_cotton_candy = { + name = "Algodón de azúcar", + text = { + "Al venderse, los {C:attention}comodines{}", + "adyacentes se vuelven {C:dark_edition}negativos{}" + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "Este comodín gana {C:chips}+#2#{} fichas", + "por cada {C:attention}renovación{} en la tienda", + "{C:green}Todas las renovaciones son gratis{}", + "{C:inactive}(Actual: {C:chips}+#1#{C:inactive} fichas)", + }, + }, + j_cry_cryptidmoment = { + name = "Cadena M", + text = { + "Vende esta carta para", + "añadir {C:money}$#1#{} de {C:attention}valor de venta{}", + "a cada {C:attention}comodín{}", + }, + }, + j_cry_cube = { + name = "Cubo", + text = { + "{C:chips}+#1#{} fichas", + }, + }, + j_cry_curse_sob = { + name = "Sollozo", + text = { + "{C:edition,E:1}no puedes{} {C:cry_ascendant,E:1}correr...{}", + "{C:edition,E:1}no puedes{} {C:cry_ascendant,E:1}esconderte...{}", + "{C:dark_edition,E:1}no puedes escapar...{}", + "{C:inactive}(debe haber espacio){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "Este comodín gana {C:chips}+#2#{} fichas", + "por cada carta {C:attention}comprada{}", + "{C:inactive}(Actual: {C:chips}+#1#{C:inactive} fichas)", + }, + }, + j_cry_cut = { + name = "Cortar", + text = { + "Este comodín destruye una", + "carta de {C:cry_code}código{} al azar,", + "y gana {X:mult,C:white} X#1# {} multi", + "al final de la {C:attention}tienda{}", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_delirious = { + name = "Comodín delirante", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_discreet = { + name = "Comodín discreto", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_doodlem = { + name = "Garabato M", + text = { + "Crea 2 {C:attention}consumibles{} {C:dark_edition}negativos{}", + "cuando se selecciona una {C:attention}ciega{}", + "Crea 1 {C:attention}consumibles{} adicional", + "por cada {C:attention}comodín contento{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Escala doble", + text = { + "Los {C:attention}comodines{} escaladores", + "aumentan {C:attention}cuadráticamente", + "{C:inactive,s:0.8}(ej. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(crece por by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Tiro al blanco", + text = { + "Este comodín consigue {X:mult,C:white} X#1# {} multi por", + "cada carta de {V:1}#2#{} jugada que no puntúa,", + "el palo cambia en cada ronda", + "{C:inactive}(Actual: {X:mult,C:white} X#3# {C:inactive} multi)", + }, + }, + j_cry_dubious = { + name = "Comodín dudoso", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "una {C:attention}#2#", + } + }, + j_cry_duos = { + name = "Los dúos", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "una {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Este comodín gana {X:mult,C:white} X#2# {} multi", + "cuando un {C:attention}comodín{} o", + "carta de juego puntúa", + "{C:inactive}(Actual: {X:mult,C:white} X#1# {C:inactive} multi)", + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Saca tu {C:green}baraja completa{} a tu mano", + "al seleccionar una {C:attention}ciega{}", + "{C:inactive,s:0.8}\"Si no puedes manejarme a mi 1x,", + "{C:inactive,s:0.8}no me mereces a mi 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "Cuando obtienes una {C:attention}etiqueta{},", + "crea {C:attention}#1#{} copias de ésta,", + "y {C:attention}aumenta{} la cantidad de", + "copias por {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Comodines aparecen usando el", + "orden de la {C:attention}colección{}", + "Crea {C:attention}#1#{} comodín(es) {C:dark_edition}negativos{}", + "al jugar una mano", + "{C:inactive,s:0.8}Comodines {C:cry_exotic,s:0.8}exóticos {C:inactive,s:0.8}o mejores no pueden aparecer", + "{s:0.8}Último comodín generado: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Llama eterna", + text = { + "Este comodín gana {X:mult,C:white} X#1# {} multi", + "por cada carta {C:attention}vendida{}", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_exoplanet = { + name = "Exoplaneta", + text = { + "Las cartas {C:dark_edition}holográficas{}", + "otorgan {C:mult}+#1#{} multi", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "Este comodín gana {X:dark_edition,C:white} ^#1# {} multi", + "cuando se activa {X:red,C:white}Xmulti{}", + "{C:inactive}(Actual: {X:dark_edition,C:white} ^#2# {C:inactive} multi)", + }, + }, + j_cry_exposed = { + name = "Expuesto", + text = { + "Reactiva todas las cartas {C:attention}numéricas{}", + "{C:attention}#1#{} veces adicionales", + "Todas las cartas de {C:attention}figura{} se debilitan", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} multi si", + "las cartas jugadas puntúan", + "{C:attention}#2#{} o menos veces", + }, + }, + j_cry_filler = { + name = "El relleno", + text={ + "{X:mult,C:white} X#1#{} multi", + "si la mano contiene", + "una {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Dedos fractales", + text = { + "{C:attention}+#1#{} límite de selección de cartas", + }, + }, + j_cry_flip_side = { + name = "Por el otro lado", + text = { + "Los comodines {C:dark_edition}doble cara{} usan", + "su lado trasero para sus efectos", + "en vez de su lado delantero", + "{C:attention}Reactiva{} todos los comodines {C:dark_edition}doble cara{}" + }, + }, + j_cry_foodm = { + name = "M de comida rápida", + text = { + "{C:mult}+#1#{} multi", + "{C:red,E:2}se autodestruye{} en {C:attention}#2#{} ronda(s)", + "Aumenta por {C:attention}#3#{} ronda cuando un", + "{C:attention}comodín contento{} es {C:attention}vendido{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foolhardy = { + name = "Comodín temerario", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_formidiulosus = { + name = "Formidiulosus", + text = { + "Cuando un comodín {X:cry_cursed,C:white}maldito{} es obtenido, destrúyelo", + "Crea {C:attention}#1#{} {C:cry_candy}dulces {C:dark_edition}negativos{} al final de la tienda", + "Gana {X:dark_edition,C:white}^#2#{} multi por cada {C:cry_candy}dulce{} en tu posesión", + "{C:inactive}(Actual: {X:dark_edition,C:white}^#3#{C:inactive} multi)", + }, + }, + j_cry_foxy = { + name = "Comodín astuto", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "Este comodín gana {C:chips}+#2#{} fichas", + "si la mano jugada {C:attention}no es{}", + "la {C:attention}mano de póker{} más jugada", + "{C:inactive}(Actual: {C:chips}+#1#{C:inactive} fichas)", + }, + }, + j_cry_fuckedup = { + name = "Comodín jodido", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_gardenfork = { + name = "El jardín de las bifurcaciones", + text = { + "Gana {C:money}$#1#{} si la {C:attention}mano jugada{}", + "contiene un {C:attention}As{} y un {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Duplica{} todos los valores", + "del {C:attention}Joker{} del extremo izquierdo", + "al final de la ronda", + }, + }, + j_cry_ghost = { + name = "Fantasma", + text = { + "Al final de la ronda:", + "{C:green}#1# en #2#{} probabilidades para", + "{C:attention}poseer{} un {C:attention}comodín{} aleatorio", + "{C:green}#1# en #3#{} probabilidades para", + "{E:2,C:red}autodestruirse" + } + }, + j_cry_giggly = { + name = "Comodín absurdo", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "una {C:attention}#2#", + } + }, + j_cry_goldjoker = { + name = "Comodín de oro", + text = { + "Gana {C:money}#1#%{} de tu dinero", + "total al final de la ronda", + "El pago aumenta por {C:money}#2#%{}", + "cuando cada carta de {C:attention}oro{}", + "jugada puntúa", + }, + }, + j_cry_googol_play = { + name = "Carta de Googol Play", + text = { + "{C:green}#1# en #2#{} probabilidades de", + "{X:red,C:white} X#3# {} multi", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Crea un {C:attention}comodín{} aleatorio", + "al final de la ronda", + "Vende esta carta para", + "crear un {C:attention}comodín{} aleatorio", + "{C:inactive}(debe haber espacio){}", + }, + }, + j_cry_happyhouse = { + name = "Casa feliz", + text = { + "{X:dark_edition,C:white}^#1#{} multi sólo después de", + "jugar {C:attention}114{} manos{}", + "{C:inactive}(Actual: #2#/114){}", + "{C:inactive,s:0.8}¡No hay un lugar como el hugar!{}", + }, + }, + j_cry_home = { + name = "El hogar", + text={ + "{X:mult,C:white} X#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consum-ible", + text = { + "Gana {C:money}$#1#{} cuando", + "usas un {C:attention}consumible{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Reactiva todas las cartas jugadas", + "{C:attention}#2#{} vez,", + "cada carta jugada otorga", + "{X:mult,C:white} X#1# {} multi cuando puntúa", + }, + }, + j_cry_jawbreaker = { + name = "Jawbreaker", + text = { + "Al derrotar la {C:attention}ciega jefe,", + "{C:attention}duplica{} los valores de los comodines adyacentes", + "{E:2,C:red}se autodestruye{}", + } + }, + j_cry_jimball = { + name = "Jimball", + text = { + "Este comodín gana {X:mult,C:white} X#1# {} multi", + "por cada mano jugada {C:attention}consecutiva{}", + "mientras juegas tu", + "{C:attention}mano de póker{} más jugada", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_jollysus = { + name = "¿Comodín contento?", + text = { + "Crea un comodín {C:dark_edition}contento{}", + "cuando un comodín es {C:attention}vendido{}", + "{C:red}Funciona una vez por ronda{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Parece legítimo...{}", + }, + }, + j_cry_kidnap = { + name = "Secuestro", + text = { + "Gana {C:money}$#2#{} al final de la ronda", + "Aumenta el pago por {C:money}$#1#{}", + "cuando un comodín de {C:attention}tipo multi{}", + "o {C:attention}tipo fichas{} es vendido", + }, + }, + j_cry_kooky = { + name = "Comodín raro", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_krustytheclown = { + name = "Krusty el Payaso", + text = { + "Este comodín gana", + "{X:mult,C:white} X#1# {} multi cuando", + "cada {C:attention}carta{} jugada puntúa", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_kscope = { + name = "Caleidoscopio", + text = { + "Agrega {C:dark_edition}policromía{} a", + "un {C:attention}comodín{} aleatorio al", + "derrotar la {C:attention}ciega jefe{}", + }, + }, + j_cry_lightupthenight = { + name = "Ilumina la noche", + text = { + "Cada {C:attention}7{} o {C:attention}2{} jugado", + "otorga {X:mult,C:white}X#1#{} multi cuando puntúa", + }, + }, + j_cry_longboi = { + name = "Monstruo", + text = { + "Otorga futuras copias de", + "este comodín {X:mult,C:white}X#1#{} multi", + "al final de la ronda", + "{C:inactive}(Actual: {X:mult,C:white}X#2#{C:inactive} multi){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Reactiva{} todos los comodines", + "una vez por cada {C:attention}comodín{}", + "{C:attention}contento{} vendido esta ronda", + "{C:inactive}(Actual:{}{C:attention:} #1#{}{C:inactive} reactivaciones){}", + "{C:inactive,s:0.8}No había suficiente espacio...{}", + }, + }, + j_cry_lucky_joker = { + name = "Comodín de la suerte", + text = { + "Gana {C:money}$#1#{} cada vez que", + "una carta {C:attention}de la suerte{} se active", + "{C:green}con éxito{}", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "Todos los comodines otorgan", + "{X:chips,C:white} X#1# {} fichas", + }, + }, + j_cry_m = { + name = "m", + text = { + "Este comodín gana {X:mult,C:white} X#1# {} multi", + "cuando un {C:attention}Comodín contento{} es vendido", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Crea un {C:attention}Comodín contento{} ", + "{C:dark_edition}negativo{} cuando se", + "selecciona una {C:attention}ciega{}", + }, + }, + j_cry_macabre = { + name = "Comodín macabro", + text = { + "Al seleccionar una {C:attention}ciega{},", + "destruye todos los {C:attention}comodines{} excepto", + "{C:legendary}comodines M{} y {C:attention}comodines contentos{}", + "y crea 1 {C:attention}comodín contento{}", + "por cada carta destruida", + }, + }, + j_cry_magnet = { + name = "Imán de refrigerador", + text = { + "Gana {C:money}$#1#{} al final de la ronda", + "Obtienes {X:money,C:white} X#2# {} si hay", + "{C:attention}#3#{} o menos {C:attention}comodines{}", + }, + }, + j_cry_manic = { + name = "Comodín maníaco", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "una {C:attention}#2#", + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Reactiva todos los comodines", + "{C:attention}#1#{} veces adicionales", + }, + }, + j_cry_mask = { + name = "Máscara", + text = { + "Reactiva todas las cartas de {C:attention}figura{}", + "{C:attention}#1#{} veces adicionales", + "Todas las cartas {C:attention}numéricas{} se debilitan", + }, + }, + + j_cry_maximized = { + name = "Maximizado", + text = { + "Todas las cartas de {C:attention}figura{}", + "son consideradas {C:attention}Reyes{},", + "todas las cartas de {C:attention}número{}", + "son consideradas {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Laberinto", + text = { + "Todas las manos son consideradas la", + "{C:attention}primera mano{} de cada ronda,", + "todos los descartes son considerados el", + "{C:attention}primer descarte{} de cada ronda", + }, + }, + j_cry_Megg = { + name = "Muevo", + text = { + "Vende esta carta para crear", + "{C:attention}#2#{} Comodines contentos, aumenta", + "por {C:attention}#1#{} al final de la ronda", + }, + }, + j_cry_mellowcreme = { + name = "Mellowcreme", + text = { + "Vende esta carta para {C:attention}multiplicar", + "el valor de venta de todos los", + "{C:attention}consumibles{} por {X:attention,C:white}X#1#" + } + }, + j_cry_membershipcard = { + name = "Carta de afiliación", + text = { + "{X:mult,C:white}X#1#{} multi por cada miembro", + "en el {C:attention}Discord de Cryptid{}", + "{C:inactive}(Actual: {X:mult,C:white}X#2#{C:inactive} multi)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Carta de miembro antigua", --renamed it for making it distinct + text = { + "{C:chips}+#1#{} fichas por cada miembro", + "en el {C:attention}Discord de Cryptid{}", + "{C:inactive}(Actual: {C:chips}+#2#{C:inactive} fichas)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Lluvia de meteoros", + text = { + "Las cartas {C:dark_edition}laminadas{}", + "otorgan {C:chips}+#1#{} fichas", + }, + }, + j_cry_mneon = { + name = "M neón", + text = { + "Gana {C:money}$#2#{} al final de la ronda", + "Aumenta el pago por", + "{C:money}$#1#{} por cada {C:attention}Comodín contento{}", + "o {C:legendary}comodín M{} al", + "final de la ronda", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "Este comodín consigue {X:mult,C:white}X#1#{} multi", + "si no se usaron {C:attention}descartes{}", + "en esta ronda", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_monkey_dagger = { + name = "Daga de mono", + text = { + "Cuando se selecciona la {C:attention}ciega{},", + "destruye al comodín de la izquierda", + "y agrega para siempre {C:attention}10 veces", + "de valor de venta a estas {C:chips}fichas{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_monopoly_money = { + name = "Dinero de monopolio", + text = { + "{C:green}#1# en #2#{} probabilidades para", + "{C:attention}destruir{} objetos comprados", + "Divide tu dinero en 2 cuando se {C:attention}vende", + } + }, + j_cry_morse = { + name = "Código morse", + text = { + "Gana {C:money}$#2#{} al final de la ronda", + "Aumenta el pago por {C:money}$#1#{} al", + "vender una carta con una {C:attention}edición{}", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Crea un {C:legendary}comodín M{} al final de la ronda", + "Cada {C:attention}Comodín contento{} o {C:legendary}comodín M", + "otorga {X:dark_edition,C:white}^#1#{} multi", + "Aumenta el valor por {X:dark_edition,C:white}^#2#{}", + "cuando un {C:attention}Comodín contento{} es {C:attention}vendido", + "{C:inactive,s:0.8}(Tredecim excluido)", + }, + }, + j_cry_mstack = { + name = "Pila de Ms", + text = { + "Reactiva todas las cartas jugadas", + "una vez por cada", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}comodines contentos{} vendidos", + "{C:inactive}(Actual:{}{C:attention:} #1#{}{C:inactive} reactivaciones){}", + }, + }, + j_cry_multjoker = { + name = "Comodín multi", + text = { + "{C:green}#1# en #2#{} probabilidades por cada", + "carta {C:attention}multi{} jugada para que cree", + "una carta {C:spectral}Críptido{} al anotar", + "{C:inactive}(Debe haber espacio)", + }, + }, + j_cry_necromancer = { + name = "Nigromante", + text = { + "Cuando un comodín es {C:attention}vendido{} por un precio mayor que {C:attention}$0{},", + "gana un comodín {C:attention}aleatorio{} {C:attention}vendido{} en esta partida", + "y establece su {C:attention}valor de venta{} a {C:attention}$0{}", + }, + }, + j_cry_negative = { + name = "Comodín negativo", + text = { + "{C:dark_edition}+#1#{} ranuras de {C:attention}comodín", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} fichas si la mano jugada", + "contiene un {C:attention}6{} y un {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Noche", + text = { + "{X:dark_edition,C:white}^#1#{} multi en la", + "última mano de la ronda", + "{E:2,C:red}se autodestruye{} en", + "la última mano de la ronda", + }, + }, + j_cry_nosound = { + name = "Sin sonido, sin memoria", + text = { + "Reactiva cada {C:attention}7{} jugado", + "{C:attention:}#1#{} veces adicionales", + }, + }, + j_cry_notebook = { + name = "Cuaderno", + text = { + "{C:green} #1# en #2#{} probabilidades para conseguir {C:dark_edition}+1{} ranura", + "de comodín por cada {C:attention}renovación{} en la tienda", + "{C:green}Siempre se activa{} si hay", + "{C:attention}#5#{} o más {C:attention}Comodines contentos{}", + "{C:red}Funciona una vez por ronda{}", + "{C:inactive}(Actual: {C:dark_edition}+#3#{}{C:inactive} y #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Bloques numerados", + text = { + "Gana {C:money}$#1#{} al final de la ronda", + "Aumenta el pago por {C:money}$#2#{}", + "por cada {C:attention}#3#{} en tu mano,", + "la categoría cambia cada ronda", + }, + }, + j_cry_nuts = { + name = "El loco", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "una {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Comodín loco", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_oil_lamp = { + name = "Lámpara de aceite", + text = { + "Al final de la ronda, aumenta los valores", + "del comodín a la {C:attention}derecha{} por {X:attention,C:white}X#1#{}" + }, + }, + + j_cry_oldblueprint = { + name = "Plano viejo", + text = { + "Copia la habilidad del", + "{C:attention}comodín{} de la derecha", + "{C:green}#1# en #2#{} probabilidades", + "de que la carta se destruya", + "al final de la ronda", + }, + }, + j_cry_oldcandy = { + name = "Dulce nostálgico", + text = { + "Vende esta carta para ganar", + "{C:attention}+#1#{} tamaño de mano", + "de forma permanente", + }, + }, + j_cry_oldinvisible = { + name = "Comodín invisible nostálgico", + text = { + "{C:attention}Duplioca{} un {C:attention}comodín{}", + "aleatorio cada {C:attention}4", + "cartas de comodín vendidas", + "{s:0.8}Comodín invisible nostálgico excluido{}", + "{C:inactive}(Actual: #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panóptico", + text = { + "Todas las manos son consideradas la", + "{C:attention}última mano{} de cada ronda", -- +4 dabloons + }, + }, + j_cry_penetrating = { + name = "Comodín penetrante", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_pickle = { --holy shit dethklok reference + name = "Pepinillo", + text = { + "Al saltar una {C:attention}ciega{}, crea", + "{C:attention}#1#{} etiquetas, disminuye por", + "{C:red}#2#{} cuando se selecciona una {C:attention}ciega{}", + }, + }, + j_cry_pirate_dagger = { + name = "Daga de pirata", + text = { + "Cuando se selecciona la {C:attention}ciega{},", + "destruye al comodín de la derecha", + "y agraga {C:attention}un cuarto{} del", + "valor de venta a {X:chips,C:white} Xfichas {}", + "{C:inactive}(Actual: {X:chips,C:white} X#1# {C:inactive} fichas)", + }, + }, + j_cry_pity_prize = { + name = "Premio de compasión", + text = { + "Al saltar un {C:attention}paquete potenciador{},", + "gana una {C:attention}etiqueta{} al azar" + }, + }, + + j_cry_pot_of_jokes = { + name = "Olla de las bromas", + text = { + "{C:attention}#1#{} tamaño de mano,", + "aumenta por", + "{C:blue}#2#{} cada runda", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "Este comodín gana {X:dark_edition,C:white} ^#1# {} multi", + "si todas las cartas en la mano jugada son", + "{C:attention}Ases{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, o {C:attention}7s{}", + "{C:inactive}(Actual: {X:dark_edition,C:white} ^#2# {C:inactive} multi)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "Este comodín gana", + "{X:mult,C:white} X#1# {} multi cuando una", + "carta de {C:cry_code}código{} se usa", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_queens_gambit = { + name = "Gambito de dama", + text = { + "Si {C:attention}la mano de póker{} es una", + "{C:attention}Escalera real{}, destruye la", + "{C:attention}reina{} puntuada y crea un", + "{C:attention}comodín{} {C:red}raro{} {C:dark_edition}negativo{}", + }, + }, + j_cry_quintet = { + name = "El quinteto", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Bloon rojo", + text = { + "Gana {C:money}$#1#{} en {C:attention}#2#{} ronda(s)", + "{C:red,E:2}se autodestruye{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} apuesta al gastar", + "{C:money}$#2#{} {C:inactive}($#3#){}", + "{s:0.8}Requisitos aumentan", + "{C:attention,s:0.8}exponencialmente{s:0.8} por cada uso", + "{C:money,s:0.8}Siguiente aumento: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "Cuando un {C:attention}comodín{} es vendido,", + "añade sus efectos a", + "todos los otros comodines", + "{C:inactive,s:0.8}No afecta a otros Rescribere{}" + } + }, + j_cry_reverse = { + name = "Carta de reversa", + text = { + "Rellena todos las ranuras de comodín vacías {C:inactive}(Max 100){}", + "con {C:attention}Comodines contentos{} {C:dark_edition}holográficos{} si", + "la {C:attention}mano de póker descartada{} es una {C:attention}#1#{}", + "{C:red,E:2}se autodestruye{}", + "{C:inactive,s:0.8}¡El regreso SUPREMO!{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Aleatoriza abilidades por cada {C:attention}apuesta{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrificio", + text = { + "Crea un comodín {C:green}inusual{}", + "y 3 {C:attention}Comodines contentos{} al", + "usar una carta {C:spectral}espectral{}", + "{C:red}Funciona una vez por ronda{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Brote", + text = { + "Después de puntuar {C:attention}#2#{} {C:inactive}[#1#]{} cartas", + "mejoradas, vende esta carta para", + "crear un {C:attention}comodín{} {C:cry_epic}épico{} ", + "{C:inactive,s:0.8}Creará un {C:attention,s:0.8}comodín{} {C:red,s:0.8}raro{} si los", + "{C:inactive,s:0.8}comodines {C:cry_epic,s:0.8}épicos{} {C:inactive,s:0.8}están desactivados{}", + }, + }, + j_cry_savvy = { + name = "Comodín inteligente", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Los {C:attention}comodines{} escalan", + "como un polinomio grado-{C:attention}#1#{},", + "aumenta el grado por {C:attention}#2#{}", + "al final de la ronda", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluido)", + }, + }, + j_cry_scrabble = { + name = "Teja de Scrabble", + text = { + "{C:green}#1# en #2#{} probabilidades de crear", + "un comodín {C:green}inusual{} {C:dark_edition}contento", + "al jugar una mano", + }, + }, + j_cry_seal_the_deal = { + name = "Sellar el acuerdo", + text = { + "Añade un {C:attention}sello al azar{} a cada carta", + "puntuada en la {C:attention}última mano{} de la ronda", + }, + }, + j_cry_shrewd = { + name = "Comodín perspicaz", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_silly = { + name = "Comodín bobo", + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_smallestm = { + name = "Diminuto", + text = { + "Crea una {C:cry_jolly}Etiqueta MM", + "si la {C:attention}mano de póker{} jugada", + "es una {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so básicamente soy muy peque", + }, + }, + j_cry_soccer = { + name = "Uno para todos", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} ranura de comodín", + "{C:attention}+#1#{} ranura de paquete potenciador", + "{C:attention}+#1#{} tamaño de mano", + "{C:attention}+#1#{} ranura de consumible", + "{C:attention}+#1#{} ranura de carta en la tienda", + }, + }, + j_cry_fleshpanopticon = { + name = "Panóptico de carne", + text = { + "{C:red}X#1#{} tamaño de {C:attention}ciegas jefe{}", + "Al derrotar una {C:attention}ciega jefe{},", + "{C:red}se autodestruye{}, y crea", + "una carta de {C:spectral}Portal{} {C:dark_edition}negativa{}", + "{C:inactive,s:0.8}\"Esta prisión... para mantenerme...?\"" + }, + }, + + j_cry_spaceglobe = { + name = "Esfera celestial", + text = { + "Este comodín gana {X:chips,C:white}X#2#{} fichas", + "si la {C:attention}mano de póker{} es un(a) {C:attention}#3#{},", + "mano cambia después de aumentar{}", + "{C:inactive}(Actual:{} {X:chips,C:white}X#1#{} {C:inactive}fichas){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Crea una copia {C:dark_edition}negativa{}", + "de un {C:attention}comodín{} aleatorio", + "al final de la {C:attention}tienda", + "{C:inactive,s:0.8}No copia otros Speculo{}", + }, + }, + j_cry_spy = { + name = "Espía", + text = { + "{X:mult,C:white} X#2# {} multi, {C:dark_edition}+1{} ranura de {C:attention}comodín{}", + "{C:inactive}¡Ese #1# es un espía!", + }, + }, + j_cry_stardust = { + name = "Polvo de estrellas", + text = { + "Las cartas {C:dark_edition}policromas{}", + "otorgan {X:mult,C:white}X#1#{} multi", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "Este comodín destruye una", + "carta de {C:planet}planeta{} al azar", + "y gana {X:dark_edition,C:white} ^#1# {} multi", + "al final de la {C:attention}tienda{}", + "{C:inactive}(Actual: {X:dark_edition,C:white} ^#2# {C:inactive} multi)", + }, + }, + j_cry_stronghold = { + name = "La fortaleza", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + + j_cry_subtle = { + name = "Comodín sutil", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} fichas, {C:mult}+#1#{} multi,", + "{X:chips,C:white}X#2#{} fichas, {X:mult,C:white}X#2#{} multi", + "Gana {C:money}$#3#{} al", + "final de la ronda", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "Al final de la ronda, crea", + "una {C:attention}copia{} de una carta", + "aleatoria {C:attention}en tu mano{},", + "destruye las demás", + "{C:attention,s:0.8}Reyes{s:0.8} de {C:hearts,s:0.8}corazones{s:0.8} son priorizados", + }, + }, + j_cry_swarm = { + name = "El enjambre", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Catalizador de sincronización", + text = { + "Equilibra las {C:blue}fichas{} y el {C:red}multi{}", + "{C:inactive,s:0.8}¡Hey! ¡Yo he visto esto antes!", + }, + }, + j_cry_tax_fraud = { + name = "Evasión fiscal", + text = { + "Gana {C:attention}$#1#{} por cada comodín {C:attention}de alquiler", + "al final de la ronda", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{} ranuras de {C:attention}comodín{}", + "Gana {C:money}$#2#{} al final de la ronda", + }, + }, + j_cry_translucent = { + name = "Comodín translúcido", + text = { + "Vende esta carta para crear", + "una copia {C:attention}Banana Perecedera{}", + "de un {C:attention}comodín{} aleatorio", + "{s:0.8,C:inactive}(La copia evita compat. de perecedero)", + }, + }, + j_cry_treacherous = { + name = "Comodín traicionero", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_trick_or_treat = { + name = "Dulce o truco", + text = { + "Al {C:attention}venderse{}:", + "{C:green}#1# en #2#{} probabilidades de crear {C:attention}2{} {C:cry_candy}dulces", + "Si no, crea un comodín {X:cry_cursed,C:white}maldito{}", + "{C:inactive}(puede desbordarse)" + } + }, + j_cry_tricksy = { + name = "Comodín tramposo", + text = { + "{C:chips}+#1#{} fichas", + "si la mano contiene", + "una {C:attention}#2#", + } + }, + j_cry_triplet_rhythm = { + name = "Ritmo triple", + text = { + "{X:mult,C:white} X#1# {} multi si la mano", + "contiene {C:attention}exactamente{} tres {C:attention}3s", + }, + }, + j_cry_tropical_smoothie = { + name = "Zalamero tropical", + text = { + "Vende esta carta para", + "{C:attention}multiplicar{} los valores de los", + "comodines conseguidos por {X:attention,C:white}X1.5{}", + }, + }, + + j_cry_unity = { + name = "La unidad", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + j_cry_universe = { + name = "Universo", + text = { + "Las cartas {C:dark_edition}astrales{}", + "otorgan {X:dark_edition,C:white}^#1#{} multi", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "Las {C:attention}manos de póker{} ganan", + "{X:red,C:white} X#1# {} multi y {X:blue,C:white} X#1# {} fichas", + "al subir de nivel", + }, + }, + j_cry_unjust_dagger = { + name = "Daga injusta", + text = { + "Cuando se selecciona la {C:attention}ciega{},", + "destruye al comodín de la izquierda", + "y agraga {C:attention}un quinto{} del", + "valor de venta a {X:mult,C:white} Xmulti {}", + "{C:inactive}(Actual: {X:mult,C:white} X#1# {C:inactive} multi)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "Cuando cualquier probabilidad", + "es activada {C:green}con éxito{},", + "este comodín gana {X:red,C:white}Xmulti{}", + "igual a sus {C:attention}probabilidades{} listadas", + "{C:inactive}(Actual: {X:mult,C:white} X#1# {C:inactive} multi)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "Este comodín gana {C:money}$#1#{} de {C:attention}valor de venta{}", + "si la {C:attention}mano de póker{} contiene una {C:attention}#2#{}", + "Vende esta carta para crear un", + "{C:attention}comodín contento{} {C:dark_edition}policroma{} por", + "cada {C:money}$4{} de {C:attention}valor de venta{} {C:inactive}(min. 1){}", + }, + }, + j_cry_wacky = { + name = "Comodín chalado", --too many freaking name collisions + text = { + "{C:red}+#1#{} multi", + "si la mano contiene", + "un {C:attention}#2#", + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "Todos los comodines otorgan", + "{X:mult,C:white} X#1# {} multi", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "Todos los comodines otorgan", + "{C:money}$#1#{} al activarse", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "Este comodín gana", + "{C:mult}+#2#{} multi cuando cada", + "{C:attention}As{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, o {C:attention}8{}", + "jugado puntúa", + "{C:inactive}(Actual: {C:mult}+#1#{C:inactive} multi)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Reactica cada {C:attention}2{} jugado", --wee gaming + "{C:attention:}#1#{} veces adicionales", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", --wee gaming :) + }, + }, + j_cry_wheelhope = { + name = "La rueda de la esperanza", + text = { + "Este comodín gana", + "{X:mult,C:white} X#1# {} multi al fallar", + "una {C:attention}Rueda de la fortuna{}", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_whip = { + name = "El LÁTIGO", + text = { + "Este comodín gana {X:mult,C:white}X#1#{} multi", + "si la {C:attention}mano jugada{} contiene un", + "{C:attention}2{} y {C:attention}7{} de diferentes palos", + "{C:inactive}(Actual: {X:mult,C:white} X#2# {C:inactive} multi)", + }, + }, + j_cry_wrapped = { + name = "Dulce envuelto", + text = { + "Crea un {C:attention}comodín de comida{}", + "en {C:attention}#1#{} ronda(s)", + "{C:red,E:2}se autodestruye{}", + }, + }, + j_cry_wtf = { + name = "¡¿Qué rayos?!", + text = { + "{X:mult,C:white} X#1# {} multi", + "si la mano contiene", + "un {C:attention}#2#", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}nvl.#4#{})({V:2}nvl.#5#{})({V:3}nvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}nvl.#4#{})({V:2}nvl.#5#{})({V:3}nvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_Kaikki = { + name = "Kaikki", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Estrella de neutrones", + text = { + "Mejora una mano de póker", + "al azar por", + "{C:attention}1{} nivel por cada", + "{C:attention}estrella de neutrones{} usada", + "en esta partida", + "{C:inactive}(Actual:{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planeta.lua", + text = { + "{C:green}#1# en #2#{} probabilidades para", + "aumentar todas las", + "{C:legendary,E:1}manos de póker{}", + "por {C:attention}1{} nivel", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}nvl.#4#{})({V:2}nvl.#5#{})({V:3}nvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}nvl.#4#{})({V:2}nvl.#5#{})({V:3}nvl.#6#{})", + "Aumento de nivel", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "y {C:attention}#3#{}", + }, + }, + c_cry_marsmoons = { + name = 'Phobos y Deimos', + text = { + "{S:0.8}({S:0.8,V:1}nvl. #1#{S:0.8}){} Aumento de nivel", + "{C:attention}#2#", + "{C:mult}+#3#{} multi y", + "{C:chips}+#4#{} fichas", + } + }, + c_cry_void = { + name = 'Vacío', + text = { + "{S:0.8}({S:0.8,V:1}nvl. #1#{S:0.8}){} Aumento de nivel", + "{C:attention}#2#", + "{C:mult}+#3#{} multi y", + "{C:chips}+#4#{} fichas", + } + }, + c_cry_asteroidbelt = { + name = 'Cinturón de asteroides', + text = { + "{S:0.8}({S:0.8,V:1}nvl. #1#{S:0.8}){} Aumento de nivel", + "{C:attention}#2#", + "{C:mult}+#3#{} multi y", + "{C:chips}+#4#{} fichas", + } + }, + c_cry_universe = { + name = 'El universo en su puta totalidad', + text = { + "{S:0.8}({S:0.8,V:1}nvl. #1#{S:0.8}){} Aumento de nivel", + "{C:attention}#2#", + "{C:mult}+#3#{} multi y", + "{C:chips}+#4#{} fichas", + } + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "Manga CCD", + text = { + "Todas las cartas también son", + "un consumible {C:attention}aleatorio{}", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Manga transportadora", + text = { + "Los comodines {C:attention}no{} se pueden mover", + "Al principio de la runda,", + "{C:attention}duplica{} el comodín del extremo derecho", + "y {C:attention}destruye{} el comodín del extremo izquierdo", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Manga crítica", + text = { + "Después de cada mano jugada,", + "{C:green}#1# en 4{} probabilidades para {X:dark_edition,C:white} ^2 {} multi", + "{C:green}#1# en 8{} probabilidades para {X:dark_edition,C:white} ^0.5 {} multi", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Manga codificada", + text = { + "Comienza con un {C:cry_code,T:j_cry_CodeJoker}Comodín de código{}", + "y {C:cry_code,T:j_cry_copypaste}Copiar y pegar{}", + "Sólo aparecen {C:cry_code}cartas de código{} en la tienda", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Manga balanceada", + text = { + "Todas las cartas tienen la ", + "{C:attention}misma probabilidad{} de", + "aparecer en las tiendas,", + "comienza la partida con", + "{C:attention,T:v_overstock_plus}+2 ranuras de carta", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Baraja infinita", + text = { + "Puedes seleccionar {C:attention}cualquier", + "cantidad de cartas", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Manga de errata", + text = { + "Los valures de cartas", + "se {C:attention}aleatorizan", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Manga redimida", + text = { + "Cuando se compra un {C:attention}vale{},", + "obtén sus {C:attention}niveles extras", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Manga de agujero de gusano", + text = { + "Comienza con un comodín {C:cry_exotic}exótico{C:attention}", + "Los comodines son {C:attention}20X{} más", + "probables de ser {C:dark_edition}Negativos", + "{C:attention}-2{} ranuras de comodín", + }, + }, + sleeve_cry_legendary_sleeve = { + name = "Manga legendaria<", + text = { + "Comienza con un comodín {C:legendary}legendario{C:legendary}", + "{C:green}1 en 5{} probabilidades para crear otro", + "cuando se derrota a la ciega jefe", + "{C:inactive}(debe haber espacio){}", + }, + }, + }, + Spectral = { + c_cry_adversary = { + name = "Adversario", + text = { + "{C:red}Todos{} tus {C:attention}comodines{} se vuelven {C:dark_edition}negativos{},", + "{C:red}todos{} los {C:attention}comodines{} en la tienda cuestan", + "el {C:red}doble{} por el resto de la partida", + }, + }, + c_cry_analog = { + name = "Análogo", + text = { + "Crea {C:attention}#1#{} copias de un", + "{C:attention}comodín{} aleatorio, destruye", + "los demás, {C:attention}+#2#{} apuesta", + }, + }, + c_cry_chambered = { + name = "Recámara", + text = { + "Crea {C:attention}#1#{} copias", + "{C:dark_edition}negativas{} de un", + "consumible {C:attention}al azar{}", + "{C:inactive,s:0.8}No copia otros Recámara{}" + }, + }, + c_cry_conduit = { + name = "Conducto", + text = { + "Intercambia las {C:attention}ediciones{} de", + "{C:attention}2{} cartas o {C:attention}comodines{} seleccionados", + }, + }, + + c_cry_gateway = { + name = "Portal", + text = { + "Crea un {C:attention}comodín", + "{C:cry_exotic,E:1}exótico{}, destruye", + "los demás", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Aplica {C:attention}consumibles{} aleatorios", + "como si fueran {C:dark_edition}mejoras{}", + "a las cartas en la mano", + }, + }, + c_cry_lock = { + name = "Cerradura", + text = { + "Remueve {C:red}todas{} las pegatinas", + "de {C:red}todos{} los comodines,", + "y aplica {C:purple,E:1}Eterno{}", + "a un {C:attention}comodín{} aleatorio", + }, + }, + c_cry_pointer = { + name = "PUNTERO://", + text = { + "Crea una carta", + "de {C:cry_code}tu elección", + "{C:inactive,s:0.8}(Comodines exóticos #1#excluidos)", + }, + }, + c_cry_replica = { + name = "Réplica", + text = { + "Convierte todas las cartas", + "en tu mano", + "a una carta {C:attention}aleatoria{}", + "en tu mano", + }, + }, + c_cry_ritual = { + name = "Ritual", + text = { + "Aplica {C:dark_edition}Negativo{}, {C:dark_edition}Mosaico{},", + "o {C:dark_edition}Astral{} a {C:attention}#1#{}", + "carta de tu mano seleccionada", + }, + }, + c_cry_source = { + name = "Origen", + text = { + "Agrega un {C:cry_code}sello verde{}", + "a {C:attention}1{} carta seleccionada", + "de tu mano al azar", + }, + }, + c_cry_summoning = { + name = "Evocación", + text = { + "Crea un {C:joker}comodín{}", + "{C:cry_epic}épico{} aleatorio, destruye", + "un {C:joker}comodín{} al azar", + }, + }, + c_cry_trade = { + name = "Intercambio", + text = { + "{C:attention}Pierde{} un vale aleatorio,", + "gana {C:attention}2{} vales aleatorios", + }, + }, + c_cry_typhoon = { + name = "Tifón", + text = { + "Agrega un {C:cry_azure}sello azur{}", + "a {C:attention}1{} carta seleccionada", + "de tu mano al azar", + }, + }, + c_cry_vacuum = { + name = "Vacío", + text = { + "Remueve {C:red}todas{} las {C:green}modificaciones{}", + "de {C:red}todas{} las cartas en tu mano,", + "gana {C:money}$#1#{} por cada {C:green}modificación{} removida", + "{C:inactive,s:0.7}(ej. mejoras, sellos, ediciones)", + }, + }, + c_cry_white_hole = { + name = "Agujero blanco", + text = { + "{C:attention}Remueve{} todos los niveles de mano,", + "mejora la mano de póker {C:legendary,E:1}más jugada{}", + "por {C:attention}3{} por cada nivel removido", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pozo rosa", + colour = "rosa", --this is used for auto-generated sticker localization + text = { + "Escalas de puntos requeridas más rápidas", + "para cada {C:attention}apuesta inicial", + }, + }, + stake_cry_brown = { + name = "Pozo marrón", + colour = "marrón", + text = { + "Todas las {C:attention}pegatinas{} son", + "compatibles entre sí", + }, + }, + stake_cry_yellow = { + name = "Pozo amarillo", + colour = "amarilla", + text = { + "Las {C:attention}pegatinas{} pueden aparecer", + "en todos los objetos comprables", + }, + }, + stake_cry_jade = { + name = "Pozo jade", + colour = "jade", + text = { + "Las cartas pueden sacarse {C:attention}boca abajo{}", + }, + }, + stake_cry_cyan = { + name = "Pozo celeste", + colour = "celeste", + text = { + "Comodines {C:green}inusuales{} y {C:red}raros{} son", + "menos probables de aparecer", + }, + }, + stake_cry_gray = { + name = "Pozo gris", + colour = "gris", + text = { + "Las renovaciones aumentan por {C:attention}$2{} cada una", + }, + }, + stake_cry_crimson = { + name = "Pozo carmesí", + colour = "carmesí", + text = { + "Los vales reaparecen en apuestas {C:attention}pares{}", + }, + }, + stake_cry_diamond = { + name = "Pozo diamante", + colour = "diamante", + text = { + "Debes vencer la apuesta {C:attention}10{} para ganar", + }, + }, + stake_cry_amber = { + name = "Pozo ámbar", + colour = "ambar", + text = { + "{C:attention}-1{} ranuras de paquete potenciador", + }, + }, + stake_cry_bronze = { + name = "Pozo bronce", + colour = "bronce", + text = { + "Los vales son {C:attention}50%{} más caros", + }, + }, + stake_cry_quartz = { + name = "Pozo cuarzo", + colour = "cuarzo", + text = { + "Los comodines pueden ser {C:attention}Fijados{}", + "{s:0.8,C:inactive}(Se queda fijado al extremo izquierdo){}", + }, + }, + stake_cry_ruby = { + name = "Pozo rubí", + colour = "rubí", + text = { + "Ciegas {C:attention}grandes{} pueden ser", + "ciegas {C:attention}jefes{}", + }, + }, + stake_cry_glass = { + name = "Pozo de vidrio", + colour = "de vidrio", + text = { + "Las cartas pueden {C:attention}destruirse{} al puntuar", + }, + }, + stake_cry_sapphire = { + name = "Pozo safiro", + colour = "safiro", + text = { + "Pierde {C:attention}25%{} del dinero actual", + "al final de la apuesta", + "{s:0.8,C:inactive}(máx. $10){}", + }, + }, + stake_cry_emerald = { + name = "Pozo esmeralda", + colour = "esmeralda", + text = { + "Cartas, paquetes y vales", + "pueden estar {C:attention}boca abajo{}", + "{s:0.8,C:inactive}(No se pueden ver hasta ser comprados){}", + }, + }, + stake_cry_platinum = { + name = "Pozo platino", + colour = "platina", + text = { + "Las ciegas pequeñas son {C:attention}removidas{}", + }, + }, + stake_cry_twilight = { + name = "Pozo crepúsculo", + colour = "crepúsculo", + text = { + "Las cartas pueden ser {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 en 10 probabilidades de ser destruidas cada ronda){}", + }, + }, + stake_cry_verdant = { + name = "Pozo verdoso", + colour = "verdosa", + text = { + "Escalas de puntos requeridas más rápidas", + "para cada {C:attention}apuesta inicial", + }, + }, + stake_cry_ember = { + name = "Pozo ascua", + colour = "ascua", + text = { + "Todos los objetos no entregan dinero al venderse", + }, + }, + stake_cry_dawn = { + name = "Pozo alba", + colour = "alba", + text = { + "Cartas tarot y espectrales seleccionan {C:attention}1", + "carta menos", + "{s:0.8,C:inactive}(mín. 1){}", + }, + }, + stake_cry_horizon = { + name = "Pozo del horizonte", + colour = "del horizonte", + text = { + "Agrega una {C:attention}carta al azar{}", + "a tu baraja al", + "seleccionar una {C:attention}ciega{}", + }, + }, + stake_cry_blossom = { + name = "Pozo florido", + colour = "florida", + text = { + "Las ciegas {C:attention}finales{} pueden aparecer", + "en {C:attention}cualquier{} apuesta", + }, + }, + stake_cry_azure = { + name = "Pozo azur", + colour = "azur", + text = { + "Los valores en comodines se reducen", + "por {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Pozo ascendente", + colour = "ascendente", + text = { + "{C:attention}-1{} ranura de carta en la tienda", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Etiqueta astral", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Etiqueta banana", + text = { + "Crea {C:attention}#1#", + "{C:inactive}(Debe haber espacio){}", + }, + }, + tag_cry_bettertop_up = { + name = "Mejor etiqueta de recarga", + text = { + "Genera hasta {C:attention}#1#", + "comodines {C:green}inusuales{}", + "{C:inactive}(Debe haber espacio)", + }, + }, + tag_cry_better_voucher = { + name = "Etiqueta de vale dorado", + text = { + "Agrega un {C:voucher}vale{} de nivel {C:attention}#1#{}", + "en la siguiente tienda", + }, + }, + tag_cry_blur = { + name = "Etiqueta borrosa", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Borroso{}", + }, + }, + tag_cry_booster = { + name = "Etiqueta potenciadora", + text = { + "El siguiente {C:cry_code}paquete potenciador{} contiene", + "el {C:attention}doble{} de cartas y", + "el {C:attention}doble{} de opciones", + }, + }, + tag_cry_bundle = { + name = "Etiqueta de manojo", + text = { + "Crea una {C:attention}Etiqueta estándar{}, {C:tarot}Etiqueta encantada{},", + "{C:attention}Etiqueta de bufón{}, y {C:planet}Etiqueta de meteoro", + }, + }, + tag_cry_cat = { + name = "Etiqueta de gato", + text = { "miau :3", "{C:inactive}Nivel {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Etiqueta de consola", + text = { + "Otorga gratis un", + "{C:cry_code}paquete de programa", + }, + }, + tag_cry_double_m = { + name = "Etiqueta MM", + text = { + "En la tienda hay", + "un {C:legendary}comodín M{} {C:dark_edition}contento", + }, + }, + tag_cry_empowered = { + name = "Etiqueta empoderada", + text = { + "Otorga un {C:spectral}paquete espectral gratis", + "con {C:legendary,E:1}El alma{} y {C:cry_exotic,E:1}Portal{}", + }, + }, + tag_cry_epic = { + name = "Etiqueta épica", + text = { + "En la tienda hay un {C:cry_epic}comodín épico{}", + "a mitad de precio", + }, + }, + tag_cry_gambler = { + name = "Etiqueta del jugador", + text = { + "{C:green}#1# en #2#{} probabilidades de crear", + "una {C:cry_exotic,E:1}Etiqueta empoderada", + }, + }, + tag_cry_glass = { + name = "Etiqueta frágil", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Frágil{}", + }, + }, + tag_cry_glitched = { + name = "Etiqueta errónea", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Erróneo{}", + }, + }, + tag_cry_gold = { + name = "Etiqueta dorada", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Dorado{}", + }, + }, + tag_cry_gourmand = { + name = "Etiqueta gourmand", + text = { + "En la tienda hay un", + "{C:attention}comodín de comida{} gratis", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Otorga gratis", + "un {C:cry_ascendant}Paquete meme", + }, + }, + tag_cry_m = { + name = "Etiqueta contenta", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Contento{}", + }, + }, + tag_cry_memory = { + name = "Etiqueta de memoria", + text = { + "Crea {C:attention}#1#{} copias de", + "la última {C:attention}etiqueta{} usada", + "durante esta partida", + "{s:0.8,C:inactive}Etiquetas de copia excluidas", + "{s:0.8,C:inactive}Actual: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Etiqueta mosaico", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Mosaico{}", + }, + }, + tag_cry_oversat = { + name = "Etiqueta sobresaturada", + text = { + "El siguiente comodín de la tienda", + "de la edición base es gratuito", + "y se convierte en {C:dark_edition}Sobresaturado{}", + }, + }, + tag_cry_quadruple = { + name = "Etiqueta cuádruple", + text = { + "Otorga {C:attention}#1#{} copias de la", + "siguiente {C:attention}Tag{} seleccionada", + "{s:0.8,C:inactive}Etiquetas de copia excluidas", + }, + }, + tag_cry_quintuple = { + name = "Etiqueta quíntuple", + text = { + "Otorga {C:attention}#1#{} copias de la", + "siguiente {C:attention}Tag{} seleccionada", + "{s:0.8,C:inactive}Etiquetas de copia excluidas", + }, + }, + tag_cry_rework = { + name = "Etiqueta de retrabajo", + text = { + "La tienda tiene un", + "{C:cry_code}#2#{} {C:dark_edition}#1#", + }, + }, + tag_cry_schematic = { + name = "Etiqueta de esquemáticas", + text = { + "La tienda tiene una", + "{C:attention}Lluvia de ideas", + }, + }, + tag_cry_scope = { + name = "Etiqueta de alcance", + text = { + "{C:attention}+#1# {C:blue}manos{} y {C:red}descartes{}", + "en la siguiente ronda", + }, + }, + tag_cry_triple = { + name = "Etiqueta triple", + text = { + "Otorga {C:attention}#1#{} copias de la", + "siguiente {C:attention}Tag{} seleccionada", + "{s:0.8,C:inactive}Etiquetas de copia excluidas", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "El autómata", + text = { + "Genera hasta {C:attention}#1#", + "cartas de {C:cry_code}código{} al azar", + "{C:inactive}(Debe haber espacio)", + }, + }, + c_cry_eclipse = { + name = "El eclipse", + text = { + "Mejora {C:attention}#1#{} carta", + "seleccionada a", + "una {C:attention}Carta de eco", + }, + }, + c_cry_meld = { + name = "Fusionar", + text = { + "Selecciona un {C:attention}comodín{} o", + "{C:attention}carta de juego{} para", + "hacerla {C:dark_edition}Doble cara", + }, + }, + c_cry_theblessing = { + name = "La bendición", + text = { + "Crea {C:attention}1{}", + "{C:attention}consumible{} aleatorio", + "{C:inactive}(Debe haber espacio){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglífico", + text = { + "Establece la apuesta a {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Lienzo en blanco", + text = { + "{C:attention}+#1#{} tamaño de mano", + }, + }, + v_cry_clone_machine = { + name = "Máquina de clonar", + text = { + "Las etiqueta dobles se vuelven", + "{C:attention}Etiquetas quíntuples{} y", + "son {C:attention}4X{} más comunes", + }, + }, + v_cry_command_prompt = { + name = "Símbolo del sistema", + text = { + "Las cartas de {C:cry_code}código{}", + "pueden aparecer", + "en la {C:attention}tienda{}", + }, + }, + v_cry_copies = { + name = "Copias", + text = { + "Las etiqueta dobles se vuelven", + "{C:attention}Etiquetas triples{} y son", + "son {C:attention}2X{} más comunes", + }, + }, + v_cry_curate = { + name = "Cura", + text = { + "Todas las cartas", + "aparecen con", + "una {C:dark_edition}edición{}", + }, + }, + v_cry_dexterity = { + name = "Destreza", + text = { + "Gana para siempre", + "{C:blue}+#1#{} mano(s)", + "por ronda", + }, + }, + v_cry_double_down = { + name = "Doble de apuesta", + text = { + "Después de cada ronda,", + "{X:dark_edition,C:white} X1.5 {} a todos los valores", + "en la parte trasera de las", + "cartas {C:dark_edition}doble cara{}" + }, + }, + v_cry_double_slit = { + name = "Abertura doble", + text = { + "{C:attention}Fusionar{} puede aparecer", + "en la tienda y", + "paquetes arcanos", + }, + }, + v_cry_double_vision = { + name = "Doble visión", + text = { + "Cartas {C:dark_edition}doble cara{} aparecen", + "{C:attention}4X{} más frecuentemente", + }, + }, + v_cry_fabric = { + name = "Fábrica universal", + text = { + "{C:dark_edition}+#1#{} ranura(s) de comodín", + }, + }, + v_cry_massproduct = { + name = "Producción en masa", + text = { + "Todas las cartas y paquetes", + "en la tienca cuestan {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Aumenta el límite", + "del interés obtenido en cada ronda", + "hasta {C:money}#1#${}", + }, + }, + v_cry_overstock_multi = { + name = "Multicapital", + text = { + "{C:attention}+#1#{} ranura(s) de carta y", + "{C:attention}+#1#{} ranura(s) de paquetes potenciadores", + "disponibles en la tienda", + }, + }, + v_cry_pacclimator = { + name = "Aclamador de planetas", + text = { + "Las cartas de {C:planet}planeta{} aparecen", + "{C:attention}#1# X{} veces más seguido", + "en la tienda", + "Todas las cartas de {C:planet}planeta{}", + "futuras son {C:green}gratis{}", + }, + }, + v_cry_pairamount_plus = { + name = "Parejamiento plus", --this is the best translation you get + text = { + "{C:attention}Reactiva{} todos los comodines M", + "ona vez por cada Pareja", + "{C:attention}contenida{} en la mano jugada", + }, + }, + v_cry_pairing = { + name = "Parejamiento", + text = { + "{C:attention}Reactiva{} todos los comodines M", + "si la mano jugada es una {C:attention}Pareja", + }, + }, + v_cry_quantum_computing = { + name = "Computación cuántica", + text = { + "Las cartas de {C:cry_code}código{} pueden aparecer", + "con edición {C:dark_edition}negativa{}", + }, + }, + v_cry_repair_man = { + name = "Reparejador", --ditto + text = { + "{C:attention}Reactiva{} todos los comodines M", + "si la mano jugada contiene una {C:attention}Pareja", + }, + }, + v_cry_rerollexchange = { + name = "Intercambio de renovaciones", + text = { + "Todas las renovaciones", + "cuestan {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Enlace satelital", + text = { + "Las cartas de {C:cry_code}código{} pueden", + "aparecer en cualquier", + "{C:attention}paquete celestial", + }, + }, + v_cry_scope = { + name = "Alcance galáctico", + text = { + "Crea la carta de {C:planet}planeta", + "por la {C:attention}mano de póker{} jugada", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Aclamador de tarot", + text = { + "Las cartas de {C:tarot}tarot{} aparecen", + "{C:attention}#1# X{} veces más seguido", + "en la tienda", + "Todas las cartas de {C:tarot}tarot{}", + "futuras son {C:green}gratis{}", + }, + }, + v_cry_tag_printer = { + name = "Impresor de etiquetas", + text = { + "Las etiqueta dobles se vuelven", + "{C:attention}Etiquetas cuadrúples{} y son", + "son {C:attention}3X{} más comunes", + }, + }, + v_cry_threers = { + name = "Las 3 Rs", + text={ + "Consigue {C:red}+#1#{}", + "descartes en cada ronda", + "de forma permanente", + }, + }, + v_cry_stickyhand = { + name = "Mano pegajosa", + text = { + "{C:attention}+#1#{} límite de", + "selección de cartas", + }, + }, + v_cry_grapplinghook = { + name = "Gancho de agarre", + text = { + "{C:attention}+#1#{} límite de", + "selección de cartas", + "{C:inactive,s:0.7}Puedes hacer mucho más con esto de lo que tú crees.{}", + + }, + }, + v_cry_hyperspacetether = { + name = "Cuerda hiperespacial", + text = { + "{C:attention}+#1#{} límite de ", + "selección de cartas", + "{C:inactive,s:0.7}NOTA: Tendrá más funcionalidades después{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# en #2#{} probabilidades de ser", + "destruida en cada ronda", + }, + }, + cry_rigged = { + name = "Amañada", + text = { + "Todas las probabilidades {C:cry_code}enumeradas{}", + "son {C:cry_code}garantizadas", + }, + }, + cry_hooked = { + name = "Enganchada", + text = { + "Cuando este comodín se {C:cry_code}activa{},", + "activa {C:cry_code}#1#", + }, + }, + cry_flickering = { + name = "Parpadeante", + text = { + "Destruido después de", + "{C:attention}#1#{} activaciones", + "{C:inactive}({C:attention}#2#{C:inactive} restantes)" + }, + }, + cry_flickering_desc = { --used by choco dice + name = "Parpadeante", + text = { + "Destruido después de", + "{C:attention}#1#{} activaciones", + }, + }, + cry_possessed = { + name = "Poseída", + text = { + "{C:attention}Deshabilita{} e {C:attention}invierte{}", + "los efectos, si es posible", + "Se destruye junto con {C:attention}Fantasma" + }, + }, + food_jokers = { + name = "Comodines de comida", + text = { + "{s:0.8}Banano, Huevo, Helado, Cavendish, Habichuela negra,", + "{s:0.8}Cola sin azúcar, Palomitas de maíz, Ramen,", + "{s:0.8}Agua con gas, Pickle, Ají picante, Caramelo,", + "{s:0.8}Dulce nostálgico, M de comida rápida, etc.", + }, + }, + ev_cry_choco0 = { + name = "", + text = { + "Detalles de un {C:cry_ascendant,E:1}evento{}", + "activo aparecerán aquí" + } + }, + ev_cry_choco1 = { + name = "1: Posesión", + text = { + "{C:attention}Comodines{} y cartas de juego tienen", + "{C:green}1 en 3{} probabilidades of ser Parpadeantes", + "Crea un {C:attention}Fantasma", + "{C:inactive,s:0.7}Has sido poseído por un fantasma, y tu", + "{C:inactive,s:0.7}consciencia está parpadeando." + } + }, + ev_cry_choco2 = { + name = "2: Casa embrujada", + text = { + "No puedes saltar {C:attention}ciegas{}", + "Sólo una {C:attention}renovación{} permitida por tienda", + "Los precios de {C:attention}vales{} se duplican", + "{C:inactive,s:0.7}¡Los espíritus espeluznantes han tomado el control!", + "{C:inactive,s:0.7}¡No toques nada y sale lo más rápido que puedas!", + } + }, + ev_cry_choco3 = { + name = "3: Brebajes de bruja", + text = { + "Crea 3 {C:attention}Pociones", + "Usa una antes del final de la {C:attention}ciega pequeña{},", + "o {C:attention}todos{} los efectos malos se aplicarán en esta {C:attention}apuesta", + "{C:inactive,s:0.7}¡Has sido secuestrado por una bruja!", + "{C:inactive,s:0.7}Ella te ofrece 3 pociones, mirándote de cerca.", + "{C:inactive,s:0.7}Escoje una, para que ella no escoja por tí.", + } + }, + ev_cry_choco4 = { + name = "4: Abismo lunar", + text = { + "Las cartas jugadas tienen una {C:green}1 en 4{} probabilidades", + "de convertirse en una figura de {C:club}tréboles{} al azar", + "Divide {C:attention}multi{} por la cantidad de cartas de figura jugadas", + "{C:inactive,s:0.7}Hasta un hombre que es puro al corazón", + "{C:inactive,s:0.7}y dice sus oraciones a la noche..." + } + }, + ev_cry_choco5 = { + name = "5: Chupasangre", + text = { + "Remueve {C:attention}mejoras{} de todas las cartas jugadas", + "{C:green}1 en 3{} chance de destruir", + "cartas de {C:heart}corazones{} y {C:diamond}diamantes{}", + "{C:inactive,s:0.7}Ten cuidado en la oscuridad de la noche, por", + "{C:inactive,s:0.7,E:1}ellos en las sombras{C:inactive,s:0.7} buscan saciar su sed..." + } + }, + ev_cry_choco6 = { + name = "6: Por favor toma uno", + text = { + "Al {C:attention}terminar una ronda{}, abre un", + "paquete {C:attention}potenciador{} al azar", + "{C:inactive,s:0.7}Mientras paseas por las calles, ves una", + "{C:inactive,s:0.7}caja de varios paquetes potenciadores. ¡Mejor agarrar uno!" + } + }, + ev_cry_choco7 = { + name = "7: Ambiente festivo", + text = { + "Crea 3 {C:attention}Dulce o truco{} y 1 {C:attention}Cesto de dulce", + "Las tiendas tienen un {C:attention}Dulce o truco{} cada ronda", + "Los {C:cry_candy}dulces{} otorgan {C:money}$3{} al obtenerse", + "{C:inactive,s:0.7}Toda la vecindad está decorada por el empeño espeluznante,", + "{C:inactive,s:0.7}¡ven a disfrutar del ambiente festivo!" + } + }, + ev_cry_choco8 = { + name = "8: Lluvia de dulces", + text = { + "Al derrotar una {C:attention}ciega{}, consigue 1 {C:cry_candy}dulce{}", + "por cada mano restante; obtén un {C:attention}comodín de comida{}", + "cuando un {C:cry_candy}dulce{} es generado", + "{C:inactive,s:0.7}¡Los dulces llueven del cielo! Rápido,", + "{C:inactive,s:0.7,E:1}¡agarra lo más que puedas!" + } + }, + ev_cry_choco9 = { + name = "9: Riquezas fantasmales", + text = { + "Gana {C:money}$20", + "Todo el {C:money}dinero{} conseguido {C:attention}duplicado", + "{C:inactive,s:0.7}¡El espectro de un pariente desaparecido tuyo", + "{C:inactive,s:0.7}te visita en el medio de la noche!", + "{C:inactive,s:0.7}Sin una palabra, colocan una bolsa de dinero en tus manos,", + "{C:inactive,s:0.7}sonríen cálidamente, y se despiden mientras se desvanecen en el aire.", + } + }, + ev_cry_choco10 = { + name = "10: Antigüedad venerada", + text = { + "Un {C:attention}comodín{} {C:legendary}legendario{} aparece", + "en la ranura de {C:attention}vales{} por {C:money}$50", + "Sólo comprable como el {C:attention}último{} objeto en la tienda", + "{C:inactive,s:0.7}Has atraído la atención del espíritu de una rélica,", + "{C:inactive,s:0.7}pero no va a ser fácil de calmar.", + } + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Actualizaciones{s:0.7} están deshabilitadas por defecto ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eterno", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Eternos{}", + }, + }, + cry_perishable_booster = { + name = "Perecedero", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Perecederos{}", + }, + }, + cry_rental_booster = { + name = "De alquiler", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Fijado", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Fijados{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "Todas las cartas en el paquete", + "son {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eterno", + text = { + "No puede ser intercambiado", + }, + }, + cry_perishable_voucher = { + name = "Perecedero", + text = { + "Se agota al cabo de ", + "{C:attention}#1#{} rondas", + "{C:inactive}({C:attention}#2#{C:inactive} restantes)", + }, + }, + cry_rental_voucher = { + name = "De alquiler", + text = { + "Pierdes {C:money}#1#${}", + "al final de la ronda", + }, + }, + cry_pinned_voucher = { + name = "Fijado", + text = { + "Se mantiene en la tienda", + "hasta que sea redimido", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# en #2#{} probabilidades de ser", + "perdido en cada ronda", + }, + }, + cry_perishable_consumeable = { + name = "Perecedero", + text = { + "Se agota al", + "final de la ronda", + }, + }, + cry_rental_consumeable = { + name = "De alquiler", + text = { + "Pierdes {C:money}#1#${}", + "al final de la ronda, y en uso", + }, + }, + cry_pinned_consumeable = { + name = "Fijado", + text = { + "No puedes usar consumibles", + "no-{C:attention}fijados{}", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# en #2#{} probabilidades de", + "no hacer nada en uso", + }, + }, + p_cry_code_normal_1 = { + name = "Paquete de programa", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas{} de {C:cry_code}código{}", + }, + }, + p_cry_code_normal_2 = { + name = "Paquete de programa", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas{} de {C:cry_code}código{}", + }, + }, + p_cry_code_jumbo_1 = { + name = "Paquete de programa jumbo", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas{} de {C:cry_code}código{}", + }, + }, + p_cry_code_mega_1 = { + name = "Paquete de programa mega", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas{} de {C:cry_code}código{}", + }, + }, + p_cry_empowered = { + name = "Paquete espectral [Etiqueta empoderada]", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas {C:spectral}espectrales{}", + "{s:0.8,C:inactive}(Generado por Etiqueta empoderada)", + }, + }, + p_cry_meme_1 = { + name = "Paquete meme", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas de {C:joker}comodín meme{}", + }, + }, + p_cry_meme_two = { + name = "Paquete meme", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas de {C:joker}comodín meme{}", + }, + }, + p_cry_meme_three = { + name = "Paquete meme", + text = { + "Elige {C:attention}#1#{} de hasta", + "{C:attention}#2# cartas de {C:joker}comodín meme{}", + }, + }, + undiscovered_code = { + name = "Por descubrir", + text = { + "Compra o usa", + "esta carta", + "en una partida sin códigos", + "para saber lo que hace", + }, + }, + undiscovered_unique = { + name = "Por descubrir", + text = { + "Compra o usa", + "esta carta", + "en una partida sin códigos", + "para saber lo que hace", + } + }, + cry_green_seal = { + name = "Sello verde", + text = { + "Crea una carta de {C:cry_code}código{}", + "cuando se juega y no puntúa", + "{C:inactive}(Debe haver espacio)", + }, + }, + cry_azure_seal = { + name = "Sello azur", + text = { + "Crea {C:attention}#1#{} {C:planet}planetas{}", + "{C:dark_edition}negativas{} por la {C:attention}mano de póker{}", + "jugada, y {C:red}destruye{} esta carta", + }, + }, + blurred_sdm0 = { + name = "a", + text = { + "{C:inactive,s:0.8}\"Odio esta carta\" - SDM_0, 2024{}", + }, + }, + + }, + Unique = { + c_cry_potion = { + name = "Poción", + text = { + "Aplica un {C:attention}efecto malo{}", + "desconocido al usarse", + "{C:inactive,s:0.7}Obtenido por Dado de chocolate" + } + } + } + }, + misc = { + poker_hands = { + ['cry_Bulwark'] = "Baluarte", + ['cry_Clusterfuck'] = "Lío de mierda", + ['cry_UltPair'] = "Pareja suprema", + ['cry_WholeDeck'] = "Toda la puta baraja", + }, + poker_hand_descriptions = { + ['cry_Bulwark'] = { + '5 cartas, sin categoría ni palo', + }, + ['cry_Clusterfuck'] = { + 'Al menos 8 cartas que no', + 'contengan una Pareja, Color o Escalera', + }, + ['cry_UltPair'] = { + 'Dos Doble pareja, donde cada', + 'Doble pareja es un palo único, por un total', + 'de 2 palos entre las dos', + }, + ['cry_WholeDeck'] = { + 'Una mano que contenga cada', + 'carta en una baraja de 52 cartas.', + '¿Estás loco?', + }, + }, + achievement_names = { + ach_cry_ace_in_crash = "Un ACE En Mi Bolsillo", + ach_cry_blurred_blurred_joker = "Legalmente Ciego", + ach_cry_bullet_hell = "Shooter Maníaco", + ach_cry_break_infinity = "Rompe El Infinito", + ach_cry_cryptid_the_cryptid = "Criptida El Críptido", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Pase De Googol Play", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Corredor De Bienes Raíces", + ach_cry_jokes_on_you = "La Broma Es Para Tí, Amigo!", + ach_cry_niw_uoy = "!ETSANAG¡", + ach_cry_now_the_fun_begins = "Ahora Se Pone Lo Bueno", + ach_cry_patience_virtue = "La Paciencia Es Una Virtud", + ach_cry_perfectly_balanced = "Perfectamente Balanceado", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Atasco De Tráfico", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "Te Dijimos Que No Lo Hagas", + ach_cry_what_have_you_done = "¡¿QUÉ ACABAS DE HACER?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Consigue Comodín Borroso borroso", + ach_cry_bullet_hell = "Consigue 15 comodines AP", + ach_cry_break_infinity = "Puntúa 1.79e308 fichas en una sola mano", + ach_cry_cryptid_the_cryptid = "Usa Críptido en Críptido", + ach_cry_exodia = "Consigue 5 comodines exóticos", + ach_cry_freak_house = "Juega un Full de color consistiendo de 6s y 9s de corazones mientras tienes a Nice", + ach_cry_googol_play_pass = "Amaña una Carta de Googol Play", + ach_cry_haxxor = "Usa un código de trampas", + ach_cry_home_realtor = "Activa Casa Feliz antes de la apuesta 8 (sin BdE/Antimateria)", + ach_cry_jokes_on_you = "Activa el efecto de La Broma en la apuesta 1 y gana la partida", + ach_cry_niw_uoy = "Llega a la apuesta -8", + ach_cry_now_the_fun_begins = "Consigue a Lienzo", + ach_cry_patience_virtue = "Espera a Ciclo Lavanda por 2 minutos antes de jugar la primera mano y vence la ciega", + ach_cry_perfectly_balanced = "Completa la Baraja Muy Justa en Ascendant Stake", + ach_cry_pull_request = "Haz que ://COMMIT haga aparecer el mismo comodín que destruyó", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Gana en 1 ronda", + ach_cry_used_crash = "Usa ://CHOQUE", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Características", + cry_set_music = "Música", + cry_set_enable_features = "Selecciona las características para activar (se aplica al reiniciar):", + cry_feat_achievements = "Logros", + ["cry_feat_antimatter deck"] = "Baraja de antimateria", + cry_feat_blinds = "Ciegas", + cry_feat_challenges = "Desafíos", + ["cry_feat_code cards"] = "Cartas de código", + ["cry_feat_misc. decks"] = "Barajas misceláneas", + ["cry_feat_https module"] = "Módulo HTTPS", + ["cry_feat_timer mechanics"] = "Mecánicas de temporizador", + ["cry_feat_enhanced decks"] = "Barajas mejoradas", + ["cry_feat_epic jokers"] = "Comodines épicos", + ["cry_feat_exotic jokers"] = "Comodines exóticos", + ["cry_feat_m jokers"] = "Comodines M", + cry_feat_menu = "Menú principal personalizado", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Comodines misceláneos", + cry_feat_planets = "Planetas", + cry_feat_jokerdisplay = "JokerDisplay (no hace nada)", + cry_feat_tags = "Etiquetas", + cry_feat_sleeves = "Mangas", + cry_feat_spectrals = "Espectrales", + cry_feat_spooky = "Actualización espeluznante", + ["cry_feat_more stakes"] = "Pozos", + cry_feat_vouchers = "Vales", + cry_mus_jimball = "Jimball (Funkytown por Lipps Inc. - Copyrighted)", + cry_mus_code = "Cartas de código (://LETS_BREAK_THE_GAME por HexaCryonic)", + cry_mus_exotic = "Comodines exóticos (Joker in Latin por AlexZGreat)", + cry_mus_high_score = "Alto puntaje (Final Boss [For Your Computer] por AlexZGreat)", + + k_cry_program_pack = "Paquete de programa", + k_cry_meme_pack = "Paquete meme", + + cry_critical_hit_ex = "¡Golpe crítico!", + cry_critical_miss_ex = "Fallo crítico...", + + cry_potion1 = "-1 a todos los niveles de mano", + cry_potion2 = "X1.15 tamaño de ciega", + cry_potion3 = "-1 mano y descarte", + + cry_debuff_oldhouse = "Sin fulls", + cry_debuff_oldarm = "Debes jugar 4 o menos cartas", + cry_debuff_oldpillar = "Sin escaleras", + cry_debuff_oldflint = "Sin colores", + cry_debuff_oldmark = "Sin manos que contengan una Pareja", + cry_debuff_obsidian_orb = "Aplica las habilidades de todos los jefes derrotados", + + k_code = "Código", + k_unique = "Único", + b_code_cards = "Cartas de código", + b_unique_cards = "Cartas únicas", + b_pull = "TIRAR", + cry_hooked_ex = "Enganchada!", + k_end_blind = "terminar_ciega", + + cry_code_rank = "INGRESAR CATEGORÍA", + cry_code_enh = "INGRESAR MEJORA", + cry_code_hand = "INGRESAR MANO DE PÓKER", + cry_code_enter_card = "INGRESAR CARTA", + cry_code_apply = "APLICAR", + cry_code_apply_previous = "APLICAR ANTERIOR", + cry_code_exploit = "EXPLOTAR", + cry_code_exploit_previous = "EXPLOTAR ANTERIOR", + cry_code_create = "CREAR", + cry_code_create_previous = "CREAR ANTERIOR", + cry_code_execute = "EJECUTAR", + cry_code_cancel = "CANCELAR", + + b_flip = "VOLTEAR", + b_merge = "COMBINAR", + + cry_hand_bulwark = "Baluarte", + cry_hand_clusterfuck = "Lío de mierda", + cry_hand_ultpair = "Pareja suprema", + + cry_again_q = "¿Otra vez?", + cry_curse = "Maldición", + cry_curse_ex = "¡Maldición!", + cry_sobbing = "Ayúdame...", + cry_gaming = "Gaming", + cry_gaming_ex = "¡Gaming!", + cry_good_luck_ex = "¡Buena suerte!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 ronda", + cry_plus_cryptid = "+1 críptido", + cry_no_triggers = "¡Sin activaciones restantes!", + cry_unredeemed = "Desredimido...", + cry_active = "Activo", + cry_inactive = "Inactivo", + + k_disable_music = "Desactivar música", + + k_cry_epic = "Épico", + k_cry_exotic = "Exótico", + k_cry_candy = "Dulce", + k_cry_cursed = "Maldito", + k_planet_disc = "Disco circunestelar", + k_planet_satellite = "Satélites naturales", + k_planet_universe = "El puto Universo Real", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Nota de copyright", + cry_notif_jimball_d1 = "Jimball reproduce la canción \"Funkytown\",", + cry_notif_jimball_d2 = "la cual tiene copyright y no puede ser", + cry_notif_jimball_d3 = "usada por streams y videos.", + }, + labels = { + food_jokers = "Comodines de comida", + banana = "Banana", + code = "Código", + unique = "Único", + cry_rigged = "Amañada", + cry_hooked = "Enganchada", + cry_flickering = "Parpadeante", + cry_possessed = "Poseída", + + cry_green_seal = "Sello verde", + cry_azure_seal = "Sello azur", + + cry_astral = "Astral", + cry_blur = "Borroso", + cry_double_sided = "Doble cara", + cry_glass = "Frágil", + cry_glitched = "Errónea", + cry_gold = "Dorada", + cry_m = "Contenta", + cry_mosaic = "Mosaico", + cry_noisy = "Ruidosa", + cry_oversat = "Sobresaturada", + + k_cry_epic = "Épico", + k_cry_exotic = "Exótico", + k_cry_candy = "Dulce", + k_cry_cursed = "Maldito", + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} multi" }, + plus_chips = { "{C:blue}+#2#{} fichas" }, + x_mult = { "{X:red,C:white} X#2#{} multi" }, + x_chips = { "{X:blue,C:white} X#2#{} fichas" }, + h_size = { "{C:attention}+#2#{} tamaño de mano" }, + money = { "{C:money}+$#2#{} al pago" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Actual: {C:red}+#1#{C:inactive} multi)" }, + plus_chips = { "{C:inactive}(Actual: {C:blue}+#1#{C:inactive} fichas)" }, + x_mult = { "{C:inactive}(Actual: {X:red,C:white} X#1# {C:inactive} multi)" }, + x_chips = { "{C:inactive}(Actual: {X:blue,C:white} X#1# {C:inactive} fichas)" }, + h_size = { "{C:inactive}(Actual: {C:attention}+#1#{C:inactive} tamaño de mano)" }, + money = { "{C:inactive}(Actual: {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Crea {C:attention}#2# comodín(es{}" }, + make_tarot = { "Crea {C:attention}#2#{} carta(s) de {C:tarot}tarot{}" }, + make_planet = { "Crea {C:attention}#2#{} carta(s) de {C:planet}planeta{}" }, + make_spectral = { "Crea {C:attention}#2#{} carta(s) {C:spectral}espectrales{}" }, + add_dollars = { "Gana {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "al abrir un {C:attention}Paquete potenciador{}" }, + buying_card = { "al comprar una carta" }, + selling_self = { "al vender esta carta" }, + selling_card = { "al vender una carta" }, + reroll_shop = { "al renovar" }, + ending_shop = { "al final de la {C:attention}tienda{}" }, + skip_blind = { "al saltar una {C:attention}ciega{}" }, + skipping_booster = { "al saltar un {C:attention}Paquete potenciador{}" }, + playing_card_added = { "al añadir una {C:attention}carta{} a tu baraja" }, + first_hand_drawn = { "al empezar la ronda" }, + setting_blind = { "al seleccionar una {C:attention}ciega{}" }, + remove_playing_cards = { "al destruir una carta" }, + using_consumeable = { "al usar un {C:attention}consumible{}" }, + debuffed_hand = { "si la {C:attention}mano{} no está permitida" }, + pre_discard = { "antes de descartar" }, + discard = { "por cada carta descartada" }, + end_of_round = { "al final de la {C:attention}ronda{}" }, + individual_play = { "por cada carta puntuada" }, + individual_hand_score = { "por cada carta en tu mano al puntuar" }, + individual_hand_end = { "por cada carta en tu mano al terminar la {C:attention}ronda{}" }, + repetition_play = { "Reactiva las cartas puntuadas" }, + repetition_hand = { "Reactiva las cartas en tu mano" }, + other_joker = { "por cada {C:attention}comodín{}" }, + before = { "antes de cada {C:attention}mano{}" }, + after = { "después de cada {C:attention}mano{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "si es un {C:attention}comodín{} {C:blue}común{}" }, + buy_uncommon = { "si es un {C:attention}comodín{} {C:green}inusual{}" }, + tarot = { "si la carta es una carta de {C:tarot}tarot{}" }, + planet = { "si la carta es una carta de {C:planet}planeta{}" }, + spectral = { "si la carta es una carta {C:spectral}espectral{}" }, + joker = { "si la carta es un {C:attention}comodín{}" }, + suit = { "si la carta es de {V:1}#3#{}" }, + rank = { "si la carta es un {C:attention}#3#{}" }, + face = { "si la carta es una carta de {C:attention}figura{}" }, + boss = { "si la {C:attention}ciega{} es una {C:attention}ciega jefe{}" }, + non_boss = { "si la {C:attention}ciega{} es una {C:attention}ciega no-jefe{}" }, + small = { "si la {C:attention}ciega{} es una {C:attention}ciega pequeña{}" }, + big = { "si la {C:attention}ciega{} es una {C:attention}ciega grande{}" }, + first = { "si es la {C:attention}primera mano{}" }, + last = { "si es la {C:attention}última mano{}" }, + common = { "si es un {C:attention}comodín{} {C:blue}común{}" }, + uncommon = { "si es un {C:attention}comodín{} {C:green}inusual{}" }, + rare = { "si es un {C:attention}comodín{} {C:red}raro{}" }, + poker_hand = { "si la mano es un(a) {C:attention}#3#{}" }, + or_more = { "si la mano contiene {C:attention}#3#{} o más cartas" }, + or_less = { "si la mano contiene {C:attention}#3#{} o menos cartas" }, + hands_left = { "si tienes #3# {C:blue}manos{} restantes al final de la ronda" }, + discards_left = { "si tienes #3# {C:red}descartes{} restantes al final de la ronda" }, + first_discard = { "si es el {C:attention}primer descarte{}" }, + last_discard = { "si es el {C:attention}último descarte{}" }, + odds = { "con {C:green}#4# {C:green}en {C:green}#3#{} probabilidades" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# fichas"}, + a_powmult = {"^#1# multi"}, + a_powchips = {"^#1# fichas"}, + a_powmultchips = {"^#1# multi+fichas"}, + a_round = {"+#1# ronda"}, + a_candy = {"+#1# Candy"}, + a_xchips_minus = {"-X#1# fichas"}, + a_powmult_minus = {"-^#1# multi"}, + a_powchips_minus = {"-^#1# fichas"}, + a_powmultchips_minus = {"-^#1# multi+fichas"}, + a_round_minus = {"-#1# ronda"}, + + a_tag = {"#1# etiqueta"}, + a_tags = {"#1# etiquetas"}, + + cry_sticker_name = {"Pegatina #1#"}, + cry_sticker_desc = { + "Usaste este comodín", + "para ganaer en la dificultad #2##1#", + "#2#Pozo#3#" + }, + + cry_art = {"Arte: #1#"}, + cry_code = {"Código: #1#"}, + cry_idea = {"Idea: #1#"} + + }, + v_text = { + ch_c_cry_all_perishable = {"Todos los comodines son {C:eternal}Perecederos{}"}, + ch_c_cry_all_rental = {"Todos los comodines son {C:eternal}de Alquiler{}"}, + ch_c_cry_all_pinned = {"Todos los comodines son {C:eternal}Fijados{}"}, + ch_c_cry_all_banana = {"Todos los comodines son {C:eternal}Banana{}"}, + ch_c_all_rnj = {"Todos los comodines son {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"Todos los objetos comprables tienen todas las pegatinas"}, + ch_c_cry_rush_hour = {"Todas las ciegas jefe son {C:attention}El reloj{} or {C:attention}Ciclo lavanda"}, + ch_c_cry_rush_hour_ii = {"Todas las ciegas son {C:attention}ciegas jefe{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}El reloj{} y {C:attention}Ciclo lavanda{} aumentan el {C:attention}doble{} de rápido"}, + ch_c_cry_no_tags = {"Saltar ciegas no está {C:attention}permitido{}"}, + ch_c_cry_no_vouchers = {"Los {C:attention}vales{} ya no aparecen en la tienda"}, + ch_c_cry_no_boosters = {"Los {C:attention}paquetes potenciadores{} ya no aparecen en la tienda"}, + ch_c_cry_no_rerolls = {"Las renovaciones no están {C:attention}permitidas{}"}, + ch_c_cry_no_consumables = {"Los {C:attention}consumibles{} ya no aparecen"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO HAY VALES", "PARA TÍ" }, + { "BOZO", "¿PENSASTE QUE TE", "DARÍA UN VALE?" }, + { "¡NO!", "¡NO HAY VALES AQUÍ!", "(EDICIÓN SLUMPAGE)" }, + { "SKILL ISSUE", "IMAGINA SER SUFICIENTE", "PARA UN VALE" }, + { "JIMBO", "DE LA GERENCIA", "OLVIDÓ REABASTECER" }, + { "¡UPS!", "SIN VALES", "" }, + { "BUFÓN,", "¿POR QUÉ MIRAS", "AQUÍ? LMAO" }, + { "EL VALE", "ESTÁ EN", "OTRO CASTILLO" }, + { "VALE EN BLANCO", "POR $0", "(¿LO PILLAS?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% DE DESCUENTO", "EN TODOS LOS VALES", "(ALGUIEN YA LOS COMPRÓ)" }, + { "INTENTA DESPUÉS", "PISTA: IGUAL NO TENDRÁS", "EL DINERO SUFICIENTE" }, + { "¿EH?", '¿"VALE"?', "ESO NI ES UNA PALABRA..." }, + { 'MANTÉN "R"', "PARA REABASTECER", "TODOS LOS VALES" }, + { "¿SABÍAS QUE?", "¡PRESIONANDO ALT+F4", "DA VALES GRATIS!" }, + { "PERDÓN,", "NO HAY VALES POR", "CORTES DE PRESUPUESTO" }, + { "LLAMA A 1-600-JIMBO", "PARA CALIFICAR TU", "EXPERIENCIA DE VALES" }, + { "DERROTA LA CIEGA", "JEFE DE APUESTA 39", "PARA RESTABLECER" }, + { "TRUCO DE MAGIA", "HICE ESTE VALE", "DESAPARECER" }, + { "¿POR QUÉ ES", "UN VALE COMO", "UN ESCRITORIO?" }, + { "HEMOS RETRAÍDO", "TUS VALES, SERÍAN MEJOR", "USADOS EN OTRAS PARTIDAS" }, + { "EL TRADUCTOR NI QUISO", "TRADUCIR ESTE MENSAJE", "NO HAY VALES PARA TI CABRÓN" }, + { "PERDÓN", "LOS VALES ESTÁN EXPERIMENTANDO", "VOUCHIFIA ABORTUS" }, + { "DESAFORTUNADAMENTE", "LA ACTUALIZACIÓN DE VALES", "HA SIDO CANCELADA" }, + { "DERROTA LA", "CIEGA JEFE PARA", "NO HACER NADA" }, + { "LAS AVES CANTAN", "LAS FLORES FLORECEN", "NIÑOS COMO TÚ..." }, + { "SENTIMOS DECIR QUE", "TODOS LOS VALES HAN SIDO DEVUELTOS", "POR EXPOSICIÓN A SALMONELLA" }, + { "LOS VALES NO PUDIERON LLEGAR POR", "LA DISPOSICIÓN DE LA TIENDA SIENDO", "200% SOBRE EL PRESUPUESTO" }, + { "TE GUSTA", "COMPRAR VALES, ¿NO?", "ERES UN COMPRADOR DE VALES" }, + { "VALES", "!E", "LISTA DE VALES" }, + { "NO", "HAY", "VALES" }, + { "NO EXISTE", "SANTA", "Y NO EXISTEN LOS VALES" }, + { "", "NO VALE", "" }, + { "ACABAS DE", "PERDER", "EL JUEGO" }, + { "¿PUEDO OFRECERTE", "UN BUEN HUEVO", "EN ESTOS TIEMPOS?" }, + { "VE A TOCAR PASTO", "EN VEZ DE USAR", "ESTA BARAJA" }, + { "PODRÍAS ESTAR", "JUGANDO EN LA BARAJA AZUL", "AHORA MISMO" }, + { "EXÓTICOS GRATIS", "CONSÍGUELOS ANTES DE QUE", "SEA DEMASIADO TARDE (se acabaron)" }, + { "PRUÉBALOS MAL", "COMPRA UN VALE", "INVISIBLE POR $10" }, + { "", "¿sin vales?", "" }, + { "¿ves este anuncio?", "si lo ves, está funcionando", "y lo puedes tener como tuyo" }, + { "TE ESTÁS PERDIENDO EN", "AL MENOS 5 VALES AHORA", "tonktonktonktonktonk" }, + { "10", "20 SIN VALES XD", "30 GOTO 10" }, + { "LOS VALES", "SON ARTÍCULOS PREMIUM", "$199.99 JÓLARES PARA DESBLOQUEAR" }, + { "¡¿SIN VALES?!", "SÓLO EN POZO ASCENDENTE", "BARAJA MUY JUSTA" }, + { "¿DISFRUTANDO TU", "EXPERIENCIA DE VALES? DÁNOS UNA CALIFICACIÓN", "DE CINCO ESTRELLAS EN JESTELP" }, + { "VALES GRATIS", "VALES CERCA DE TÍ", "CONSIGUE VALES RÁPIDO CON ESTE TRUCO" }, + { "INTRODUCIENDO", "¡EL PRIMER VALE NIVEL 0!", "(llegando pronto a Cryptid 1.0)" }, + { "¡UN VALE!", "ES SÓLO IMAGINARIO", "IMAGINAMOS QUE LO QUERÍAS, ESO SÍ" }, + { "DESACTIVA TU ADBLOCKER", "SIN ANUNCIOS, NO PODRÍAMOS", "VENDERTE NINGÚN VALE" }, + { "SI TIENES", "UN PROBLEMA CON ESTO", "ENVÍA UN CORREO A NORESPONSE@JMAIL.COM" }, + { "SIN DINERO SUFICIENTE", "PARA COMPRAR ESTE VALE", "¿PARA QUÉ PONERLO AQUÍ?" }, + { "¿QUIERES UN VALE?", "ENTONCES CÁLLATE", "NO PUEDES TENER NINGUNO LOL" }, + { "^$%& NINGÚN", "VALE ^%&% %&$^% PARA", "$%&%%$ %&$&*%$^ TÍ" }, + { "UN VALE (CONFÍA)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--- ..- --. .- -.. --- .-. -.. . ... -.-. .. ..-. .-. .- -.-. --- -.. .. --. ---", + "-- --- .-. ... . .--. .- .-. .- . -. -.-. --- -. - .-. .- .-. ...- .- .-.. .", + }, + { "RUN > NEW", "MIRAR A NADA", "POR UNA HORA O DOS" }, + { "LO SENTIMOS,", "EL ÚLTIMO CHICO COMPRÓ EN PÁNICO", "TODOS LOS VALES" }, + { "CÓMO SE SIENTE", "NO COMPRAR", "VALES" }, + { "JIMBO TUVO UN NAT 1", "Y TIRÓ TODOS LOS", "VALES EN UNA ZANJA" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "AH, ¿DE VERAS PENSASTE QUE LEYENDO TODAS ESTAS LÍNEAS TE DEVOLVERÍA TUS VALES?", + "PERDÓN POR DECÍRTELO, PERO ESTA BARAJA NO CONTIENE QUE TÚ BUSCAS.", + "ESTE TEXTO ANORMALMENTE LARGO ESTÁ AQUÍ Y ESTÁ DISEÑADO PARA GASTAR TU TIEMPO Y ESFUERZO MIENTRAS LO LEES.", + }, + { "VE A", "https://youtu.be/p7YXXieghto", "PARA VALES GRATIS" }, + } + } +} diff --git a/Cryptid/localization/fr.lua b/Cryptid/localization/fr.lua new file mode 100644 index 0000000..809e795 --- /dev/null +++ b/Cryptid/localization/fr.lua @@ -0,0 +1,3951 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +--Wip Localization, putting this here since people have expressed interst in this + +--[[ +Progress: + +Decks: Almost [ChatSigna] + Beta Deck TODO + Note from Jevonn: Enchanced decks are planned to have their localization done differently, don't worry about these for now +Jokers: Yes [HastagGuigui] +Code Cards: Yes [ChatSigna] +Deck Sleeves (requires Decksleeves Mod): Yes [HastagGuigui] +Boss Blinds: Yes [HastagGuigui] +Spectrals: Yes [HastagGuigui] +Tarots: Yes [HastagGuigui] +Planets: Yes [HastagGuigui] +Dictonary: Yes [HastagGuigui] +Editions: Yes [HastagGuigui] +Vouchers: Yes [HastagGuigui] +Enhancements (aka echo card): Yes [HastagGuigui] +Tags: Yes [HastagGuigui] +Other (packs, stickers, etc): Yes [HastagGuigui] +Misc: Yes [HastagGuigui] +Spooky Update: Yes [HastagGuigui] + +]] -- +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Jeu d'Antimatière", + text = { + "Applique les {C:legendary,E:1}effets{}", + "de {C:attention}tous{} les jeux", + }, + }, + b_cry_beta = { + name = "Nostalgic Deck", + text = { + "{C:attention}Joker{} and {C:attention}Consumable{}", + "slots are {C:attention}combined", + "{C:attention}Nostalgic{} Blinds replace", + "their updated Blind" + }, + }, + b_cry_blank = { + name = "Jeu à blanc", + text = { + "{C:inactive,E:1}Ne fait rien?", + }, + }, + b_cry_bontiful = { + name = "Jeu généreux", + text = { + "Après avoir utilisé une {C:blue}main{} ou une {C:red}défausse{},", + "{C:attention}5{} cartes sont toujours tirées" + } + }, + b_cry_CCD = { + name = "Jeu CCD", + text = { + "Chaque carte est aussi", + "un consommable {C:attention}aléatoire{}", + }, + }, + b_cry_conveyor = { + name = "Jeu Convoyeur", + text = { + "Les Jokers {C:attention}ne peuvent pas{} être déplacés", + "A chaque manche,", + "{C:attention}copie{} le Joker le plus à droite", + "and {C:attention}destroy{} le Joker le plus à gauche", + }, + }, + b_cry_critical = { + name = "Jeu Critique", + text = { + "Après chaque main jouée,", + "{C:green}#1# chance(s) sur 4{} d'obtenir {X:dark_edition,C:white} ^2 {} Multi", + "{C:green}#1# chance(s) sur 8{} d'obtenir {X:dark_edition,C:white} ^0.5 {} Multi", + }, + }, + b_cry_encoded = { + name = "Jeu Encodé", + text = { + "Démarre avec un {C:cry_code,T:j_cry_CodeJoker}Joker Code{}", + "et un {C:cry_code,T:j_cry_copypaste}Copier/Coller{}", + "Seules les {C:cry_code}Cartes Code{} apparaissent dans la boutique", + }, + }, + b_cry_equilibrium = { + name = "Jeu de l'Équilibrium", + text = { + "Toutes les cartes ont", + "la {C:attention}même chance{}", + "d'apparaître dans la boutique,", + "démarre la partie avec", + "{C:attention,T:v_overstock_plus}Excédent Plus", + }, + }, + b_cry_glowing = { + name = "Jeu Brillant", --wtf is glowing? + text = { + "Multiplie les valeurs de", + "tous les Jokers par {X:dark_edition,C:white} X1.25 {}", + "quand la Boss Blinde est battue", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Jeu Infini", + text = { + "Sélectionne {C:attention}n'importe quel", + "nombre de cartes", + "Taille de la main {C:attention}+1{} ", + }, + }, + b_cry_misprint = { + name = "Jeu Mal Imprimé", + text = { + "La valeur des cartes", + "et des mains de poker", + "sont {C:attention}aléatoires", + }, + }, + b_cry_redeemed = { + name = "Jeu Acheté", + text = { + "Quand un {C:attention}Bon d'Achat{} est acheté,", + "gagne son {C:attention}amélioration", + }, + }, + b_cry_spooky = { + name = "Jeu fantôme", + text = { + "Démarre avec un {C:attention,T:j_cry_chocolate_dice}Dé en chocolat{} {C:eternal}éternel", + "Après chaque {C:attention}Ante{}, crée un", + "{C:cry_candy}Bonbon{} ou un Joker {X:cry_cursed,C:white}Maudit{}" + } + }, + b_cry_very_fair = { + name = "Jeu Très Équilibré", + text = { + "{C:blue}-2{} mains, {C:red}-2{} défausses", + "à chaque manche", + "Les {C:attention}Bons d'Achat{} n'apparaissent", + "plus dans la boutique", + }, + }, + b_cry_wormhole = { + name = "Jeu Vortex", + text = { + "Démarre avec un Joker {C:cry_exotic}Exotique{C:attention}", + "Les Jokers sont {C:attention}20X{} ", + "plus susceptibles d'être {C:dark_edition}Négatifs", + "{C:attention}-2{} emplacements de Joker", + }, + }, + b_cry_legendary = { + name = "Jeu Légendaire", + text = { + "Démarre avec un Joker {C:legendary}Légendaire{C:legendary}", + "{C:green}1 chance sur 5{} d'en créer un autre", + "lorsque la Blinde de Boss est battue", + "{C:inactive}(selon la place disponible)", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "La Boîte", + text = { + "Tous les Jokers Communs", + "sont affaiblis", + }, + }, + bl_cry_clock = { + name = "L'Horloge", + text = { + "+0.1X du score requis toutes", + "les 3 secondes passées dans cette Ante", + }, + }, + bl_cry_hammer = { + name = "Le Marteau", + text = { + "Toutes les cartes avec un", + "rang impair sont affaiblies", + }, + }, + bl_cry_joke = { + name = "La Blague", + text = { + "Si le score est >2X plus grand que le score requis,", + "saute directement à l'Ante multiple de #1# suivant", + }, + }, + bl_cry_magic = { + name = "La Magie", + text = { + "Toutes les cartes avec un", + "rang pair sont affaiblies", + }, + }, + bl_cry_lavender_loop = { + name = "Boucle Lavande", + text = { + "1.25X du score requis toutes les", + "1.5 secondes passées dans cette manche", + }, + }, + bl_cry_obsidian_orb = { + name = "Orbe d'Obsidienne", + text = { + "Applique les capacités", + "de tous les boss battus", + }, + }, + bl_cry_oldarm = { + name = "Bras Nostalgique", + text = { + "4 cartes ou moins", + "doivent être jouées", + }, + }, + bl_cry_oldfish = { + name = "Poisson Nostalgique", + text = { + "Toutes les mains", + "démarrent à 1 Multi", + }, + }, + bl_cry_oldflint = { + name = "Silex Nostalgique", + text = { + "Pas de Couleur", + }, + }, + bl_cry_oldhouse = { + name = "Maison Nostalgique", + text = { + "Pas de Full", + }, + }, + bl_cry_oldmanacle = { + name = "Menottes Nostalgiques", + text = { + "Divise le Mult par", + "les Défausses restantes", + }, + }, + bl_cry_oldmark = { + name = "Marque Nostalgique", + text = { + "Pas de main", + "contenant une Paire", + }, + }, + bl_cry_oldox = { + name = "Bœuf Nostalgique", + text = { + "Toutes les mains", + "démarrent avec 0 Jetons", + }, + }, + bl_cry_oldpillar = { + name = "Pilier Nostalgique", + text = { + "Pas de Quinte", + }, + }, + bl_cry_oldserpent = { + name = "Serpent Nostalgique", + text = { + "Divise le Mult par le niveau", + "de la main de poker jouée", + }, + }, + bl_cry_pin = { + name = "L'Épingle", + text = { + "Tous les Jokers Épiques ou de", + "rareté supérieure sont affaiblis", + }, + }, + bl_cry_pinkbow = { + name = "Nœud Rose", + text = { + "Le rang des cartes en main ou jouées", + "est altéré de manière aléatoire", + }, + }, + bl_cry_sapphire_stamp = { + name = "Tampon Saphir", + text = { + "Sélectionnez une carte en plus, déselectionne", + "une carte aléatoire avant de compter les points", + }, + }, + bl_cry_shackle = { + name = "La Manille", + text = { + "Tous les Jokers Négatifs", + "sont affaiblis", + }, + }, + bl_cry_striker = { + name = "La Butée", -- the fuck is striker meaning here + text = { + "Tous les Jokers Rares", + "sont affaiblis", + }, + }, + bl_cry_tax = { + name = "La Taxe", + text = { + "Le score par main est capé à", + "0.4X le score requis", + }, + }, + bl_cry_tornado = { + name = "Tornade Turquoise", + text = { + "#1# chances sur #2# que", + "la main jouée ne score pas", + }, + }, + bl_cry_trick = { + name = "La Ruse", + text = { + "Après chaque main, retourne", + "les cartes visibles en main", + }, + }, + bl_cry_vermillion_virus = { + name = "Virus Vermillion", + text = { + "Un Joker aléatoire est", + "remplacé à chaque main", + }, + }, + bl_cry_windmill = { + name = "Le Moulin", + text = { + "Tous les Jokers Peu communs", + "sont affaiblis", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASSE", + text = { + "Convertit {C:cry_code}#1#{} carte sélectionnée", + "en une amélioration {C:cry_code}de votre choix{}", + }, + }, + c_cry_commit = { + name = "://ENGAGER", + text = { + "Détruit un Joker {C:cry_code}choisi{},", + "crée un {C:cry_code}nouveau{} Joker", + "de la {C:cry_code}même rareté", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Ne le faites pas.", + }, + }, + c_cry_ctrl_v = { + name = "://CTRL+V", + text = { + "Crée une {C:cry_code}copie{} d'une", + "carte à jouer ou d'un consommable", + "sélectionné" + } + }, + c_cry_delete = { + name = "://EFFACER", + text = { + "Enlève de manière {C:cry_code}permanente{}", + "un objet {C:cry_code}choisi{} de la boutique", + "{C:inactive,s:0.8}L'objet n'apparaîtra plus pendant le reste de la partie", + }, + }, + c_cry_divide = { + name = "://DIVISER", + text = { + "{C:cry_code}Divise par 2{} tous les prix", + "indiqués dans la boutique actuelle", + }, + }, + c_cry_exploit = { + name = "://EXPLOITER", + text = { + "Chaque main jouée est calculée", + "comme {C:cry_code}contenant{} une main de poker {C:cry_code}choisie,", + "réinitialisé à la fin de la manche", + "{C:inactive,s:0.8}Les mains secretes doivent être", + "{C:inactive,s:0.8}découvertes pour être valides", + }, + }, + c_cry_hook = { + name = "ACCROCHER://", + text = { + "Les deux jokers sélectionnés deviennent {C:cry_code}Accrochés", + "{C:inactive,s:0.8}Ne marche seulement si les Jokers se déclenchent dans le même contexte,", + "{C:inactive,s:0.8}comme Joker et Le Duo (les deux après le compte)" + }, + }, + c_cry_inst = { + name = "://INSTANCIER", + text = { + "Tire une carte avec la {C:cry_code}couleur{} de la carte sélectionnée", + "et une carte avec le {C:cry_code}rang{} de la carte sélectionnée", + "{C:inactive}(si possible){}" + } + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Ajoute {C:dark_edition}Glitché{} à toutes", " les cartes {C:cry_code}tenues en main" }, + }, + c_cry_merge = { + name = "://FUSIONNER", + text = { + "Fusionne un {C:cry_code}consommable{} sélectionné", + "avec une {C:cry_code}carte à jouer{} choisie", + }, + }, + c_cry_multiply = { + name = "://MULTIPLIER", + text = { + "{C:cry_code}Double{} toutes les valeurs d'un", + "{C:cry_code}Joker{} sélectionné jusqu'à", + "la fin de la manche", + }, + }, + c_cry_patch = { + name = "://PATCH", + text = { + "Enlève tous les affaiblissements et", + "stickers de tous les objets visibles" + } + }, + c_cry_payload = { + name = "://CHARGEUTILE", + text = { + "La prochaine Blinde battue", + "donnera {C:cry_code}X#1#{} intérêts", + }, + }, + c_cry_oboe = { + name = "://DÉCALAGE", + text = { + "Le prochain {C:cry_code}Paquet Booster{} a", + "{C:cry_code}#1#{} carte supplémentaire et", + "{C:cry_code}#1#{} choix supplémentaire", + "{C:inactive}(Currently {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REDÉMARRAGE", + text = { + "Réapprovisionne les {C:blue}Mains{} et les {C:red}Défausses{},", + "remet {C:cry_code}toutes{} les cartes dans le deck", + "et tire une {C:cry_code}nouvelle{} main", + }, + }, + c_cry_revert = { + name = "://REVENIR", + text = { + "Fixe {C:cry_code}l'état du jeu{} au", + "départ de {C:cry_code}cet Ante{}", + }, + }, + c_cry_rework = { + name = "://REMANIEMENT", + text = { + "Détruit un Joker {C:cry_code}choisi{},", + "crée un {C:cry_code}Badge Remaniement{} avec", + "une édition {C:cry_code}améliorée{}", + "{C:inactive,s:0.8}Les améliorations sont", + "{C:inactive,s:0.8}utilisés dans l'ordre de la Collection", + }, + }, + c_cry_run = { + name = "://COURIR", + text = { + "Visite la {C:cry_code}boutique", + "pendant la {C:cry_code}Blinde", + }, + }, + c_cry_seed = { + name = "://GRAINE", + text = { + "Choisit un Joker", + "ou une Carte à Jouer", + "pour ajouter {C:cry_code}Truqué", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Finit la {C:cry_code}Blinde{} non-Boss actuelle", "{C:cry_code}sans{} encaissement" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Crée un Joker", + "Nourriture {C:cry_code}Glitché", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convertit {C:cry_code}#1#{} cartes sélectionnés", + "en un rang {C:cry_code}choisi{}", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astrale", + text = { + "{X:dark_edition,C:white}^#1#{} Multi", + }, + }, + e_cry_blur = { + name = "Floue", + text = { + "{C:attention}Redéclenche{} cette", + "carte {C:attention}1{} fois", + "{C:green}#1# chance(s) sur #2#{}", + "de la redéclencher {C:attention}#3#{}", + "fois de plus", + }, + }, + e_cry_double_sided = { + name = "Double-Face", + text = { + "Cette carte peut être", + "{C:attention}retournée{} pour révéler", + "une autre carte", + }, + }, + e_cry_glass = { + name = "Fragile", + label = "Fragile", + text = { + "{C:white,X:mult} X#3# {} Multi", + "{C:green}#1# chance(s) sur #2#{} de", + "ne pas {C:red}détruite{} cette carte", + "lorsqu'elle est déclenchée", + }, + }, + e_cry_glitched = { + name = "Glitchée", + text = { + "Toutes les valeurs de cette carte", + "sont {C:dark_edition}randomized{}", + "entre {C:attention}X0.1{} et {C:attention}X10{}", + "{C:inactive}(si possible){}", + }, + }, + e_cry_gold = { + name = "Dorée", + label = "Dorée", + text = { + "{C:money}+$#1#{} lorsque cette carte", + "est utilisée ou déclenchée", + }, + }, + e_cry_m = { + name = "Joyeuse", + text = { + "{C:mult}+#1#{} Multi", + "Cette carte a l'air", + "plutôt {C:attention}joyeuse{}", + }, + }, + e_cry_mosaic = { + name = "Mosaïque", + text = { + "{X:chips,C:white} X#1# {} Jetons", + }, + }, + e_cry_noisy = { + name = "Bruitée", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Super-saturée", + text = { + "Toutes les valeurs", + "de cette carte", + "sont {C:attention}doublées{}", + "{C:inactive}(si possible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Carte Écho", + text = { + "{C:green}#2# chance(s) sur #3#{} de", + "{C:attention}redéclencher{} cette carte #1# fois", + "lorsqu'elle est marquée", + }, + }, + }, + Joker = { + j_cry_adroit = { + name = "Joker adroit", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_altgoogol = { + name = "Carte Googol Play Nostalgique", + text = { + "Vendre cette carte crée", + "{C:attention}2{} copies du {C:attention}Joker{} le plus à gauche", + "{C:inactive,s:0.8}Ne copie pas les Carte Googol Play Nostalgiques{}", + }, + }, + j_cry_astral_bottle = { + name = "Astres en Bouteille", + text = { + "Lorsque cette carte est vendue,", + "applique {C:dark_edition}Astral{}", + "et {C:attention}Périssable{} à", + "un {C:attention}Joker{} aléatoire", + } + }, + j_cry_antennastoheaven = { + name = "...Like Antennas to Heaven", + text = { + "Ce Joker gagne", + "{X:chips,C:white} X#1# {} Jetons lorsqu'un", + "{C:attention}7{} or {C:attention}4{} est compté", + "{C:inactive}(Actuellement {X:chips,C:white}X#2# {C:inactive} Jetons)", + }, + }, + j_cry_apjoker = { + name = "Joker AP", + text = { "{X:mult,C:white} X#1# {} Multi contre les {C:attention}Blindes de Boss{}" }, + }, + j_cry_big_cube = { + name = "Gros cube", + text = { + "{X:chips,C:white} X#1# {} Jetons", + }, + }, + j_cry_biggestm = { + name = "Énorme", + text = { + "{X:mult,C:white} X#1# {} Multi jusqu'à la fin", + "de la manche si la {C:attention}main de poker{}", + "est une {C:attention}#2#{}", + "{C:inactive}(Actuellement {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}Je ne suis pas gros, juste un peu enveloppé.", + }, + }, + j_cry_blacklist = { + name = "Liste noire", + text = { + "Si un {C:attention}#1#{} est tenu en main ou joué,", + "fixe les {C:chips}Jetons{} et {C:mult}Multi{} à 0", + "{C:red,E:2}s'auto-détruit{} s'il n'y a pas de {C:attention}#1#{} dans le jeu", + "{C:inactive,s:0.8}Le rang ne change pas" + } + }, + j_cry_blender = { + name = "Blender", + text = { + "Crée un consommable", + "{C:attention}aléatoire{} lorsqu'une", + "carte {C:cry_code}Code{} est utilisée", + "{C:inactive}(Selon la place disponible){}", + }, + }, + j_cry_blurred = { + name = "Joker flou", + text = { + "Gagne {C:blue}+#1#{} main(s) lorsque", + "la {C:attention}Blinde{} est sélectionnée", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Chaque {C:attention}Joker{} donne {C:chips}+#1#{} Jetons", + "Augmente ce nombre de {C:chips}+#2#{} si la", + "{C:attention}main de poker{} jouée est une {C:attention}#3#{}", + "{C:inactive,s:0.8}Les Jokers Joyeux donnent{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}jetons{}", + }, + }, + j_cry_bonkers = { + name = "Joker dingue", + text = { + "{C:red}+#1#{} Multi si", + "la main jouée", + "contient un {C:attention}#2#" + } + }, + j_cry_bonusjoker = { + name = "Joker Bonus", + text = { + "{C:green}#1# chance(s) sur #2#{} pour que chaque", + "carte {C:attention}Bonus{} augmente le", + "nombre d'emplacements {C:attention}Joker{} or {C:attention}Consommable", + "de {C:dark_edition}1{} lorsqu'elle est comptée", + "{C:red}Marche deux fois par manche", + "{C:inactive,s:0.8}(Les chances sont les mêmes pour les deux cas){}", + }, + }, + j_cry_booster = { + name = "Paquet Joker", + text = { + "{C:attention}+#1#{} paquet supplémentaire", + "dans la boutique", + }, + }, + j_cry_boredom = { + name = "Ennui", + text = { + "{C:green}#1# chance(s) sur #2#{} que", + "chaque {C:attention}Joker{} ou {C:attention}carte à jouer{}", + "soient {C:attention}redéclenché(e)s{}", + "{C:inactive,s:0.8}N'affecte pas d'autres Ennuis{}", + }, + }, + j_cry_brittle = { + name = "Nougatine", + text = { + "Pour les {C:attention}#1#{} prochaines mains,", + "ajoute {C:attention}Pierre{}, {C:attention}Or{}, ou {C:attention}Acier{} à", + "la carte marquante la plus à droite" + } + }, + j_cry_bubblem = { + name = "M Bulle", + text = { + "Crée un {C:attention}Joker Joyeux {C:dark_edition}Brillant", + "si la main jouée contient", + "un {C:attention}#1#{}", + "puis {C:red,E:2}s'auto-détruit{}", + }, + }, + j_cry_busdriver = { + name = "Chauffeur de bus", + text = { + "{C:green}#1# chance(s) sur #3#{}", + "pour {C:mult}+#2#{} Multi", + "{C:green}1 chance(s) sur 4{}", + "pour {C:mult}-#2#{} Multi", + }, + }, + j_cry_candy_basket = { + name = "Panier de bonbons", + text = { + "Vendre cette carte crée {C:attention}#1#{} {C:cry_candy}bonbons", + "{C:attention}+#2#{} {C:cry_candy}bonbons{} tous les {C:attention}2{} Blindes battues", + "{C:attention}+#3#{} {C:cry_candy}bonbons{} lorsque la {C:attention}Blinde de Boss{} est battue" + } + }, + j_cry_candy_buttons = { + name = "Bonbons boutons", + text = { + "Les {C:attention}#1#{} prochains réassorts", + "coûtent {C:money}$1{}", + } + }, + j_cry_candy_cane = { + name = "Bonbon canne à sucre", + text = { + "Pour les {C:attention}#1#{} prochaine(s) manche(s),", + "les cartes à jouer donnent {C:money}$#2#", + "lorsqu'elles sont {C:attention}redéclenchées" + } + }, + j_cry_candy_dagger = { + name = "Dague bonbon", + text = { + "Lorsque la {C:attention}Blinde{} est sélectionnée,", + "détruit le Joker à droite de celui-ci", + "pour créer un {C:cry_candy}Bonbon{}", + } + }, + j_cry_candy_sticks = { + name = "Bonbon stick", + text = { + "L'effet de la prochaine blinde de boss n'est pas actif", + "avant que vous ayez joué {C:attention}#1#{} main" + } + }, + j_cry_canvas = { + name = "Canevas", + text = { + "{C:attention}Redéclenche{} tous les {C:attention}Jokers{} à gauche", + "une fois pour {C:attention}chaque{C:attention} Joker{} non-{C:blue}Commun", + "à droite de ce Joker", + }, + }, + j_cry_caramel = { + name = "Caramel", + text = { + "Chaque carte jouée donne", + "{X:mult,C:white}X#1#{} Multi lorsqu'elle est comptée", + "pour les {C:attention}#2#{} prochaines manches", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Redéclenche le Joker", + "{C:attention}le plus à gauche", + "{C:attention}#1#{} fois supplémentaires", + }, + }, + j_cry_chili_pepper = { + name = "Piment", + text = { + "Ce Joker gagne {X:mult,C:white} X#2# {} Multi", + "à la fin de la manche,", + "{C:red,E:2}s'auto-détruit{} après {C:attention}#3#{} manches", + "{C:inactive}(Actuellement{} {X:mult,C:white} X#1# {} {C:inactive}Multi){}", + }, + }, + j_cry_chocolate_dice = { + name = "Dé en chocolat", + text = { + "Lance un {C:green}d10{} lorsque", + "la {C:attention}Blinde de Boss{} est battue", + "pour démarrer un {C:cry_ascendant,E:1}événement", + "{C:inactive}(Actuellement: #1#)" + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Jetons, {X:dark_edition,C:white}^#1#{} Multi", + "s'il reste {C:attention}exactement{}", + "#2# mains à jouer", + }, + }, + j_cry_circus = { + name = "Cirque", + text = { + "Les Jokers {C:red}Rares{} donnent chacun {X:mult,C:white} X#1# {} Multi", + "Les Jokers {C:cry_epic}Épiques{} donnent chacun {X:mult,C:white} X#2# {} Multi", + "Les Jokers {C:legendary}Légendaires{} donnent chacun {X:mult,C:white} X#3# {} Multi", + "Les Jokers {C:cry_exotic}Exotiques{} donnent chacun {X:mult,C:white} X#4# {} Multi", + }, + }, + j_cry_clash = { + name = "Le clash", + text = { + "{X:mult,C:white}x#1#{} Multi si", + "la main jouée", + "contient un {C:attention}#2#" + }, + }, + j_cry_CodeJoker = { + name = "Joker Code", + text = { + "Crée une {C:cry_code}Carte Code", + "{C:dark_edition}Négative{} lorsque", + "la {C:attention}Blinde{} est sélectionnée", + }, + }, + j_cry_coin = { + name = "Crypto-monnaie", + text = { + "Gagne entre", + "{C:money}$#1#{} et {C:money}$#2#{} pour", + "chaque Joker {C:attention}vendu{}", + }, + }, + j_cry_compound_interest = { + name = "Cumul d'intérêts", + text = { + "Gagne {C:money}#1#%{} de votre argent total", + "à la fin de la manche,", + "augmente de {C:money}#2#%{} après", + "chaque paiement", + }, + }, + j_cry_copypaste = { + name = "Copier/Coller", + text = { + "Lorsqu'une carte {C:cry_code}Code{} est utilisée,", + "{C:green}#1# chance(s) sur #2#{} pour ajouter une copie", + "dans votre zone de consommables", + "{C:inactive}(Selon la place disponible)", + }, + }, + j_cry_cotton_candy = { + name = "Barbe-à-papa", -- admit it, you only wish it was as crazy sounding in english + text = { + "Lorsque cette carte est vendue,", + "les {C:attention}Jokers{} adjacents deviennent {C:dark_edition}Négatifs{}" + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "Ce joker gagne {C:chips}+#2#{} Jetons", + "par {C:attention}réapprovisionnement{} dans la boutique", + "{C:green}Tous les réapprovisionnements sont gratuits{}", + "{C:inactive}(Actuellement {C:chips}+#1#{C:inactive} jetons)", + }, + }, + j_cry_cryptidmoment = { + name = "Chaîne de M", + text = { + "Vendre cette carte ajoute", + "{C:money}$#1#{} à la {C:attention}valeur de vente{}", + "de toutes les cartes {C:attention}Joker{}", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Jetons", + }, + }, + j_cry_curse_sob = { + name = "Pleure", + text = { + "{C:edition,E:1}tu ne peux pas {} {C:cry_ascendant,E:1}t'enfuir...{}", + "{C:edition,E:1}tu ne peux pas{} {C:cry_ascendant,E:1}te cacher...{}", + "{C:dark_edition,E:1}tu ne peux pas m'échapper...{}", + "{C:inactive}(Selon la place disponible){}", + }, + }, + j_cry_cursor = { + name = "Curseur", + text = { + "Ce Joker gagne {C:chips}+#2#{} Jetons", + "pour chaque carte {C:attention}achetée{}", + "{C:inactive}(Actuellement {C:chips}+#1#{C:inactive} Jetons)", + }, + }, + j_cry_cut = { + name = "Couper", + text = { + "Ce Joker détruit", + "une carte {C:cry_code}Code{} aléatoire", + "et gagne {X:mult,C:white} X#1# {} Multi", + "à la fin de la {C:attention}boutique{}", + "{C:inactive}(Actuellement {X:mult,C:white} X#2# {C:inactive} Multi)", + }, + }, + j_cry_delirious = { + name = "Joker Délirant", + text = { + "{C:mult}+#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "M Croquis", + text = { + "Crée 2 {C:attention}consommables {C:dark_edition}négatifs{}", + "lorsque la {C:attention}Blinde{} est sélectionnée", + "Crée 1 {C:attention}consommable de plus", + "pour chaque {C:attention}Joker Joyeux{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Échelle", + text = { + "Les {C:attention}Jokers{} augmentant", + "augmentent de manière {C:attention}quadratique", + "{C:inactive,s:0.8}(par ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grandit comme +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Tir Amorti", + text = { + "Ce Joker gagne {X:mult,C:white} X#1# {} Multi pour", + "chaque carte {V:1}#2#{} {C:attention}non-comptée{}", + "La couleur change toutes les manches", + "{C:inactive}(Actuellement {X:mult,C:white} X#3# {C:inactive} Multi)", + }, + }, + j_cry_dubious = { + name = "Joker douteux", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_duos = { + name = "Les duos", + text = { + "{X:mult,C:white}X#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Ce Joker gagne {X:mult,C:white} X#2# {} Multi", + "lorsqu'un {C:attention}Joker{} ou une", + "carte à jouer est marquée", + "{C:inactive}(Actuellement {X:mult,C:white} X#1# {C:inactive} Multi)", + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Tire {C:green}toutes les cartes du deck{} en main", + "lorsque la {C:attention}Blinde{} est sélectionnée", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + -- what did they mean by that + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "Lorsqu'un {C:attention}Badge{} est obtenu,", + "crée {C:attention}#1#{} copies de celui-ci", + "et {C:attention}augmente{} le nombre de", + "copies de {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Les Jokers apparaissent en utilisant", + "l'ordre de la {C:attention}Collection{}", + "Crée {C:attention}#1#{} Joker(s) {C:dark_edition}Négatif(s){}", + "lorsqu'une main est jouée", + "{C:inactive,s:0.8}Les Jokers {C:cry_exotic,s:0.8}Exotiques {C:inactive,s:0.8}ou mieux ne peuvent pas apparaître", + "{s:0.8}Dernier Joker généré: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}E{}{C:red}UR{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Flamme éternelle", + text = { + "Ce Joker gagne {X:mult,C:white} X#1# {} Multi", + "pour chaque carte {C:attention}vendue{}", + "{C:inactive}(Actuellement {X:mult,C:white} X#2# {C:inactive} Multi)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanète", + text = { + "Les cartes {C:dark_edition}Holographiques{}", + "donnent toutes {C:mult}+#1#{} Multi", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "Ce Joker gagne {X:dark_edition,C:white} ^#1# {} Multi", + "lorsqu'un {X:red,C:white} XMult {} est déclenché", + "{C:inactive}(Actuellement {X:dark_edition,C:white} ^#2# {C:inactive} Multi)", + }, + }, + j_cry_exposed = { + name = "Exposé", + text = { + "Retrigger all non-{C:attention}face{} cards", + "{C:attention}#1#{} additional time(s)", + "All {C:attention}face{} cards are debuffed", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Multi si", + "les cartes jouées n'ont été marqués", + "que {C:attention}#2#{} fois ou moins", + }, + }, + j_cry_filler = { + name = "Le filler", -- "english word used in french" moment + text = { + "{X:mult,C:white}X#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + }, + }, + j_cry_fractal = { + name = "Doigts de fractale", + text = { + "{C:attention}+#1#{} à la limite de sélection de cartes", + }, + }, + j_cry_flip_side = { + name = "De l'autre côté", + text = { + "Les jokers {C:dark_edition}Double-face{} utilisent", + "leur face arrière pour les effets", + "au lieu de leur face avant", + "{C:attention}Redéclenche{} tous les jokers {C:dark_edition}Double-Face{}" + }, + }, + j_cry_foodm = { + name = "M Fast-food", + text = { + "{C:mult}+#1#{} Multi", + "{C:red,E:2}s'auto-détruit{} dans {C:attention}#2#{} manches", + "Augmente de {C:attention}#3#{} manche lorsqu'un", + "{C:attention}Joker Joyeux{} est {C:attention}vendu{}", + "{C:inactive,s:0.8}2 Double Cheese, 2 McChickens{}", + "{C:inactive,s:0.8}1 Grande Frite, 20 McNuggets & un muffin{}", + }, + }, + j_cry_foolhardy = { + name = "Joker imprudent", + text = { + "{C:red}+#1#{} Multi si la", + "main jouée contient", + "une {C:attention}#2#" + } + }, + j_cry_formidiulosus = { + name = "Formidiulosus", + text = { + "Lorsqu'un Joker {X:cry_cursed,C:white}Maudit{} est obtenu, le détruit", + "Crée {C:attention}#1#{} {C:cry_candy}Bonbons {C:dark_edition}négatifs{} à la fin de la boutique", + "Gagne {X:dark_edition,C:white}+^#2#{} Multi pour chaque {C:cry_candy}Bonbon{} tenu", + "{C:inactive}(Actuellement {X:dark_edition,C:white}^#3#{C:inactive} Multi)", + -- another word instead of "détenu" i thought of would have been "possédé" + -- but. the ghost. possession. the misunderstanding writes itself + -- - #Guigui + }, + }, + j_cry_foxy = { + name = "Joker narquois", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Hand spinner", + text = { + "Ce Joker gagne {C:chips}+#2#{} Jetons", + "si la main jouée n'est {C:attention}pas{}", + "la {C:attention}main de poker{} la plus jouée", + "{C:inactive}(Actuellement {C:chips}+#1#{C:inactive} Jetons)", + }, + }, + j_cry_fuckedup = { + name = "Joker merdique", + text = { + "{C:mult}+#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Gagne {C:money}$#1#{} si la {C:attention}main jouée{}", + "contient un {C:attention}As{} et un {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} toutes les valeurs", + "du {C:attention}Joker{} le plus à gauche", + "à la fin de la manche", + }, + }, + j_cry_ghost = { + name = "Fantôme", + text = { + "À la fin de la manche:", + "{C:green}#1# chance(s) sur #2#{} de", + "{C:attention}posséder{} un {C:attention}Joker{} aléatoire", + "{C:green}#1# chance(s) sur #3#{} de", + "{E:2,C:red}s'auto-détruire" + } + }, + j_cry_giggly = { + name = "Joker absurde", + text = { + "{C:mult}+#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Joker doré", + text = { + "Gagne {C:money}#1#%{} de l'argent", + "total à la fin de la manche", + "Le gain augmente de {C:money}#2#%{}", + "lorsqu'une carte {C:attention}dorée{}", + "est comptée", + }, + }, + j_cry_googol_play = { + name = "Carte Googol Play", + text = { + "{C:green}#1# chance(s) sur #2#{} de compter", + "{X:red,C:white} X#3# {} Multi", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Crée un {C:attention}Joker{} aléatoire", + "à la fin de la manche", + "Vendre cette carte", + "crée un {C:attention}Joker{} aléatoire", + "{C:inactive}(Selon la place disponible){}", + }, + }, + j_cry_happyhouse = { + name = "Jolie maison", + text = { + "{X:dark_edition,C:white}^#1#{} Multi seulement après", + "avoir joué {C:attention}114{} mains{}", + "{C:inactive}(Actuellement #2#/114){}", + "{C:inactive,s:0.8}On n'est jamais aussi bien que chez soi !{}", + }, + }, + j_cry_home = { + name = "La maison", + text = { + "{X:mult,C:white}X#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + }, + }, + j_cry_hunger = { + name = "Consomme-able", + text = { + "Gagne {C:money}$#1#{} lorsqu'un", + "{C:attention}consommable{} est utilisé", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Redéclenche toutes les cartes", + "{C:attention}#2#{} fois,", + "chaque carte jouée donne", + "{X:mult,C:white} X#1# {} Multi lorsqu'elle est comptée", + }, + }, + j_cry_jawbreaker = { + name = "Casse-dent", + text = { + "Lorsque la {C:attention}Blinde de Boss{} est battue,", + "{C:attention}double{} toutes les valeurs des Jokers adjacents", + "puis {E:2,C:red}s'auto-détruit{}", + } + }, + j_cry_jimball = { + name = "Jimboule", + text = { + "Ce Joker gagne {X:mult,C:white} X#1# {} Multi", + "pour chaque main {C:attention}consécutive{} jouée", + "tant que votre {C:attention}main de poker{} la plus jouée", + "est jouée", + "{C:inactive}(Actuellement {X:mult,C:white} X#2# {C:inactive} Multi)", + }, + }, + j_cry_jollysus = { + name = "Joker Joyeux?", + text = { + "Crée un Joker {C:dark_edition}Joyeux{}", + "quand un joker est {C:attention}vendu{}", + "{C:red}Fonctionne une fois par manche{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Je vois pas de souci...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Gagne {C:money}$#2#{} à la fin de la manche", + "Augmente le gain de {C:money}$#1#{}", + "lorsqu'un Joker {C:attention}type {C:mult}multi{} ou", + "{C:attention}type chips{} est vendu", + }, + }, + j_cry_kooky = { + name = "Joker cinglé", + text = { + "{C:mult}+#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty le clown", + text = { + "Ce Joker gagne", + "{X:mult,C:white} X#1# {} Multi lorsqu'une", + "{C:attention}carte à jouer{} est marquée", + "{C:inactive}(Actuellement {X:mult,C:white} X#2# {C:inactive} Multi)", + }, + }, + j_cry_kscope = { + name = "Kaléidoscope", + text = { + "Ajoute {C:dark_edition}Polychrome{} à", + "un {C:attention}Joker{} aléatoire lorsque", + "{C:attention}la Blinde de Boss{} est battue", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Chaque {C:attention}7{} ou {C:attention}2{} joué", + "donne {X:mult,C:white}X#1#{} Multi lorsqu'il est marqué", + }, + }, + j_cry_longboi = { + name = "Monstre", + text = { + "Ajoute aux prochaines versions", + "de ce joker {X:mult,C:white}X#1#{} Multi", + "à la fin de la manche", + "{C:inactive}(Actuellement {X:mult,C:white}X#2#{C:inactive} Multi){}", + }, + }, + j_cry_loopy = { + name = "Bouclé", + text = { + "{C:attention}Redéclenche{} tous les jokers", + "une fois pour chaque {C:attention}Joker{}", + "{C:attention}joyeux{} vendu cette manche", + "{C:inactive}(Actuellement {}{C:attention:}#1#{}{C:inactive} redéclenchement(s)){}", + "{C:inactive,s:0.8}Y'avait pas assez de place...{}", + }, + }, + j_cry_lucky_joker = { + name = "Joker chanceux", + text = { + "Gagne {C:money}$#1#{} à chaque fois qu'une", + "carte {C:attention}Chance{} est {C:green}déclenchée", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "Tous les Jokers donnent", + "{X:chips,C:white} X#1# {} Jetons", + }, + }, + j_cry_m = { + name = "m", + text = { + "Ce Joker gagne {X:mult,C:white} X#1# {} Multi", + "lorsqu'un {C:attention}Joker joyeux{} est vendu", + "{C:inactive}(Actuellement {X:mult,C:white} X#2# {C:inactive} Multi)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Crée un {C:attention}Joker joyeux{}", + "{C:dark_edition}Négatif{} lorsque", + "la {C:attention}Blinde{} est sélectionnée", + }, + }, + j_cry_macabre = { + name = "Joker macabre", + text = { + "Lorsque la {C:attention}Blinde{} est sélectionnée,", + "détruit chaque {C:attention}Joker{} à part les", + "{C:legendary}jokers M{} et {C:attention}jokers joyeux{}", + "et crée un {C:attention}joker joyeux{}", + "pour chaque carte détruite", + }, + }, + j_cry_magnet = { + name = "Magnet frigo", + text = { + "Gagne {C:money}$#1#{} à la fin de la manche", + "Le gain est {X:money,C:white} X#2# {} s'il y a", + "{C:attention}#3#{} cartes {C:attention}Joker{} ou moins", + }, + }, + j_cry_manic = { + name = "Joker maniaque", + text = { + "{C:mult}+#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Redéclenche tous les Jokers", + "{C:attention}#1#{} fois de plus", + }, + }, + j_cry_mask = { + name = "Masque", + text = { + "Redéclenche toutes les cartes {C:attention}Figure{}", + "{C:attention}#1#{} fois supplémentaire(s)", + "Toutes les cartes non-{C:attention}Figure{} sont affaiblies" + } + }, + j_cry_maximized = { + name = "À donf", + text = { + "Toutes les cartes {C:attention}Figure{}", + "sont considérés des {C:attention}Rois{},", + "toutes les cartes {C:attention}numérotées{}", + "sont considérées des {C:attention}10{}", + }, + }, + j_cry_maze = { + name = "Labyrinthe", + text = { + "Toutes les mains sont considérées la", + "{C:attention}première main{} de chaque manche,", + "toutes les défausses sont considérées la", + "{C:attention}première défausse{} de chaque manche", + }, + }, + j_cry_Megg = { + name = "Mœuf", + text = { + "Vendre cette carte crée", + "{C:attention}#2#{} jokers joyeux #3#, augmente", + "de {C:attention}#1#{} à la fin de la manche", + }, + }, + j_cry_mellowcreme = { + name = "Bonbon citrouille", + text = { + "Vendre cette carte {C:attention}multiplie", + "la valeur de vente de tous", + "les {C:attention}consommables{} par {C:attention}X#1#" + } + }, + j_cry_membershipcard = { + name = "Carte de membre", + text = { + "{X:mult,C:white}X#1#{} Multi pour chaque membre", + "dans le {C:attention}serveur Discord{} de {C:attention}Cryptid{}", + "{C:inactive}(Actuellement {X:mult,C:white}X#2#{C:inactive} Multi)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Vieille carte de membre", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Jetons pour chaque membre", + "dans le {C:attention}serveur Discord{} de {C:attention}Cryptid{}", + "{C:inactive}(Actuellement {C:chips}+#2#{C:inactive} Jetons)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Pluie de météores", + text = { + "Les cartes {C:dark_edition}Brillantes{} donnent", + "chacun {C:chips}+#1#{} Jetons", + }, + }, + j_cry_mneon = { + name = "M Néon", + text = { + "Gagne {C:money}$#2#{} à la fin de la manche", + "Augmente le prix de", + "{C:money}$#1#{} pour tous les {C:attention}jokers joyeux{}", + "ou {C:attention}légendaires{} à la fin de la manche", + "{C:inactive}(Augmentation minimum de{} {C:money}$1{}{C:inactive}){}", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "Ce Joker gagne {X:mult,C:white} X#1# {} Multi", + "si aucune {C:attention}défausse{} n'a été", + "utilisée cette manche", + "{C:inactive}(Actuellement {X:mult,C:white} X#2# {C:inactive} Multi)", + }, + }, + j_cry_monkey_dagger = { + name = "Dague du singe", + text = { + "Lorsque la {C:attention}Blinde{} est sélectionnée,", + "détruit le Joker situé à sa gauche", + "et augmente ces {C:mult}Jetons{} de {C:attention}dix fois{}", + "la valeur de vente du Joker détruit", + "{C:inactive}(Actuellement {C:chips}+#1#{C:inactive} Jetons)", + }, + }, + j_cry_monopoly_money = { + name = "Billet de Monopoly", + text = { + "{C:green}#1# chance(s) sur #2#{} de", + "{C:attention}détruire{} les objets achetés", + "Divise l'argent par 2 si {C:attention}vendu", + } + }, + j_cry_morse = { + name = "Code morse", + text = { + "Gagne {C:money}$#2#{} à la fin de la manche", + "Augmente le gain de {C:money}$#1#{} lorsqu'une", + "carte avec une {C:attention}Édition{} est vendue", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Crée un {C:legendary}Joker M{} à la fin de la manche", + "Chaque {C:attention}Joker joyeux{} ou {C:legendary}Joker M", + "donne {X:dark_edition,C:white}^#1#{} Multi", + "Augmente ce nombre de {X:dark_edition,C:white}^#2#{}", + "lorsqu'un {C:attention}Joker joyeux{} est {C:attention}vendu", + "{C:inactive,s:0.8}(Tredecim exclu)", + }, + }, + j_cry_mstack = { + name = "Pile de Ms", + text = { + "Redéclenche toutes les cartes jouées", + "une fois pour chaque", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jokers Joyeux{} vendus", + "{C:inactive}(Actuellement {C:attention:}#1#{C:inactive} redéclenchements)", + }, + }, + j_cry_multjoker = { + name = "Joker Multi", + text = { + "{C:green}#1# chance(s) sur #2#{} pour chaque", + "carte {C:attention}Multi{} jouée de créer", + "une carte {C:spectral}Cryptide{} lorsqu'elle est marquée", + "{C:inactive}(Selon la place disponible)", + }, + }, + j_cry_necromancer = { + name = "Nécromancien", + text = { + "Lorsqu'un Joker est {C:attention}vendu{} pour plus de {C:attention}$0{},", + "gagne un Joker {C:attention}aléatoire{} {C:attention}vendu{} durant cette partie", + "et fixe sa {C:attention}valeur de vente{} à {C:attention}$0{}", + }, + }, + j_cry_negative = { + name = "Joker négatif", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} max", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Jetons si la main jouée", + "contient un {C:attention}6{} et un {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Nuit", + text = { + "{X:dark_edition,C:white}^#1#{} Multi sur la", + "main finale de la manche", + "{E:2,C:red}S'auto-détruit{} après déclenchement", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Redéclenche chaque {C:attention}7{} joué", + "{C:attention:}#1#{} fois supplémentaires", + }, + }, + j_cry_notebook = { + name = "Carnet", + text = { + "{C:green} #1# chance(s) sur #2#{} de gagner {C:dark_edition}+1{} Joker max", + "à chaque {C:attention}réapprovisionnement{} de la boutique", + "Se {C:green}déclenche toujours{} s'il y a", + "{C:attention}#5# Jokers Joyeux{} ou plus", + "{C:red}Fonctionne une seule fois par manche{}", + "{C:inactive}(Actuellement {C:dark_edition}+#3#{}{C:inactive} et #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Blocs numéros", + text = { + "Gagne {C:money}$#1#{} à la fin de la manche", + "Augmente le gain de {C:money}$#2#{}", + "pour chaque {C:attention}#3#{} tenu en main,", + "le rang change à chaque manche", + }, + }, + j_cry_nuts = { + name = "Les noix", + text = { + "{X:mult,C:white}X#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + }, + }, + j_cry_nutty = { + name = "Joker foldingue", + text = { + "{C:mult}+#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_oil_lamp = { + name = "Lampe à huile", + text = { + "Augmente les valeurs du Joker {C:attention}à la droite{} de celui-ci", + "de {C:attention}x#1#{} à la fin de la manche", + }, + }, + j_cry_oldblueprint = { + name = "Vieux modèle", + text = { + "Copie les capacités du", + "{C:attention}Joker{} à sa droite", + "{C:green}#1# chance(s) sur #2#{} que", + "cette carte soit détruite", + "à la fin de la manche", + }, + }, + j_cry_oldcandy = { + name = "Bonbon rétro", + text = { + "Vendre cette carte", + "augmente la taille de la main de", + "{C:attention}+#1#{} de manière permanente", + }, + }, + j_cry_oldinvisible = { + name = "Joker Invisible Nostalgique", + text = { + "{C:attention}Duplique{} un {C:attention}Joker{}", + "aléatoire tous les {C:attention}4", + "cartes Joker vendues", + "{s:0.8}Joker Invisible Nostalgique exclu{}", + "{C:inactive}(Actuellement #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panoptique", + text = { + "Toutes les mains sont considérés", + "la {C:attention}dernière main{} de chaque manche", -- +$4 + }, + }, + j_cry_penetrating = { + name = "Joker perçant", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_pickle = { + name = "Cornichon", + text = { + "Lorsque la {C:attention}Blinde{} est passée, crée", + "{C:attention}#1#{} Badges, réduit de", + "{C:red}#2#{} lorsque la {C:attention}Blinde{} est sélectionnée", + }, + }, + j_cry_pirate_dagger = { + name = "Dague pirate", + text = { + "Lorsque la {C:attention}Blinde{} est sélectionnée,", + "détruit le Joker à droite de celui-ci", + "et gagne {C:attention}un quart{} de", + "sa valeur de vente en tant que {X:chips,C:white} XJetons {}", + "{C:inactive}(Actuellement {X:chips,C:white} X#1# {C:inactive} Jetons)", + }, + }, + j_cry_pot_of_jokes = { + name = "Pot de Blagues", + text = { + "{C:attention}#1#{} à la taille de la main,", + "mais augmente de", + "{C:blue}#2#{} à la fin de la manche", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "Ce Joker gagne {X:dark_edition,C:white} ^#1# {} Multi", + "si toutes les cartes jouées sont des", + "{C:attention}As{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, ou {C:attention}7{}", + "{C:inactive}(Actuellement {X:dark_edition,C:white} ^#2# {C:inactive} Multi)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "Ce Joker gagne", + "{X:mult,C:white} X#1# {} Multi lorsqu'une", + "carte {C:cry_code}Code{} est utilisée", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Gambit de la Dame", + text = { + "Si la {C:attention}main de poker{} jouée est une", + "{C:attention}Quinte flush royale{}, détruit la", + "{C:attention}Dame{} marquante et crée", + "un {C:attention}Joker {C:red}Rare {C:dark_edition}Negativé", + }, + }, + j_cry_quintet = { + name = "Le Quintet", + text = { + "{X:mult,C:white}X#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + }, + }, + j_cry_redbloon = { + name = "Bloon Rouge", + text = { + "Gagne {C:money}$#1#{} dans {C:attention}#2#{} manche#3#", + "{C:red,E:2}s'auto-détruit{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante lorsque", + "{C:money}$#2#{} {C:inactive}($#3#){} sont dépensés", + "{s:0.8}Cette condition croît", + "{C:attention,s:0.8}exponentiellement{s:0.8} à chaque déclenchement", + "{C:money,s:0.8}Prochaine augmentation: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "Lorsqu'un {C:attention}Joker{} est vendu,", + "ajoute ses effets à", + "tous les autres jokers", + "{C:inactive,s:0.8}N'affecte pas d'autres Rescribere{}" + } + }, + j_cry_reverse = { + name = "Carte Inversion", + text = { + "Remplit tous les emplacements de Joker vides {C:inactive}(100 max){}", + "avec des {C:attention}Jokers Joyeux {C:dark_edition}Holographiques{} si", + "la {C:attention}main de poker défaussée{} est une {C:attention}#1#{}", + "{C:red,E:2}s'auto-détruit{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Les capacités sont changées à chaque {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Crée un Joker {C:green}Peu commun{}", + "et 3 {C:attention}Jokers Joyeux{} lorsqu'une", + "carte {C:spectral}Spectrale{} est utilisée", + "{C:red}Ne marche qu'une fois par manche{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Arbuste", + text = { + "Après avoir marqué {C:attention}#2#{} {C:inactive}[#1#]{}", + "cartes améliorées, vendre cette carte", + "crée un {C:attention}Joker{} {C:cry_epic}Épique{}", + "{C:inactive,s:0.8}Crée un {C:attention,s:0.8}Joker{} {C:red,s:0.8}Rare{}", + "{C:inactive,s:0.8}si les Jokers {C:cry_epic,s:0.8}Épiques{} {C:inactive,s:0.8}sont désactivés{}", + }, + }, + j_cry_savvy = { + name = "Joker avisé", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient un {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Les {C:attention}Jokers{} augmentant augmentent", + "comme un polynome de degré {C:attention}#1#{}", + "élève le degré de {C:attention}#2#{}", + "à la fin de la manche", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} exclu)", + }, + }, + j_cry_scrabble = { + name = "Lettre de Scrabble", + text = { + "{C:green}#1# chance(s) sur #2#{} de créer", + "un Joker {C:dark_edition}Joyeux {C:green}Peu commun{}", + "lorsque la main est jouée", + }, + }, + j_cry_seal_the_deal = { + name = "Affaire scellée", + text = { + "Ajoute un {C:attention}sceau aléatoire{} à chaque carte", + "marquée lors de la {C:attention}dernière main{} de la manche", + }, + }, + j_cry_shrewd = { + name = "Joker astucieux", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Joker farfelu", + text = { + "{C:mult}+#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Riquiqui", + text = { + "{X:chips,C:white} X#1# {} Jetons jusqu'à la", + "fin de la manche si la {C:attention}main de poker{} jouée", + "est un(e) {C:attention}#2#{}", + "{C:inactive}(Actuellement {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker max", + "{C:attention}+#1#{} Paquet Booster max", + "{C:attention}+#1#{} taille de main", + "{C:attention}+#1#{} emplacement de consommable", + "{C:attention}+#1#{} carte dans la boutique", + }, + }, + j_cry_spaceglobe = { + name = "Globe Céleste", + text = { + "Ce Joker gagne {X:chips,C:white}X#2#{} Jetons", + "si la {C:attention}main de poker{} jouée est un(e) {C:attention}#3#{},", + "La main change après chaque gain{}", + "{C:inactive}(Actuellement{} {X:chips,C:white}X#1#{} {C:inactive}Jetons){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Crée une copie {C:dark_edition}Négative{}", + "d'un {C:attention}Joker{} aléatoire", + "à la fin de la {C:attention}boutique", + "{C:inactive,s:0.8}Ne copie pas d'autres Speculo{}", + }, + }, + j_cry_spy = { + name = "Le Spy", + text = { + "{X:mult,C:white} X#2# {} Multi, {C:dark_edition}+1{} emplacement {C:attention}Joker{}", + "{C:inactive}Ce #1# est un Espion!", + }, + }, + j_cry_stardust = { + name = "Poussière d'étoile", + text = { + "Les cartes {C:dark_edition}Polychrome{}", + "donnent chacun {X:mult,C:white}X#1#{} Multi", + "{C:inactive,s:0.8}L'effet ne se déclenche pas", + "{C:inactive,s:0.8}sur Poussière d'étoile", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "Ce Joker détruit une", + "carte {C:planet}Planète{} aléatoire", + "et gagne {X:dark_edition,C:white} ^#1# {} Multi", + "à la fin de la {C:attention}boutique{}", + "{C:inactive}(Actuellement {X:dark_edition,C:white} ^#2# {C:inactive} Multi)", + }, + }, + j_cry_stronghold = { + name = "La Forteresse", + text = { + "{X:mult,C:white}x#1#{} Multi si", + "la main jouée", + "contient un {C:attention}#2#" + }, + }, + j_cry_subtle = { + name = "Joker subtil", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Jetons, {C:mult}+#1#{} Multi,", + "{X:chips,C:white}X#2#{} Jetons, {X:mult,C:white}X#2#{} Multi", + "Gagne {C:money}$#3#{} à", + "la fin de la manche", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "À la fin de la manche, crée", + "une {C:attention}copie{} d'une carte", + "aléatoire {C:attention}tenue en main{},", + "détruit toutes les autres", + "{s:0.8}Les {C:attention,s:0.8}Rois{s:0.8} de {C:hearts,s:0.8}Cœur{s:0.8} sont prioritaires", + }, + }, + j_cry_swarm = { + name = "L'essaim", + text = { + "{X:mult,C:white}x#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + }, + }, + j_cry_sync_catalyst = { + name = "Catalyse Synchro", + text = { + "Balance les {C:chips}Jetons{} et le {C:mult}Multi{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tax_fraud = { + name = "Évasion Fiscale", + text = { + "Octroie {C:attention}#1#${} par {C:attention}Joker en Location", + "à la fin de la manche" + } + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} max", + "Gagne {C:money}$#2#{} à la fin de la manche", + }, + }, + j_cry_translucent = { + name = "Joker translucent", + text = { + "Vendre cette carte crée", + "une copie {C:attention}Périssable Banane{}", + "d'un {C:attention}Joker{} aléatoire", + "{s:0.8,C:inactive}(La copie ignore les compatibilités Périssable)", + }, + }, + j_cry_treacherous = { + name = "Joker traître", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_trick_or_treat = { + name = "Des bonbons ou un sort!", + text = { + "Lorsque cette carte est {C:attention}vendue{}:", + "{C:green}#1# chance(s) sur #2#{} de créer {C:attention}2{} {C:cry_candy}bonbons", + "Sinon, crée un Joker {X:cry_cursed,C:white}Maudit{}", + "{C:inactive}(Peut dépasser les maximums)" + } + }, + j_cry_tricksy = { + name = "Joker espiègle", + text = { + "{C:chips}+#1#{} Jetons si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Rythme triolet", + text = { + "{X:mult,C:white} X#1# {} Multi si la main marquée", + "contient {C:attention}exactement{} trois cartes {C:attention}3", + }, + }, + j_cry_tropical_smoothie = { + name = "Smoothie tropical", + text = { + "Vendre cette carte", + "{C:attention}multiplie{} les valeurs", + "des jokers possédés de {C:attention}X1.5{}" + } + }, + j_cry_unity = { + name = "L'Unité", + text = { + "{X:mult,C:white}x#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + }, + },j_cry_universe = { + name = "L'Univers", + text = { + "Les cartes {C:dark_edition}Astrales{}", + "donnent toutes {X:dark_edition,C:white}^#1#{} Multi", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "Les {C:attention}mains de poker{} gagnent", + "{X:red,C:white} X#1# {} Multi et {X:blue,C:white} X#1# {} Jetons", + "à chaque amélioration", + }, + }, + j_cry_unjust_dagger = { + name = "Dague injuste", + text = { + "Lorsque la {C:attention}Blind{} est sélectionnée,", + "détruit le Joker le plus à droite", + "et gagne {C:attention}un cinquième{} de", + "sa valeur de vente en tant que {X:mult,C:white} XMulti {}", + "{C:inactive}(Actuellement {X:mult,C:white} X#1# {C:inactive} Multi)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "Lorsqu'une probabilitée", + "est déclenchée et {C:green}succainte{},", + "ce Joker gagne {X:red,C:white}XMulti{}", + "égal à ses {C:attention}probabilités{} listées", + "{C:inactive}(Actuellement {X:mult,C:white} X#1# {C:inactive} Multi)", + }, + }, + j_cry_virgo = { + name = "Vierge", + text = { + "Ce Joker gagne {C:money}$#1#{} à sa {C:attention}valeur de vente{}", + "si la {C:attention}main de poker{} contient une {C:attention}#2#{}", + "Vendre cette carte crée", + "un {C:attention}Joker Joyeux{} {C:dark_edition}Polychrome{}", + "tous les {C:money}$4{} de {C:attention}valeur de vente{} {C:inactive}(1 minimum){}", + }, + }, + j_cry_wacky = { + name = "Joker loufoque", + text = { + "{C:mult}+#1#{} Multi si", + "la main jouée", + "contient une {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "Tous les Jokers donnent", + "{X:mult,C:white} X#1# {} Multi", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "Tous les Jokers donnent", + "{C:money}$#1#{} à chaque déclenchement", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "Ce Joker gagne", + "{C:mult}+#2#{} Multi lorsque chaque", + "{C:attention}As{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, ou {C:attention}8{}", + "joué est compté", + "{C:inactive}(Actuellement {C:mult}+#1#{C:inactive} Multi)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Redéclenche chaque {C:attention}2{} joué", --wee gaming + "{C:attention:}#1#{} fois supplémentaire(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "la Roue de l'espérance", + text = { + "Ce Joker gagne", + "{X:mult,C:white} X#1# {} Multi à chaque échec", + "de la {C:attention}Roue de la Fortune{}", + "{C:inactive}(Actuellement {X:mult,C:white} X#2# {C:inactive} Multi)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "Ce Joker gagne {X:mult,C:white} X#1# {} Multi", + "si la {C:attention}main jouée{} contient", + "un {C:attention}2{} et un {C:attention}7{} de couleur différentes", + "{C:inactive}(Actuellement {X:mult,C:white} X#2# {C:inactive} Multi)", + }, + }, + j_cry_wrapped = { + name = "Bonbon enveloppé", + text = { + "Crée un {C:attention}Joker Nourriture{}", + "dans {C:attention}#1#{} manche(s)", + "puis {C:red,E:2}s'auto-détruit{}", + }, + }, + j_cry_wtf = { + name = "Le Bordel", + text = { + "{X:mult,C:white}x#1#{} Multi si", + "la main jouée", + "contient un {C:attention}#2#" + }, + } + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Améliore", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "et {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Améliore", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "et {C:attention}#3#{}", + }, + }, + c_cry_Kaikki = { + name = "Kaikki", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Améliore", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "et {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Étoile à neutrons", + text = { + "Améliore une main de poker", + "aléatoire d'{C:attention}1{} niveau", + "pour chaque utilisation", + "d'{C:attention}Étoile à neutrons{}", + "durant cette partie", + "{C:inactive}(Actuellement{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planete.lua", + text = { + "{C:green}#1# chance(s) sur #2#{}", + "d'améliorer toutes", + "les {C:legendary,E:1}mains de poker{}", + "d'{C:attention}1{} niveau", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Améliore", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "et {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Améliore", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "et {C:attention}#3#{}", + }, + }, + c_cry_marsmoons = { + name = "Phobos & Déimos", + text = { + "{S:0.8}({S:0.8,V:1}niv.#1#{S:0.8}){} Améliore", + "{C:attention}#2#", + "{C:mult}+#3#{} Multi et", + "{C:chips}+#4#{} Jetons" + } + }, + c_cry_void = { + name = "Vide", + text = { + "{S:0.8}({S:0.8,V:1}niv.#1#{S:0.8}){} Améliore", + "{C:attention}#2#", + "{C:mult}+#3#{} Multi et", + "{C:chips}+#4#{} Jetons" + } + }, + c_cry_asteroidbelt = { + name = "Ceinture d'astéroïdes", + text = { + "{S:0.8}({S:0.8,V:1}niv.#1#{S:0.8}){} Améliore", + "{C:attention}#2#", + "{C:mult}+#3#{} Multi et", + "{C:chips}+#4#{} Jetons" + } + }, + c_cry_universe = { + name = "l'entièreté du putain d'univers", + text = { + "{S:0.8}({S:0.8,V:1}niv.#1#{S:0.8}){} Améliore", + "{C:attention}#2#", + "{C:mult}+#3#{} Multi et", + "{C:chips}+#4#{} Jetons" + } + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "Pochette CCD", + text = { + "Chaque carte est aussi", + "un consommable {C:attention}aléatoire{}", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Pochette Convoyeur", + text = { + "Les Jokers {C:attention}ne peuvent pas{} être déplacés", + "A chaque manche,", + "{C:attention}copie{} le Joker le plus à droite", + "and {C:attention}destroy{} le Joker le plus à gauche", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Pochette Critique", + text = { + "Après chaque main jouée,", + "{C:green}1 chance(s) sur 4{} d'obtenir {X:dark_edition,C:white} ^2 {} Multi", + "{C:green}1 chance(s) sur 8{} d'obtenir {X:dark_edition,C:white} ^0.5 {} Multi", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Pochette encodée", + text = { + "Démarre avec un {C:cry_code,T:j_cry_CodeJoker}Joker Code{}", + "et un {C:cry_code,T:j_cry_copypaste}Copier/Coller{}", + "Seules les {C:cry_code}Cartes Code{} apparaissent dans la boutique", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Pochette équilibrée", + text = { + "Toutes les cartes ont", + "la {C:attention}même chance{}", + "d'apparaître dans la boutique,", + "démarre la partie avec", + "{C:attention,T:v_overstock_plus}+2 cartes dans la boutique", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Pochette illimitée", + text = { + "Vous pouvez sélectionner", + "{C:attention}n'importe quel{} nombre de cartes", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Pochette défective", + text = { + "La valeur des cartes", + "sont {C:attention}aléatoires", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Pochette 2-pour-le-prix-d'un", + text = { + "Quand un {C:attention}coupon{} est acheté,", + "gagne ses {C:attention}améliorations", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Pochette Vortex", + text = { + "Démarre avec un Joker {C:cry_exotic}Exotique{C:attention}", + "Les Jokers sont {C:attention}20X{} ", + "plus susceptibles d'être {C:dark_edition}Négatifs", + "{C:attention}-2{} emplacements de Joker", + }, + }, + sleeve_cry_legendary_sleeve = { + name = "Pochette Légendaire", + text = { + "Démarre avec un Joker {C:legendary}Légendaire{C:legendary}", + "{C:green}1 chance sur 5{} d'en créer un autre", + "lorsque la Blinde de Boss est battue", + "{C:inactive}(selon la place disponible)", + }, + }, + }, + Spectral = { + c_cry_adversary = { + name = "Opposition", + text = { + "{C:red}Tous{} vos {C:attention}Jokers{} deviennent {C:dark_edition}Négatifs{},", + "{C:red}tous{} les {C:attention}Jokers{} dans la boutique coûteront", + "{C:red}2X{} plus cher pour le reste de la partie", + }, + }, + c_cry_analog = { + name = "Analogue", + text = { + "Crée {C:attention}#1#{} copies d'un", + "{C:attention}Joker{} aléatoire, détruit", + "tous les autres Jokers, {C:attention}+#2#{} Ante", + }, + },c_cry_chambered = { + name = "Chambré", + text = { + "Crée {C:attention}#1#{} copies {C:dark_edition}Négatives{}", + "d'un consommable {C:attention}aléatoire", + "{C:inactive,s:0.8}Ne copie pas Chambré{}" + }, + }, + c_cry_conduit = { + name = "Conduit", + text = { + "Échange les {C:attention}éditions{} de", + "{C:attention}2{} cartes ou {C:attention}Jokers{} sélectionnées", + }, + }, + c_cry_gateway = { + name = "Portail", + text = { + "Crée un {C:attention}Joker{} {C:cry_exotic,E:1}Exotique", + "aléatoire, détruit", + "tous les autres Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Verrou", + text = { + "Enlève {C:red}tous{} les stickers", + "de {C:red}tous{} les Jokers,", + "puis applique {C:purple,E:1}Éternel{}", + "à un {C:attention}Joker{} aléatoire", + }, + }, + c_cry_pointer = { + name = "POINTEUR://", + text = { + "Crée une carte", + "de {C:cry_code}votre choix", + "{C:inactive,s:0.8}(Jokers exotiques #1#exclus)", + }, + }, + c_cry_replica = { + name = "Réplique", + text = { + "Convertit toutes les", + "cartes en main", + "en une carte {C:attention}aléatoire{}", + "tenue en main", + }, + }, + c_cry_ritual = { + name = "Rituel", + text = { + "Applique {C:dark_edition}Négatif{}, {C:dark_edition}Mosaïque", + "ou {C:dark_edition}Astral{} à {C:attention}#1#{}", + "carte sélectionnée" + } + }, + c_cry_source = { + name = "Source", + text = { + "Ajoute un {C:cry_code}Sceau vert{}", + "à {C:attention}#1#{} carte", + "sélectionnée", + }, + }, + c_cry_summoning = { + name = "Invocation", + text = { + "Crée un {C:joker}Joker{} {C:cry_epic}Épique{}", + "aléatoire, détruit un", + "autre {C:joker}Joker{} aléatoire", + }, + }, + c_cry_trade = { + name = "Échange", + text = { + "{C:attention}Perdez{} un Coupon aléatoire,", + "gagnez {C:attention}2{} Coupons aléatoires", + }, + }, + c_cry_typhoon = { + name = "Typhon", + text = { + "Ajoute un {C:cry_azure}Sceau Azur{}", + "à {C:attention}#1#{} carte", + "sélectionnée", + }, + }, + c_cry_vacuum = { + name = "Aspiration", + text = { + "Enlève {C:red}toutes{} les {C:green}modifications{}", + "de {C:red}toutes{} les cartes en main", + "Gagne {C:money}$#1#{} par {C:green}modification{} enlevée", + "{C:inactive,s:0.7}(par ex. Améliorations, Sceaux, Éditions)", + }, + }, + c_cry_white_hole = { + name = "Trou blanc", + text = { + "{C:attention}Enlève{} toues les niveaux de toutes les mains,", + "améliore la main de poker {C:legendary,E:1}la plus jouée{}", + "de {C:attention}3{} pour chaque niveau enlevé", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Mise rose", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Le score requis augmente (beaucoup)", + "plus rapidement pour chaque {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Mise marron", + colour = "Brown", + text = { + "Tous les {C:attention}stickers{} sont", + "compatibles entre eux", + }, + }, + stake_cry_yellow = { + name = "Mise jaune", + colour = "Yellow", + text = { + "Les {C:attention}stickers{} peuvent apparaître", + "sur tous les objets achetables", + }, + }, + stake_cry_jade = { + name = "Mise jade", + colour = "Jade", + text = { + "Les cartes peuvent être tirées {C:attention}face cachée{}", + }, + }, + stake_cry_cyan = { + name = "Mise cyan", + colour = "Cyan", + text = { + "Les Jokers {C:green}Peu-communs{} et {C:red}Rares{} ont", + "moins de chances d'apparaître", + }, + }, + stake_cry_gray = { + name = "Mise grise", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Mise pourpre", + colour = "Crimson", + text = { + "Les coupons sont réapprovisionnés aux Antes {C:attention}pairs{}", + }, + }, + stake_cry_diamond = { + name = "Mise diamant", + colour = "Diamond", + text = { + "Gagner nécessite de battre l'Ante {C:attention}10{}", + }, + }, + stake_cry_amber = { + name = "Mise ambre", + colour = "Amber", + text = { + "{C:attention}-1{} emplacement de Paquet Booster", + }, + }, + stake_cry_bronze = { + name = "Mise bronze", + colour = "Bronze", + text = { + "Les coupons coûtent {C:attention}50%{} plus cher", + }, + }, + stake_cry_quartz = { + name = "Mise quartz", + colour = "Quartz", + text = { + "Les jokers peuvent être {C:attention}Épinglés{}", + "{s:0.8,C:inactive}(Restent épinglés à la position la plus à gauche){}", + }, + }, + stake_cry_ruby = { + name = "Mise rubis", + colour = "Ruby", + text = { + "Les {C:attention}Grosses{} Blindes peuvent être", + "des Blindes de {C:attention}Boss{}", + }, + }, + stake_cry_glass = { + name = "Mise verre", + colour = "Glass", + text = { + "Les cartes peuvent se {C:attention}briser{}", + "lorsqu'elles sont marquées", + }, + }, + stake_cry_sapphire = { + name = "Mise saphir", + colour = "Sapphire", + text = { + "À la fin de l'ante,", + "perdez {C:attention}25%{} de votre argent", + "{s:0.8,C:inactive}(Jusqu'à $10){}", + }, + }, + stake_cry_emerald = { + name = "Mise émeraude", + colour = "Emerald", + text = { + "Les cartes, paquets et coupons", + "peuvent être {C:attention}face cachée{}", + "{s:0.8,C:inactive}(Impossible de les voir avant de les acheter){}", + }, + }, + stake_cry_platinum = { + name = "Mise platine", + colour = "Platinum", + text = { + "Les Petites Blindes sont {C:attention}enlevées{}", + }, + }, + stake_cry_twilight = { + name = "Mise aube", + colour = "Twilight", + text = { + "Les cartes peuvent être {C:attention}Banane{}", + "{s:0.8,C:inactive}(1 chance(s) sur 10 d'être détruite à la fin de la manche){}", + }, + }, + stake_cry_verdant = { + name = "Mise verdoyante", + colour = "Verdant", + text = { + "Le score requis augmente (toujours)", + "plus rapidement pour chaque {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Mise embre", + colour = "Ember", + text = { + "Tous les objets ne donnent pas d'argent", + "lorsqu'ils sont vendus" + }, + }, + stake_cry_dawn = { + name = "Mise aurore", + colour = "Dawn", + text = { + "Les cartes Tarots et Spectrales ciblent", + "{C:attention}1{} carte de moins", + "{s:0.8,C:inactive}(Minimum 1){}", + }, + }, + stake_cry_horizon = { + name = "Mise horizon", + colour = "Horizon", + text = { + "Lorsque la blinde est sélectionnée,", + "une {C:attention}carte aléatoire{} est", + "ajoutée au deck", + }, + }, + stake_cry_blossom = { + name = "Mise éclat", + colour = "Blossom", + text = { + "Les Blindes {C:attention}Finales{} peuvent apparaître", + "dans {C:attention}n'importe quel{} Ante", + }, + }, + stake_cry_azure = { + name = "Mise azur", + colour = "Azure", + text = { + "Les valeurs sur les Jokers", + "sont réduits de {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Mise ascendante", + colour = "Ascendant", + text = { + "{C:attention}-1{} emplacement", + "dans la boutique", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Badge Astral", + text = { + "Le prochain Joker sans Édition dans", + "la boutique devient gratuit et {C:dark_edition}Astral" + } + }, + tag_cry_banana = { + name = "Badge Banane", + text = { + "Crée un {C:attention}#1#", + "{C:inactive}(Selon la place disponible){}", + }, + }, + tag_cry_bettertop_up = { + name = "Badge de remplissage (en mieux)", + text = { + "Crée jusqu'à {C:attention}#1#", + "Jokers {C:green}Peu-Communs{}", + "{C:inactive}(Selon la place disponible){}", + }, + }, + tag_cry_better_voucher = { + name = "Badge Ticket d'Or", -- base cryptid isn't crazy enough with their badge names smh + text = { + "Ajoute un coupon de catégorie {C:attention}#1#{}", + "dans la prochaine boutique", + }, + }, + tag_cry_blur = { + name = "Badge Flou", + text = { + "Le prochain Joker sans Édition dans", + "la boutique devient gratuit et {C:dark_edition}Flou" + } + }, + tag_cry_booster = { + name = "Badge Booster", + text = { + "Le prochain {C:cry_code}Paquet Booster{} a", + "{C:attention}2X plus{} de cartes et", + "{C:attention}2X plus{} de choix", + }, + }, + tag_cry_bundle = { + name = "Lot de Badges", + text = { + "Crée un {C:attention}Badge Standard{}, {C:tarot}Badge Breloque{},", + "{C:attention}Badge Bouffon{}, and {C:planet}Badge Météore", + }, + }, + tag_cry_cat = { + name = "Badge Chat", + text = { + "Miaou !", + "{C:inactive}Niveau {C:dark_edition}#1#" + }, + }, + tag_cry_console = { + name = "Badge Commande", + text = { + "Octroie un", + "{C:cry_code}Paquet Code" + } + }, + tag_cry_double_m = { + name = "Badge Double M", + text = { + "La boutique a un", + "{C:red}Joker M {C:dark_edition}Joyeux{}" + } + }, + tag_cry_empowered = { + name = "Badge Renforcé", + text = { + "Octroie un {C:spectral}Paquet Spectral", + "avec {C:legendary,E:1}l'Âme{} et {C:cry_exotic,E:1}Portail{}" + } + }, + tag_cry_epic = { + name = "Badge Épique", + text = { + "La boutique a un", + "{C:cry_epic}Joker Épique{} à moitié prix" + } + }, + tag_cry_gambler = { + name = "Badge du Parieur", + text = { + "{C:green}#1# chance(s) sur #2#{} de créer", + "un {C:cry_exotic,E:1}Badge Renforcé", + }, + }, + tag_cry_glass = { + name = "Badge Fragile", + text = { + "Le prochain Joker sans Édition dans", + "la boutique devient gratuit et {C:dark_edition}Fragile" + } + }, + tag_cry_glitched = { + name = "Badge Buggé", + text = { + "Le prochain Joker sans Édition dans", + "la boutique devient gratuit et {C:dark_edition}Glitché" + } + }, + tag_cry_gold = { + name = "Badge Doré", + text = { + "Le prochain Joker sans Édition dans", + "la boutique devient gratuit et {C:dark_edition}Doré" + } + }, + tag_cry_gourmand = { + name = "Badge à Emporter", -- the icon is a bag so + text = { + "La boutique a un", + "{C:cry_epic}Joker Nourriture{} gratuit" + } + }, + tag_cry_loss = { + name = "Loss", + text = { + "Octroie un", + "{C:cry_ascendant}Paquet Mème" + }, + }, + tag_cry_m = { + name = "Badge Sourire", + text = { + "Le prochain Joker sans Édition dans", + "la boutique devient gratuit et {C:dark_edition}Joyeux" + } + }, + tag_cry_memory = { + name = "Badge Mémoire", + text = { + "Crée {C:attention}#1#{} copies du", + "dernier {C:attention}Badge{} utilisé", + "durant cette partie", + "{s:0.8,C:inactive}Les Badges de Copie sont exclus", + "{s:0.8,C:inactive}Actuellement : {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Badge Mosaïque", + text = { + "Le prochain Joker sans Édition dans", + "la boutique devient gratuit et {C:dark_edition}Mosaïque" + } + }, + tag_cry_oversat = { + name = "Badge Sur-saturé", + text = { + "Le prochain Joker sans Édition dans", + "la boutique devient gratuit et {C:dark_edition}Sur-saturé" + } + }, + tag_cry_quadruple = { + name = "Badge Quadruple", + text = { + "Octroiera {C:attention}#1#{} copies du", + "prochain {C:attention}Badge{} sélectionné", + "{s:0.8,C:inactive}Badges de Copie exclus", + }, + }, + tag_cry_quintuple = { + name = "Badge Quintuple", + text = { + "Octroiera {C:attention}#1#{} copies du", + "prochain {C:attention}Badge{} sélectionné", + "{s:0.8,C:inactive}Badges de Copie exclus", + }, + }, + tag_cry_rework = { + name = "Badge Remaniement", + text = { + "La boutique a un", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Badge Schéma", + text = { + "La boutique a un", + "{C:attention}Remue-méninges", + }, + }, + tag_cry_scope = { + name = "Badge Mesuré", + text = { + "{C:attention}+#1# {C:blue}mains{} et", + "{C:red}défausses{} à la prochaine manche", + }, + }, + tag_cry_triple = { + name = "Badge Triple", + text = { + "Octroiera {C:attention}#1#{} copies du", + "prochain {C:attention}Badge{} sélectionné", + "{s:0.8,C:inactive}Badges de Copie exclus", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "l'Automate", + text = { + "Crée jusqu'à {C:attention}#1#", + "cartes {C:cry_code}Code{} aléatoires", + "{C:inactive}(Selon la place disponible)", + }, + }, + c_cry_eclipse = { + name = "l'Éclipse", + text = { + "Transforme jusqu'à {C:attention}#1#", + "cartes sélectionnées en", + "{C:attention}Carte(s) écho", + }, + }, + c_cry_meld = { + name = "le Mélange", + text = { + "Transforme un {C:attention}Joker{} ou", + "une {C:attention}carte à jouer{} en", + "carte {C:dark_edition}Double-face", + }, + }, + c_cry_theblessing = { + name = "la Bénédiction", + text = { + "Crée {C:attention}1{}", + "{C:attention}consommable{} aléatoire", + "{C:inactive}(Selon la place disponible){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Astéroglyphe", + text = { + "Règle l'Ante sur {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Tableau Blanc", + text = { + "{C:attention}+#1#{} à la taille de la main", + }, + }, + v_cry_clone_machine = { + name = "Machine de Clones", + text = { + "Les Badges double deviennent", + "des {C:attention}Badges Quintuple{} et", + "sont {C:attention}4X{} plus communs", + }, + }, + v_cry_command_prompt = { + name = "Invite de Commandes", + text = { + "Les cartes {C:cry_code}Code{}", + "peuvent apparaître", + "dans la {C:attention}boutique{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Les Badges double deviennent", + "des {C:attention}Badges Triple{} et", + "sont {C:attention}2X{} plus communs", + }, + }, + v_cry_curate = { + name = "Curation", + text = { + "Toutes les cartes", + "apparaissent avec", + "une {C:dark_edition}Édition{}", + }, + }, + v_cry_dexterity = { + name = "Dextérité", + text = { + "{C:blue}+#1#{} main(s) par manche", + "{C:inactive}(oui, encore)" + } + }, + v_cry_double_down = { + name = "Double ou double", + text = { + "Après chaque manche,", + "{X:dark_edition,C:white} X1.5 {} à toutes les valeurs", + "à l'arrière des", + "cartes {C:dark_edition}Double-Face{}" + }, + }, + v_cry_double_slit = { + name = "Double fente", + text = { + "{C:attention}le Mélange{} peut apparaître", + "dans la boutique et dans", + "les Paquets Arcana", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "Les cartes {C:dark_edition}Double-Face{} apparaîssent", + "{C:attention}4X{} plus souvent", + }, + }, + v_cry_fabric = { + name = "Toile Universelle", + text = { + "{C:dark_edition}+#1#{} emplacement(s) de Joker", + }, + }, + v_cry_massproduct = { + name = "Production de Masse", + text = { + "Toutes les cartes et tous les paquets", + "dans la boutique coutent {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Haricot d'Argent", + text = { + "Augmente le plafond", + "des intérêts perçus", + "par manche à {C:money}#1#${}" + } + }, + v_cry_overstock_multi = { + name = "Multi-stock", + text = { + "{C:attention}+#1#{} emplacement(s) de carte(s) et", + "{C:attention}+#1#{} emplacement(s) de paquet(s)", + "disponibles dans la boutique", + }, + }, + v_cry_pacclimator = { + name = "Acclimateur de Planète", + text = { + "Les cartes {C:planet}Planète{} apparaissent", + "{C:attention}X#1#{} plus souvent", + "dans la boutique", + "Toutes les futures cartes", + "{C:planet}Planète{} sont {C:green}gratuites{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pair'amount Plus", + text = { + "{C:attention}Redéclenche{} tous les Jokers M", + "une fois pour chaque Paire", + "{C:attention}contenue{} dans la main jouée", + }, + }, + v_cry_pairing = { + name = "Lapaire", + text = { + "{C:attention}Redéclenche{} tous les Jokers M", + "si la main jouée est une {C:attention}Paire", + "{C:inactive,s:0.8}Y'en a pas deux!" + }, + }, + v_cry_quantum_computing = { + name = "Informatique Quantique", + text = { + "Les cartes {C:cry_code}Code{} peuvent apparaître", + "avec l'édition {C:dark_edition}Négative{}", + }, + }, + v_cry_repair_man = { + name = "Ré-paire-ation", + text = { + "{C:attention}Redéclenche{} tous les Jokers M", + "si la main jouée contient une {C:attention}Paire", + }, + }, + v_cry_rerollexchange = { + name = "Réassorts en Folie", + text = { + "{C:green}Réassortir{} la boutique", + "coûte {C:money}2${}" + } + }, + v_cry_satellite_uplink = { + name = "Liaison Astrale", + text = { + "Les Cartes {C:cry_code}Code{} peuvent", + "apparaître dans n'importe lequel", + "des {C:attention}Paquets Célestes{}", + }, + }, + v_cry_scope = { + name = "Portée Galactique", + text = { + "Crée la carte {C:planet}Planète", + "de la {C:attention}main de poker", + "jouée", + "{C:inactive}(Selon la place disponible){}", + }, + }, + v_cry_tacclimator = { + name = "Acclimateur de Tarot", + text = { + "Les cartes {C:tarot}Tarot{} apparaissent", + "{C:attention}X#1#{} plus souvent", + "dans la boutique", + "Toutes les futures cartes", + "{C:tarot}Tarot{} seront {C:green}gratuites{}", + }, + }, + v_cry_tag_printer = { + name = "Imprimante à Badge", + text = { + "Les Badges Double deviennent des", + "{C:attention}Badges Quadruple{} et", + "sont {C:attention}3X{} plus communs", + }, + }, + v_cry_threers = { + name = "Les 3 R", + text = { + "{C:red}#1#{} défausses", + "par manche" + }, + }, + v_cry_stickyhand = { + name = "Main Collante", + text = { + "{C:attention}+#1#{} limite", + "de sélection de cartes", + }, + }, + v_cry_grapplinghook = { + name = "Grappin", + text = { + "{C:attention}+#1#{} limite", + "de sélection de cartes", + "{C:inactive,s:0.7}Il est possible de faire beacuoup plus que ce que vous pensez.{}", + }, + }, + v_cry_hyperspacetether = { + name = "Accroche Intersidérale", + text = { + "{C:attention}+#1#{} limite", + "de sélection de cartes", + "{C:inactive,s:0.7}NOTE: Aura plus de{}", + "{C:inactive,s:0.7}fonctionnalités plus tard{}", + }, + }, + }, + Other = { + banana = { + name = "Banane", + text = { + "{C:green}#1# chance(s) sur #2#{} d'être", + "détruit après chaque manche", + }, + }, + cry_rigged = { + name = "Truqué", + text = { + "Toutes les probabilités {C:cry_code}listées{}", + "sont {C:cry_code}garanties", + }, + }, + cry_hooked = { + name = "Accroché", + text = { + "Quand ce joker est {C:cry_code}déclenché{},", + "déclenche {C:cry_code}#1#", + }, + }, + cry_flickering = { + name = "Luisant", + text = { + "Détruit après", + "{C:attention}#1#{} déclenchements", + "{C:inactive}({C:attention}#2#{C:inactive} restants)" + }, + }, + cry_flickering_desc = { --used by choco dice + name = "Luisant", + text = { + "Détruit après", + "{C:attention}#1#{} déclenchements", + }, + }, + cry_possessed = { + name = "Possédé", + text = { + "{C:attention}Désactive{} et {C:attention}inverse{}", + "les effets, si possible", + "Détruit en même temps que {C:attention}Fantôme" + }, + }, + food_jokers = { + name = "Jokers Nourriture", + text = { + "{s:0.8}Gros Michel, Œuf, Crème Glacée, Cavendish,", + "{s:0.8}Haricot Noir, Cola zéro, Popcorn, Ramen,", + "{s:0.8}Eau de Seltz, Cornichon, Piment, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}La mise à jour{s:0.7} est désactivée par défaut ({C:attention,s:0.7}Module HTTPS{s:0.7})", + }, + }, + ev_cry_choco0 = { + name = "", + text = { + "Les détails d'un {C:cry_ascendant,E:1}événement", + "seront affichés ici" + } + }, + ev_cry_choco1 = { + name = "1: Possession", + text = { + "Les {C:attention}Jokers{} et cartes à jouer ont", + "{C:green}1 chance sur 3{} de gagner Luisant", + "Crée un {C:attention}Fantôme", + "{C:inactive,s:0.7}Vous avez été possédé par un fantôme, et", + "{C:inactive,s:0.7}votre conscience vacille." + } + }, + ev_cry_choco2 = { + name = "2: Maison Hantée", + text = { + "Impossible de passer une Blinde {C:attention}Blinde{}", + "Un {C:attention}réassort{} autorisé par boutique", + "Les prix des {C:attention}coupons{} ont doublé", + "{C:inactive,s:0.7}Les esprits d'Halloween ont pris d'assaut cette partie!", + "{C:inactive,s:0.7}Ne touchez à rien et échappez-vous au plus vite!", + } + }, + ev_cry_choco3 = { + name = "3: Les préparations de la sorcière", + text = { + "Crée 3 {C:attention}Potions", + "Utilises-en une avant la fin de la {C:attention}Petite Blinde{},", + "ou {C:attention}tous{} les malus seront appliqués durant cet {C:attention}Ante", + "{C:inactive,s:0.7}Vous avez été kidnappé par une sorcière!", + "{C:inactive,s:0.7}Elle vous scrute, et vous offre trois potions.", + "{C:inactive,s:0.7}Choisis-en une, ou elle va choisir pour toi.", + } + }, + ev_cry_choco4 = { + name = "4: Abysse Lunaire", + text = { + "Les cartes à jouer ont {C:green}1 chance sur 4{}", + "de devenir une carte figure {C:club}Trèfle{}", + "Divise le {C:attention}Multi{} par le nombre de cartes face joués", + "{C:inactive,s:0.7}Même un homme au cœur pur", + "{C:inactive,s:0.7}qui fait ses prières le soir..." + } + }, + ev_cry_choco5 = { + name = "5: Sangsue", + text = { + "Enlève les {C:attention}Améliorations{} de toutes les cartes jouées", + "{C:green}1 chance sur 3{} de détruire", + "les cartes {C:heart}Cœur{} et {C:diamond}Carreau{}", + "{C:inactive,s:0.7}Méfiez vous à la tombée de la nuit, car", + "{C:inactive,s:0.7,E:1}ceux dans les ombres {C:inactive,s:0.7} cherchent à assouvir leur soif..." + } + }, + ev_cry_choco6 = { + name = "6: Prenez-en qu'un", + text = { + "À la {C:attention}fin de la manche{}, ouvre un", + "Paquet {C:attention}Booster{} aléatoire", + "{C:inactive,s:0.7}Tandis que vous déambulez dans les rues, vous apercevez une", + "{C:inactive,s:0.7}boîte remplie de Paquets Booster. Autant se servir !" + } + }, + ev_cry_choco7 = { + name = "7: L'heure de la fête", + text = { + "Crée 3 {C:attention}Des bonbons ou un sort!{} et 1 {C:attention}Panier de bonbons", + "Les boutiques ont un {C:attention}Des bonbons ou un sort!{} à chaque manche", + "Les {C:cry_candy}bonbons{} donnent {C:money}$3{} lorsqu'ils sont obtenus", + "{C:inactive,s:0.7}Tout le quartier est décoré pour les festivités fantasmagoriques,", + "{C:inactive,s:0.7}venez rejoindre l'ambience!" + } + }, + ev_cry_choco8 = { + name = "8: Pluie de bonbons", + text = { + "Lorsque la {C:attention}Blinde{} est battue, obtenez 1 {C:cry_candy}bonbon{}", + "par main restante; Obtenez un {C:attention}Joker Nourriture{}", + "lorsqu'un {C:cry_candy}bonbon{} est généré", + "{C:inactive,s:0.7}Il pleut des bonbons ! Dépechez vous et", + "{C:inactive,s:0.7,E:1}attrappez-en autant que vous pouvez !" + } + }, + ev_cry_choco9 = { + name = "9: Richesse spirituelle", + text = { + "Donne {C:money}$20", + "Tout {C:money}l'argent{} gagné est {C:attention}doublé", + "{C:inactive,s:0.7}Le spectre d'un proche disparu depuis longtemps", + "{C:inactive,s:0.7}vous visite au milieu de la nuit!", + "{C:inactive,s:0.7}Sans un mot, il place un sac d'argent dans vos mains,", + "{C:inactive,s:0.7}arbore un grand sourire, et vous salue avant de disparaitre dans l'air.", + } + }, + ev_cry_choco10 = { + name = "10: Antiquité vénérée", + text = { + "Un {C:attention}Joker{} {C:legendary}Légendaire{} apparaît", + "dans l'emplacement {C:attention}Coupon{} pour {C:money}$50", + "Ne peut être acheté qu'en tant que le {C:attention}dernier{} objet de la boutique", + "{C:inactive,s:0.7}Vous avez attiré l'attention de l'esprit d'une relique,", + "{C:inactive,s:0.7}mais cet esprit ne va pas se laisser apaiser si facilement.", + } + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Éternel", + text = { + "Toutes les cartes du paquet", + "sont {C:attention}Éternelles{}", + }, + }, + cry_perishable_booster = { + name = "Périssable", + text = { + "Toutes les cartes du paquet", + "sont {C:attention}Périssables{}", + }, + }, + cry_rental_booster = { + name = "Location", + text = { + "Toutes les cartes du paquet", + "sont {C:attention}en Location{}", + }, + }, + cry_pinned_booster = { + name = "Épinglé", + text = { + "Toutes les cartes du paquet", + "sont {C:attention}Épinglées{}", + }, + }, + cry_banana_booster = { + name = "Banane", + text = { + "Toutes les cartes du paquet", + "sont {C:attention}Bananes{}", + }, + }, + cry_eternal_voucher = { + name = "Éternel", + text = { + "Ne peut pas être échangé", + }, + }, + cry_perishable_voucher = { + name = "Périssable", + text = { + "Affaibli après", + "{C:attention}#1#{} manches", + "{C:inactive}({C:attention}#2#{C:inactive} restantes)", + }, + }, + cry_rental_voucher = { + name = "Location", + text = { + "Coûte {C:money}#1#${} à", + "la fin de la manche" + } + }, + cry_pinned_voucher = { + name = "Épinglé", + text = { + "Reste dans la boutique", + "jusqu'à ce qu'il soit échangé", + }, + }, + cry_banana_voucher = { + name = "Banane", + text = { + "{C:green}#1# chance(s) sur #2#{} d'être", + "enlevé à la fin de la manche", + }, + }, + cry_perishable_consumeable = { + name = "Périssable", + text = { + "Affaibli à la", + "fin de la manche", + }, + }, + cry_rental_consumeable = { + name = "Location", + text = { + "Coûte {C:money}#1#${} à", + "la fin de la manche, et", + "à l'utilisation" + } + }, + cry_pinned_consumeable = { + name = "Épinglé", + text = { + "Vous ne pouvez pas utiliser", + "de consommables non-{C:attention}Épinglés{}", + }, + }, + cry_banana_consumeable = { + name = "Banane", + text = { + "{C:green}#1# chance(s) sur #2#{} de", + "ne rien faire à l'utilisation", + }, + }, + p_cry_code_normal_1 = { + name = "Paquet Programme", + text = { + "Jusqu'à {C:attention}#1#{} cartes", + "{C:cry_code}Code{} parmi {C:attention}#2#", + }, + }, + p_cry_code_normal_2 = { + name = "Paquet Programme", + text = { + "Jusqu'à {C:attention}#1#{} cartes", + "{C:cry_code}Code{} parmi {C:attention}#2#", + }, + }, + p_cry_code_jumbo_1 = { + name = "Super Paquet Programme", + text = { + "Jusqu'à {C:attention}#1#{} cartes", + "{C:cry_code}Code{} parmi {C:attention}#2#", + }, + }, + p_cry_code_mega_1 = { + name = "Méga Paquet Programme", + text = { + "Jusqu'à {C:attention}#1#{} cartes", + "{C:cry_code}Code{} parmi {C:attention}#2#", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Paquet Mème", + text = { + "Jusqu'à {C:attention}#1#{} cartes", + "{C:attention}Joker Mème{} parmi {C:attention}#2#{}", + }, + }, + p_cry_meme_two = { + name = "Paquet Mème", + text = { + "Jusqu'à {C:attention}#1#{} cartes", + "{C:attention}Joker Mème{} parmi {C:attention}#2#{}", + }, + }, + p_cry_meme_three = { + name = "Paquet Mème", + text = { + "Jusqu'à {C:attention}#1#{} cartes", + "{C:attention}Joker Mème{} parmi {C:attention}#2#{}", + }, + }, + undiscovered_code = { + name = "Non découvert", + text = { + "Acheter ou utiliser", + "cette carte dans une", + "partie sans seed pour", + "révéler son effet" + } + }, + undiscovered_unique = { + name = "Non découvert", + text = { + "Acheter ou utiliser", + "cet objet dans une", + "partie sans seed pour", + "révéler son effet" + } + }, + cry_green_seal = { + name = "Sceau vert", + text = { + "Crée une carte {C:cry_code}Code{}", + "lorsque la carte est jouée", + "et non comptée", + "{C:inactive}(Selon la place disponible)", + }, + }, + cry_azure_seal = { + name = "Sceau azur", + text = { + "Crée {C:attention}#1#{} {C:planet}Planètes", + "{C:dark_edition}négatives{} pour la", + "{C:attention}main de poker jouée{},", + "puis {C:red}détruit{} cette carte", + }, + }, + blurred_sdm0 = { + name = "a", + text = { + "{C:inactive,s:0.7}\"Je déteste cette carte\" - SDM_0, 2024{}{}", + } + } + }, + Unique = { + c_cry_potion = { + name = "Potion", + text = { + "Applique un {C:attention}malus{}", + "inconnu lorsqu'elle est utilisée", + "{C:inactive,s:0.7}Obtenu du Dé en chocolat" + } + } + } + }, + misc = { + poker_hands = { + ['cry_Bulwark'] = "Rempart", + ['cry_Clusterfuck'] = "Foutoir", + ['cry_UltPair'] = "Super Paire", + ['cry_WholeDeck'] = "un putain de jeu complet", + }, + poker_hand_descriptions = { + ['cry_Bulwark'] = { + '5 cartes sans rang ni couleur' + }, + ['cry_Clusterfuck'] = { + 'Au moins 8 cartes sans', + 'Paire, Couleur, ou Quinte' + }, + ['cry_UltPair'] = { + 'Deux Doubles Paires, où chaque', + 'Double Paire est une couleur unique,', + 'pour un total de deux couleurs entre elles' + }, + ['cry_WholeDeck'] = { + 'Une main qui contient toutes les cartes', + "d'un jeu standard de 52 cartes.", + "Je sais pas quoi dire, mais tu es fou." + } + }, + achievement_names = { + ach_cry_ace_in_crash = "ACE de poche", + ach_cry_blurred_blurred_joker = "Cécité totale", + ach_cry_bullet_hell = "Pluie de balles", + ach_cry_break_infinity = "Atteindre l'infini", + ach_cry_cryptid_the_cryptid = "Cryptid un Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Full tordu", + ach_cry_googol_play_pass = "Pass Googol Play", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Agent immobilier", + ach_cry_jokes_on_you = "On fait moins le malin, maintenant !", + ach_cry_niw_uoy = "! éngag zeva suoV", + ach_cry_now_the_fun_begins = "Le fun peut commencer", + ach_cry_patience_virtue = "La patience est une vertue.", + ach_cry_perfectly_balanced = "Équilibre parfait", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Embouteillages", + ach_cry_ult_full_skip = "Je passe", + ach_cry_used_crash = "Tu t'attendais à quoi ?", + ach_cry_what_have_you_done = "QU'EST-CE QUE TU AS FAIT ?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtenir un Joker Flou flou", + ach_cry_bullet_hell = "Avoir 15 Jokers AP", + ach_cry_break_infinity = "Marquer 1.79e308 Jetons en une seule main", + ach_cry_cryptid_the_cryptid = "Utiliser une carte Cryptide sur une carte Cryptide", + ach_cry_exodia = "Avoir 5 Jokers exotiques", + ach_cry_freak_house = "Jouer un Full flush composé de 6 et 9 tout en ayant Nice", + ach_cry_googol_play_pass = "Truquer une Carte Googol Play", + ach_cry_haxxor = "Utiliser un code de triche", + ach_cry_home_realtor = "Activer Jolie Maison avant l'Ante 8 (sans Équilibrium/Antimatière)", + ach_cry_jokes_on_you = "Déclencher l'effet de La Blague à l'Ante 1 puis gagner la partie", + ach_cry_niw_uoy = "Atteindre l'Ante -8", + ach_cry_now_the_fun_begins = "Obtenir un Canevas", + ach_cry_patience_virtue = + "Attendre 2 minutes face à la Boucle Lavande avant de jouer la première main, puis battre la blinde", + ach_cry_perfectly_balanced = "Battre le Jeu Très Équilibré avec la Mise Ascendante", + ach_cry_pull_request = "Faire ://ENGAGER apparaître le même joker qu'il a détruit", + ach_cry_traffic_jam = "Battre tous les challenges Heure de Pointe", + ach_cry_ult_full_skip = "Gagner en 1 tour", + ach_cry_used_crash = "Utiliser ://CRASH", + ach_cry_what_have_you_done = "Supprimer ou sacrifier un Joker exotique", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Bataille de dagues", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "La carte unique", + c_cry_rng = "Aléas de l'aléatoire", -- okay i think i cooked with this one + c_cry_rush_hour = "Heure de pointe I", + c_cry_rush_hour_ii = "Heure de pointe II", + c_cry_rush_hour_iii = "Heure de pointe III", + c_cry_sticker_sheet = "Feuille d'autocollants", + c_cry_sticker_sheet_plus = "Feuille d'autocollants +", + }, + dictionary = { + --Settings Menu + cry_set_features = "Fonctionnalités", + cry_set_music = "Musique", + cry_set_enable_features = "Sélectionnez les fonctionnalités à activer (appliqué au prochain redémarrage):", + cry_feat_achievements = "Succès", + ["cry_feat_antimatter deck"] = "Jeu Anti-matière", + cry_feat_blinds = "Blindes", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Cartes Code", + ["cry_feat_misc. decks"] = "Jeux divers", + ["cry_feat_https module"] = "Module HTTPS", + ["cry_feat_timer mechanics"] = "Mécaniques Chrono", + ["cry_feat_enhanced decks"] = "Decks Améliorés", + ["cry_feat_epic jokers"] = "Jokers épiques", + ["cry_feat_exotic jokers"] = "Jokers exotiques", + ["cry_feat_m jokers"] = "Jokers M", + cry_feat_menu = "Menu principal personnalisé", + ["cry_feat_misc."] = "Divers", + ["cry_feat_misc. jokers"] = "Jokers divers", + cry_feat_planets = "Planètes", + cry_feat_jokerdisplay = "JokerDisplay (Fait rien)", + cry_feat_tags = "Badges", + cry_feat_sleeves = "Pochettes", + cry_feat_spectrals = "Cartes spectrales", + cry_feat_spooky = "Mise à jour Spooky", + ["cry_feat_more stakes"] = "Mises", + cry_feat_vouchers = "Coupons", + cry_mus_jimball = "Jimboule (Funkytown par Lipps Inc. - Sous droits d'auteur)", + cry_mus_code = "Cartes Code (://LETS_BREAK_THE_GAME par HexaCryonic)", + cry_mus_exotic = "Jokers exotiques (Joker in Latin par AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] par AlexZGreat)", + + k_cry_program_pack = "Paquet Programme", + k_cry_meme_pack = "Paquet Meme", + + cry_critical_hit_ex = "Coup critique!", + cry_critical_miss_ex = "Échec critique!", + + cry_potion1 = "-1 à tous les niveaux de main", + cry_potion2 = "X1.15 à la taille de la Blinde", + cry_potion3 = "-1 main et défausse", + + cry_debuff_oldhouse = "Pas de Full", + cry_debuff_oldarm = "4 cartes ou moins doivent être jouées", + cry_debuff_oldpillar = "Pas de Quinte", + cry_debuff_oldflint = "Pas de Couleur", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applique les capacités de tous les boss battus", + + k_code = "Code", + k_unique = "Unique", + b_code_cards = "Cartes Code", + b_unique_cards = "Cartes uniques", + b_pull = "TIRER", + cry_hooked_ex = "Accroché!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTRER UN RANG", + cry_code_enh = "ENTRER UNE AMELIORATION", + cry_code_hand = "ENTRER MAIN DE POKER", + cry_code_enter_card = "ENTRER UNE CARTE", + cry_code_apply = "APPLIQUER", + cry_code_apply_previous = "APPLIQUER PRECEDENT", + cry_code_exploit = "EXPLOITER", + cry_code_exploit_previous = "EXPLOITER PRECEDENT", + cry_code_create = "CREER", + cry_code_create_previous = "CREER PRECEDENT", + cry_code_execute = "EXECUTER", + cry_code_cancel = "ANNULER", + + b_flip = "RETOURNER", + b_merge = "FUSIONNER", + + cry_hand_bulwark = "Muraille", + cry_hand_clusterfuck = "Foutoir", + cry_hand_ultpair = "Super Paire", + + cry_again_q = "Encore?", + cry_curse = "Malédiction", + cry_curse_ex = "Malédiction!", + cry_sobbing = "À l'aide...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Imposteur!", + cry_jolly_ex = "Plus joyeux!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Tour", + cry_plus_cryptid = "+1 Cryptide", + cry_no_triggers = "Pas de redéclenchements restants!", + cry_unredeemed = "Plus échangé...", + cry_active = "Actif", + cry_inactive = "Inactif", + + k_disable_music = "Désactiver la musique", + + k_cry_epic = "Épique", + k_cry_exotic = "Exotique", + k_cry_candy = "Bonbon", + k_cry_cursed = "Maudit", + k_planet_disc = "Disque Circumstellaire", + k_planet_satellite = "Satellites Naturels", + k_planet_universe = "Le putain d'univers", + + cry_notif_jimball_1 = "Jimboule", + cry_notif_jimball_2 = "Notice de droits d'auteur", + cry_notif_jimball_d1 = "Jimboule joue la musique \"Funkytown\",", + cry_notif_jimball_d2 = "qui est soumise aux droits d'auteur et ne peut", + cry_notif_jimball_d3 = "pas être utilisée dans des streams ou vidéos.", + }, + labels = { + food_jokers = "Jokers nourriture", + banana = "Banane", + code = "Code", + unique = "Unique", + cry_rigged = "Truqué", + cry_hooked = "Accroché", + cry_flickering = "Luisant", + cry_possessed = "Possédé", + + cry_green_seal = "Sceau vert", + cry_azure_seal = "Sceau azur", + + cry_astral = "Astral", + cry_blur = "Flou", + cry_double_sided = "Double-Face", + cry_glass = "Fragile", + cry_glitched = "Glitché", + cry_gold = "Doré", + cry_m = "Joyeux", + cry_mosaic = "Mosaïque", + cry_noisy = "Bruité", + cry_oversat = "Super-saturée", + + k_cry_epic = "Épique", + k_cry_exotic = "Exotique", + k_cry_candy = "Candy", + k_cry_cursed = "Cursed", + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Multi" }, + plus_chips = { "{C:blue}+#2#{} Jetons" }, + x_mult = { "{X:red,C:white} X#2#{} Multi" }, + x_chips = { "{X:blue,C:white} X#2#{} Jetons" }, + h_size = { "{C:attention}+#2#{} à la taille de main" }, + money = { "gagne {C:money}+$#2#{}" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Actuellement {C:red}+#1#{C:inactive} Multi)" }, + plus_chips = { "{C:inactive}(Actuellement {C:blue}+#1#{C:inactive} Jetons)" }, + x_mult = { "{C:inactive}(Actuellement {X:red,C:white} X#1# {C:inactive} Multi)" }, + x_chips = { "{C:inactive}(Actuellement {X:blue,C:white} X#1# {C:inactive} Jetons)" }, + h_size = { "{C:inactive}(Actuellement {C:attention}+#1#{C:inactive} taille de main)" }, + money = { "{C:inactive}(Actuellement {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Crée un {C:attention}#2# Joker{}" }, + make_tarot = { "Crée une carte {C:attention}#2#{C:tarot} Tarot" }, + make_planet = { "Crée une carte {C:attention}#2#{C:planet} Planète" }, + make_spectral = { "Crée une carte {C:attention}#2#{C:spectral} Spectrale{}" }, + add_dollars = { "Gagne {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "lorsqu'un {C:attention}paquet{} est ouvert" }, + buying_card = { "lorsqu'une carte est achetée" }, + selling_self = { "lorsque cette carte est vendue" }, + selling_card = { "lorsqu'une carte est vendue" }, + reroll_shop = { "lors d'un réassort" }, + ending_shop = { "à la fin de la {C:attention}boutique{}" }, + skip_blind = { "lorsqu'une {C:attention}blinde{} est passée" }, + skipping_booster = { "lorsque n'importe quel {C:attention}paquet{} est passé" }, + playing_card_added = { "à chaque fois qu'une {C:attention}carte à jouer{} est ajoutée à votre jeu" }, + first_hand_drawn = { "au début de la manche" }, + setting_blind = { "lorsque la {C:attention}Blinde{} est sélectionnée" }, + remove_playing_cards = { "lorsqu'une carte est détruite" }, + using_consumeable = { "lorsq'une carte {C:attention}consommable{} est utilisée" }, + debuffed_hand = { "si la {C:attention}main jouée{} n'est pas autorisée" }, + pre_discard = { "avant chaque défausse" }, + discard = { "pour chaque carte défaussée" }, + end_of_round = { "à la fin de {C:attention}la manche{}" }, + individual_play = { "pour chaque carte marquée" }, + individual_hand_score = { "pour chaque carte en main lors du compte" }, + individual_hand_end = { "pour chaque carte en main à la fin de la {C:attention}manche{}" }, + repetition_play = { "Redéclenche les cartes jouées" }, + repetition_hand = { "Redéclenche les cartes en main" }, + other_joker = { "pour chaque {C:attention}Joker{}" }, + before = { "avant chaque {C:attention}main{}" }, + after = { "après chaque {C:attention}main{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "si c'est un {C:attention}Joker{} {C:blue}Commun{}" }, + buy_uncommon = { "si c'est un {C:attention}Joker{} {C:blue}Peu commun{}" }, + tarot = { "si cette carte est une carte {C:tarot}Tarot{}" }, + planet = { "si cette carte est une carte {C:planet}Planète{}" }, + spectral = { "si cette carte est une carte {C:spectral}Spectrale{}" }, + joker = { "si cette carte est un {C:attention}Joker{}" }, + suit = { "si cette carte est un {V:1}#3#{}" }, + rank = { "si cette carte est un {C:attention}#3#{}" }, + face = { "si cette carte est une carte {C:attention}figure{}" }, + boss = { "si la {C:attention}blinde{} est une {C:attention}Blinde de Boss{}" }, + non_boss = { "si la {C:attention}blinde{} n'est pas une {C:attention}Blinde de Boss{}" }, + small = { "si la {C:attention}blinde{} est une {C:attention}Petite Blinde{}" }, + big = { "si la {C:attention}blinde{} est une {C:attention}Grosse Blinde{}" }, + first = { "si c'est la {C:attention}première main{}" }, + last = { "si c'est la {C:attention}dernière main{}" }, + common = { "si c'est un {C:attention}Joker {C:blue}Commun{}" }, + uncommon = { "si c'est un {C:attention}Joker{} {C:green}Peu commun{}" }, + rare = { "si c'est un {C:attention}Joker{} {C:red}Rare{}" }, + poker_hand = { "si la main contient une {C:attention}#3#{}" }, + or_more = { "si la main contient au moins {C:attention}#3#{} cartes" }, + or_less = { "si la main contient au plus {C:attention}#3#{} cartes" }, + hands_left = { "s'il reste #3# {C:blue}mains{} à la fin de la manche" }, + discards_left = { "s'il reste #3# {C:red}défausses{} à la fin de la manche" }, + first_discard = { "si c'est la {C:attention}première défausse{}" }, + last_discard = { "si c'est la {C:attention}dernière défausse{}" }, + odds = { "avec {C:green}#4# chances sur {C:green}#3#{}" }, + }, + }, + v_dictionary = { + a_xchips = { "X#1# Jetons" }, + a_powmult = { "^#1# Multi" }, + a_powchips = { "^#1# Jetons" }, + a_powmultchips = { "^#1# Multi+Jetons" }, + a_round = { "+#1# Manche(s)" }, + a_candy = { "+#1# bonbon" }, + a_xchips_minus = { "-X#1# Jetons" }, + a_powmult_minus = { "-^#1# Multi" }, + a_powchips_minus = { "-^#1# Jetons" }, + a_powmultchips_minus = { "-^#1# Multi+Jetons" }, + a_round_minus = { "-#1# Manche(s)" }, + + a_tag = { "#1# Badge" }, + a_tags = { "#1# Badges" }, + + cry_sticker_name = { "Sticker #1#" }, + cry_sticker_desc = { + "Vous avez remporté", + "la #2#Mise #1##3#", + "avec ce Joker" + }, + + cry_art = {"Graphismes : #1#"}, + cry_code = {"Code : #1#"}, + cry_idea = {"Idée : #1#"}, + }, + v_text = { + ch_c_cry_all_perishable = { "Tous les jokers sont {C:eternal}Périssables{}" }, + ch_c_cry_all_rental = { "Tous les jokers sont {C:eternal}en Location{}" }, + ch_c_cry_all_pinned = { "Tous les jokers sont {C:eternal}Épinglés{}" }, + ch_c_cry_all_banana = { "Tous les jokers sont {C:eternal}Bananes{}" }, + ch_c_all_rnj = { "Tous les jokers sont {C:attention}RNJoker{}" }, + ch_c_cry_sticker_sheet_plus = { "Tous les objets achetables ont tous les stickers" }, + ch_c_cry_rush_hour = { "Toutes les Blindes de Boss sont {C:attention}L'Horloge{} ou la {C:attention}Boucle Lavande" }, + ch_c_cry_rush_hour_ii = { "Toutes les blindes sont des {C:attention}Blindes de Boss{}" }, + ch_c_cry_rush_hour_iii = { "{C:attention}L'Horloge{} et la {C:attention}Boucle Lavande{} augmentent {C:attention}deux fois{} plus vite" }, + ch_c_cry_no_tags = { "Passer la blinde n'est {C:attention}plus possible{}" }, + ch_c_cry_no_vouchers = { "Les {C:attention}coupons{} n'apparaissent plus dans la boutique" }, + ch_c_cry_no_boosters = { "Les {C:attention}Paquets Booster{} n'apparaissent plus dans la boutique" }, + ch_c_cry_no_rerolls = { "Le réapprovisionnement n'est {C:attention}plus possible{}" }, + ch_c_cry_no_consumables = { "Les {C:attention}consommables{} n'apparaissent plus" } + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/id.lua b/Cryptid/localization/id.lua new file mode 100644 index 0000000..65b3b86 --- /dev/null +++ b/Cryptid/localization/id.lua @@ -0,0 +1,3314 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antimatter Deck", + text = { + "Applies the {C:legendary,E:1}upsides{}", + "of {C:attention}every{} deck", + }, + }, + b_cry_beta = { + name = "Nostalgic Deck", + text = { + "{C:attention}Joker{} and {C:attention}Consumable{}", + "slots are {C:attention}combined", + "{C:attention}Nostalgic{} Blinds replace", + "their updated Blind" + }, + }, + b_cry_blank = { + name = "Blank Deck", + text = { + "{C:inactive,E:1}Does nothing?", + }, + }, + b_cry_CCD = { + name = "CCD Deck", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + b_cry_conveyor = { + name = "Conveyor Deck", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + b_cry_critical = { + name = "Critical Deck", + text = { + "After each hand played,", + "{C:green}#1# in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}#1# in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + b_cry_encoded = { + name = "Encoded Deck", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + b_cry_equilibrium = { + name = "Deck of Equilibrium", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}Overstock Plus", + }, + }, + b_cry_glowing = { + name = "Glowing Deck", + text = { + "Multiply the values of", + "all Jokers by {X:dark_edition,C:white} X1.25 {}", + "when Boss Blind is defeated", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Infinite Deck", + text = { + "You can select {C:attention}any", + "number of cards", + "{C:attention}+1{} hand size", + }, + }, + b_cry_misprint = { + name = "Misprint Deck", + text = { + "Values of cards", + "and poker hands", + "are {C:attention}randomized", + }, + }, + b_cry_redeemed = { + name = "Redeemed Deck", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + b_cry_very_fair = { + name = "Very Fair Deck", + text = { + "{C:blue}-2{} hands, {C:red}-2{} discards", + "every round", + "{C:attention}Vouchers{} no longer", + "appear in the shop", + }, + }, + b_cry_wormhole = { + name = "Wormhole Deck", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + b_cry_legendary = { + name = "Legendary Deck", + text = { + "Start with an {C:legendary}Legendary{C:legendary} Joker", + "{C:green}1 in 5{} chance to create another", + "when Boss Blind is defeated {C:inactive}(must have room){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "The Box", + text = { + "All Common Jokers", + "are debuffed", + }, + }, + bl_cry_clock = { + name = "The Clock", + text = { + "+0.1X blind requirements every", + "3 seconds spent this ante", + }, + }, + bl_cry_hammer = { + name = "The Hammer", + text = { + "All cards with odd", + "rank are debuffed", + }, + }, + bl_cry_joke = { + name = "The Joke", + text = { + "If score is >2X requirements,", + "set ante to multiple of #1#", + }, + }, + bl_cry_magic = { + name = "The Magic", + text = { + "All cards with even", + "rank are debuffed", + }, + }, + bl_cry_lavender_loop = { + name = "Lavender Loop", + text = { + "1.25X blind requirements every", + "1.5 seconds spent this round", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidian Orb", + text = { + "Applies abilities of", + "all defeated bosses", + }, + }, + bl_cry_oldarm = { + name = "Nostalgic Arm", + text = { + "Must play 4", + "or fewer cards", + }, + }, + bl_cry_oldfish = { + name = "Nostalgic Fish", + text = { + "All hands start", + "with 1 Mult", + }, + }, + bl_cry_oldflint = { + name = "Nostalgic Flint", + text = { + "No Flushes", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgic House", + text = { + "No Full Houses", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgic Manacle", + text = { + "Divide Mult by discards", + }, + }, + bl_cry_oldmark = { + name = "Nostalgic Mark", + text = { + "No hands that", + "contain a Pair", + }, + }, + bl_cry_oldox = { + name = "Nostalgic Ox", + text = { + "All hands start", + "with 0 Chips", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgic Pillar", + text = { + "No Straights", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgic Serpent", + text = { + "Divide Mult by level", + "of played poker hand", + }, + }, + bl_cry_pin = { + name = "The Pin", + text = { + "Jokers with Epic or higher", + "rarity are debuffed", + }, + }, + bl_cry_pinkbow = { + name = "Pink Bow", + text = { + "Randomize rank of cards", + "held in hand on play", + }, + }, + bl_cry_sapphire_stamp = { + name = "Sapphire Stamp", + text = { + "Select an extra card, deselect", + "random card before scoring", + }, + }, + bl_cry_shackle = { + name = "The Shackle", + text = { + "All Negative Jokers", + "are debuffed", + }, + }, + bl_cry_striker = { + name = "The Striker", + text = { + "All Rare Jokers", + "are debuffed", + }, + }, + bl_cry_tax = { + name = "The Tax", + text = { + "Score per hand capped at", + "0.4X blind requirements", + }, + }, + bl_cry_tornado = { + name = "Turquoise Tornado", + text = { + "#1# in #2# chance for", + "played hand to not score", + }, + }, + bl_cry_trick = { + name = "The Trick", + text = { + "After each hand, flip all", + "face-up cards held in hand", + }, + }, + bl_cry_vermillion_virus = { + name = "Vermillion Virus", + text = { + "One random Joker", + "replaced every hand", + }, + }, + bl_cry_windmill = { + name = "The Windmill", + text = { + "All Uncommon Jokers", + "are debuffed", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Convert {C:cry_code}#1#{} selected card", + "to a {C:cry_code}chosen{} enhancement", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}new{} Joker", + "of the {C:cry_code}same rarity", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Don't.", + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Permanently{} remove a", + "{C:cry_code}selected{} shop item", + "{C:inactive,s:0.8}Item cannot appear again this run", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "{C:cry_code}Halve{} all listed prices", + "in current shop", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "The {C:cry_code}next{} hand played", + "is calculated as a", + "{C:cry_code}chosen{} poker hand", + "{C:inactive,s:0.8}Secret hands must be", + "{C:inactive,s:0.8}discovered to be valid", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Select two Jokers", + "to become {C:cry_code}Hooked", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Add {C:dark_edition}Glitched{} to all", "cards {C:cry_code}held in hand" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Merge a selected {C:cry_code}consumable", + "with a selected {C:cry_code}playing card", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Double{} all values of", + "a selected {C:cry_code}Joker{} until", + "end of round", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Next defeated Blind", + "gives {C:cry_code}X#1#{} interest", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:cry_code}#1#{} extra card and", + "{C:cry_code}#1#{} extra choice", + "{C:inactive}(Currently {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Replenish {C:blue}Hands{} and {C:red}Discards{},", + "return {C:cry_code}all{} cards to deck", + "and draw a {C:cry_code}new{} hand", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Set {C:cry_code}game state{} to", + "start of {C:cry_code}this Ante{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}Rework Tag{} with", + "an {C:cry_code}upgraded{} edition", + "{C:inactive,s:0.8}Upgrades using order in the Collection", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Visit a {C:cry_code}shop", + "during a {C:cry_code}Blind", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Select a Joker", + "or playing card", + "to become {C:cry_code}Rigged", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Ends current non-Boss {C:cry_code}Blind{}", "{C:cry_code}without{} cashing out" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Create a {C:cry_code}Glitched", + "Food Joker", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convert {C:cry_code}#1#{} selected cards", + "to a {C:cry_code}chosen{} rank", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} Mult", + }, + }, + e_cry_blur = { + name = "Blurred", + text = { + "{C:attention}Retrigger{} this", + "card {C:attention}1{} time", + "{C:green}#1# in #2#{} chance", + "to retrigger {C:attention}#3#{}", + "additional time", + }, + }, + e_cry_double_sided = { + name = "Double-Sided", + text = { + "This card can be", + "{C:attention}flipped{} to reveal", + "a different card", + "{C:inactive}(Blank side can be merged", + "{C:inactive}with another card)", + }, + }, + e_cry_glass = { + name = "Fragile", + label = "Fragile", + text = { + "{C:white,X:mult} X#3# {} Mult", + "{C:green}#1# in #2#{} chance this", + "card isn't {C:red}destroyed", + "when triggered", + }, + }, + e_cry_glitched = { + name = "Glitched", + text = { + "All values on this card", + "are {C:dark_edition}randomized{}", + "between {C:attention}X0.1{} and {C:attention}X10{}", + "{C:inactive}(If possible){}", + }, + }, + e_cry_gold = { + name = "Golden", + label = "Golden", + text = { + "Earn {C:money}$#1#{} when used", + "or triggered", + }, + }, + e_cry_m = { + name = "Jolly", + text = { + "{C:mult}+#1#{} Mult", + "This card is feeling", + "rather {C:attention}jolly{}", + }, + }, + e_cry_mosaic = { + name = "Mosaic", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + e_cry_noisy = { + name = "Noisy", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Oversaturated", + text = { + "All values", + "on this card", + "are {C:attention}doubled{}", + "{C:inactive}(If possible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Echo Card", + text = { + "{C:green}#2# in #3#{} chance to", + "{C:attention}retrigger{} #1# additional", + "times when scored", + }, + }, + }, + Joker = { + j_cry_altgoogol = { + name = "Nostalgic Googol Play Card", + text = { + "Sell this card to create", + "{C:attention}2{} copies of the leftmost {C:attention}Joker{}", + "{C:inactive,s:0.8}Does not copy Nostalgic Googol Play Cards{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Like Antennas to Heaven", + text = { + "This Joker gains", + "{X:chips,C:white} X#1# {} Chips when each", + "played {C:attention}7{} or {C:attention}4{} is scored", + "{C:inactive}(Currently {X:chips,C:white}X#2# {C:inactive} Chips)", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "{X:mult,C:white} X#1# {} Mult against {C:attention}Boss Blinds{}" }, + }, + j_cry_big_cube = { + name = "Big Cube", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_biggestm = { + name = "Huge", + text = { + "{X:mult,C:white} X#1# {} Mult until end", + "of round if {C:attention}poker hand{}", + "is a {C:attention}#2#{}", + "{C:inactive}(Currently {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}not fat, just big boned.", + }, + }, + j_cry_blender = { + name = "Blender", + text = { + "Create a {C:attention}random{}", + "consumable when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_blurred = { + name = "Blurred Joker", + text = { + "Gain {C:blue}+#1#{} hand(s) when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Each {C:attention}Joker{} gives {C:chips}+#1#{} Chips", + "Increase amount by {C:chips}+#2#{} if", + "{C:attention} poker hand{} is a {C:attention}#3#{}", + "{C:inactive,s:0.8}Jolly Jokers give{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips instead{}", + }, + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Bonus{} card to increase", + "{C:attention}Joker{} or {C:attention}Consumable slots", + "by {C:dark_edition}1{} when scored", + "{C:red}Works twice per round", + "{C:inactive,s:0.8}(Equal chance for each){}", + }, + }, + j_cry_booster = { + name = "Booster Joker", + text = { + "{C:attention}+#1#{} Booster Pack slot", + "available in shop", + }, + }, + j_cry_boredom = { + name = "Boredom", + text = { + "{C:green}#1# in #2#{} chance to", + "{C:attention}retrigger{} each {C:attention}Joker{}", + "or {C:attention}played card{}", + "{C:inactive,s:0.8}Does not affect other Boredom{}", + }, + }, + j_cry_bubblem = { + name = "Bubble M", + text = { + "Create a {C:dark_edition}Foil {C:attention}Jolly Joker{}", + "if played hand contains", + "a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_busdriver = { + name = "Bus Driver", + text = { + "{C:green}#1# in #3#{} chance", + "for {C:mult}+#2#{} Mult", + "{C:green}1 in 4{} chance", + "for {C:mult}-#2#{} Mult", + }, + }, + j_cry_canvas = { + name = "Canvas", + text = { + "{C:attention}Retrigger{} all {C:attention}Jokers{} to the left", + "once for {C:attention}every{} non-{C:blue}Common{C:attention} Joker{}", + "to the right of this Joker", + }, + }, + j_cry_caramel = { + name = "Caramel", + text = { + "Each played card gives", + "{X:mult,C:white}X#1#{} Mult when scored", + "for the next {C:attention}#2#{} rounds", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Retrigger {C:attention}leftmost{} Joker", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_chili_pepper = { + name = "Chili Pepper", + text = { + "This Joker gains {X:mult,C:white} X#2# {} Mult", + "at end of round,", + "{C:red,E:2}self destructs{} after {C:attention}#3#{} rounds", + "{C:inactive}(Currently{} {X:mult,C:white} X#1# {} {C:inactive}Mult){}", + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Chips and {X:dark_edition,C:white}^#1#{} Mult", + "if {C:attention}exactly{} #2#", + "hands remaining", + }, + }, + j_cry_circus = { + name = "Circus", + text = { + "{C:red}Rare{} Jokers each give {X:mult,C:white} X#1# {} Mult", + "{C:cry_epic}Epic{} Jokers each give {X:mult,C:white} X#2# {} Mult", + "{C:legendary}Legendary{} Jokers each give {X:mult,C:white} X#3# {} Mult", + "{C:cry_exotic}Exotic{} Jokers each give {X:mult,C:white} X#4# {} Mult", + }, + }, + j_cry_CodeJoker = { + name = "Code Joker", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:cry_code}Code Card{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Earn between", + "{C:money}$#1#{} and {C:money}$#2#{} for", + "each Joker {C:attention}sold{}", + }, + }, + j_cry_compound_interest = { + name = "Compound Interest", + text = { + "Earn {C:money}#1#%{} of total money", + "at end of round,", + "increases by {C:money}#2#%{} per", + "consecutive payout", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "When a {C:cry_code}Code{} card is used,", + "{C:green}#1# in #2#{} chance to add a copy", + "to your consumable area", + "{C:inactive}(Must have room)", + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "per {C:attention}reroll{} in the shop", + "{C:green}All rerolls are free{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Chain", + text = { + "Sell this card to", + "add {C:money}$#1#{} of {C:attention}sell value{}", + "to every {C:attention}Joker{} card", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob = { + name = "Sob", + text = { + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}run...{}", + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}hide...{}", + "{C:dark_edition,E:1}you cannot escape...{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "for each card {C:attention}purchased{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Cut", + text = { + "This Joker destroys", + "a random {C:cry_code}Code{} card", + "and gains {X:mult,C:white} X#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Delirious Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "Doodle M", + text = { + "Create 2 {C:dark_edition}Negative{} {C:attention}consumables{}", + "when {C:attention}Blind{} is selected", + "Create 1 more {C:attention}consumable", + "for each {C:attention}Jolly Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Scale", + text = { + "Scaling {C:attention}Jokers{}", + "scale {C:attention}quadratically", + "{C:inactive,s:0.8}(ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grows by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult for", + "each played, {C:attention}nonscoring{} {V:1}#2#{} card,", + "suit changes every round", + "{C:inactive}(Currently {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubious Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_duos = { + name = "The Duos", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Every {C:attention}Joker{} gives", + "{X:dark_edition,C:white}^#1#{} Mult" + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Draw {C:green}full deck{} to hand", + "when {C:attention}Blind{} is selected", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "When a {C:attention}Tag{} is acquired,", + "create {C:attention}#1#{} copies of it", + "and {C:attention}increase{} the number of", + "copies by {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Jokers appear using the", + "order from the {C:attention}Collection{}", + "Create {C:attention}#1#{} {C:dark_edition}Negative{} Joker(s)", + "when hand is played", + "{C:cry_exotic,s:0.8}Exotic {C:inactive,s:0.8}or better Jokers cannot appear", + "{s:0.8}Last Joker Generated: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Eternal Flame", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "for each card {C:attention}sold{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographic{} cards", + "each give {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "when {X:red,C:white} XMult {} is triggered", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult if", + "played cards are scored", + "{C:attention}#2#{} or fewer times", + }, + }, + j_cry_filler = { + name = "The Filler", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fractal Fingers", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + j_cry_flip_side = { + name = "On the Flip Side", + text = { + "{C:dark_edition}Double-Sided{} Jokers use", + "their back side for effects", + "instead of the front side", + "{C:attention}Retrigger{} all {C:dark_edition}Double-Sided{} Jokers" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}self destructs{} in {C:attention}#2#{} round(s)", + "Increases by {C:attention}#3#{} round when", + "{C:attention}Jolly Joker{} is {C:attention}sold{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foxy = { + name = "Foxy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "if hand played is {C:attention}not{}", + "most played {C:attention}poker hand{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Earn {C:money}$#1#{} if {C:attention}played hand{}", + "contains an {C:attention}Ace{} and a {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} all values", + "of leftmost {C:attention}Joker", + "at end of round", + }, + }, + j_cry_giggly = { + name = "Absurd Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Earn {C:money}#1#%{} of total", + "money at end of round", + "Payout increases by {C:money}#2#%{}", + "when each played {C:attention}Gold{}", + "card is scored", + }, + }, + j_cry_googol_play = { + name = "Googol Play Card", + text = { + "{C:green}#1# in #2#{} chance for", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Create a random {C:attention}Joker{}", + "at end of round", + "Sell this card to", + "create a random {C:attention}Joker{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_happyhouse = { + name = "Happy House", + text = { + "{X:dark_edition,C:white}^#1#{} Mult only after", + "playing {C:attention}114{} hands{}", + "{C:inactive}(Currently #2#/114){}", + "{C:inactive,s:0.8}There is no place like home!{}", + }, + }, + j_cry_home = { + name = "The Home", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Earn {C:money}$#1#{} when", + "using a {C:attention}consumable{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Retrigger all cards played", + "{C:attention}#2#{} time(s),", + "each played card gives", + "{X:mult,C:white} X#1# {} Mult when scored", + }, + }, + j_cry_jimball = { + name = "Jimball", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "per {C:attention}consecutive{} hand played", + "while playing your", + "most played {C:attention}poker hand", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Jolly Joker?", + text = { + "Create a {C:dark_edition}Jolly{} Joker", + "when a Joker is {C:attention}sold{}", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{}", + "when a {C:attention}Type Mult{} or", + "{C:attention}Type Chips{} Joker is sold", + }, + }, + j_cry_kooky = { + name = "Kooky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty the Clown", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when", + "each played {C:attention}card{} is scored", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoscope", + text = { + "Add {C:dark_edition}Polychrome{} to", + "a random {C:attention}Joker{} when", + "{C:attention}Boss Blind{} is defeated", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Each played {C:attention}7{} or {C:attention}2{}", + "gives {X:mult,C:white}X#1#{} Mult when scored", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Give future copies of", + "this Joker {X:mult,C:white}X#1#{} Mult", + "at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Retrigger{} all Jokers", + "once for each {C:attention}Jolly{}", + "{C:attention}Joker{} sold this round", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Retrigger(s)){}", + "{C:inactive,s:0.8}There wasn't enough room...{}", + }, + }, + j_cry_lucky_joker = { + name = "Lucky Joker", + text = { + "Earn {C:money}$#1#{} every time a", + "{C:attention}Lucky{} card {C:green}successfully{}", + "triggers", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "All Jokers give", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "when {C:attention}Jolly Joker{} is sold", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:attention}Jolly Joker{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_macabre = { + name = "Macabre Joker", + text = { + "When {C:attention}Blind{} is selected,", + "destroys each {C:attention}Joker{} except", + "{C:legendary}M Jokers{} and {C:attention}Jolly Jokers{}", + "and create 1 {C:attention}Jolly Joker{}", + "for each destroyed card", + }, + }, + j_cry_magnet = { + name = "Fridge Magnet", + text = { + "Earn {C:money}$#1#{} at end of round", + "This earns {X:money,C:white} X#2# {} if there are", + "{C:attention}#3#{} or fewer {C:attention}Joker{} cards", + }, + }, + j_cry_manic = { + name = "Manic Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Retrigger all Jokers", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_maximized = { + name = "Maximized", + text = { + "All {C:attention}face{} cards", + "are considered {C:attention}Kings{},", + "all {C:attention}numbered{} cards", + "are considered {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Labyrinth", + text = { + "All hands are considered the", + "{C:attention}first hand{} of each round,", + "all discards are considered the", + "{C:attention}first discard{} of each round", + }, + }, + j_cry_Megg = { + name = "Megg", + text = { + "Sell this card to create", + "{C:attention}#2#{} Jolly #3#, increase", + "by {C:attention}#1#{} at end of round", + }, + }, + j_cry_membershipcard = { + name = "Membership Card", + text = { + "{X:mult,C:white}X#1#{} Mult for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Old Membership Card", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteor Shower", + text = { + "{C:dark_edition}Foil{} cards each", + "give {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by", + "{C:money}$#1#{} for each {C:attention}Jolly Joker{}", + "or {C:legendary}M Joker{} at", + "end of round", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if no {C:attention}discards{} were", + "used this round", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Monkey Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and permanently add {C:attention}ten times{}", + "its sell value to this {C:chips}Chips{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{} when", + "a card with an {C:attention}Edition{} is sold", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Create an {C:legendary}M Joker{} at end of round", + "Each {C:attention}Jolly Joker{} or {C:legendary}M Joker", + "gives {X:dark_edition,C:white}^#1#{} Mult", + "Increase amount by {X:dark_edition,C:white}^#2#{}", + "when a {C:attention}Jolly Joker{} is {C:attention}sold", + "{C:inactive,s:0.8}(Tredecim excluded)", + }, + }, + j_cry_mstack = { + name = "M Stack", + text = { + "Retrigger all cards played", + "once for every", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jolly Jokers{} sold", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} retriggers){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Mult{} card to create", + "a {C:spectral}Cryptid{} card when scored", + "{C:inactive}(Must have room)", + }, + }, + j_cry_negative = { + name = "Negative Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips if played hand", + "contains a {C:attention}6{} and a {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Night", + text = { + "{X:dark_edition,C:white}^#1#{} Mult on final", + "hand of round", + "{E:2,C:red}self destructs{} on", + "final hand of round", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Retrigger each played {C:attention}7{}", + "{C:attention:}#1#{} additional time(s)", + }, + }, + j_cry_notebook = { + name = "Notebook", + text = { + "{C:green} #1# in #2#{} chance to gain {C:dark_edition}+1{} Joker", + "slot per {C:attention}reroll{} in the shop", + "{C:green}Always triggers{} if there are", + "{C:attention}#5#{} or more {C:attention}Jolly Jokers{}", + "{C:red}Works once per round{}", + "{C:inactive}(Currently {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Number Blocks", + text = { + "Earn {C:money}$#1#{} at end of round", + "Increase payout by {C:money}$#2#{}", + "for each {C:attention}#3#{} held in hand,", + "rank changes every round", + }, + }, + j_cry_nuts = { + name = "The Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_oldblueprint = { + name = "Old Blueprint", + text = { + "Copies ability of", + "{C:attention}Joker{} to the right", + "{C:green}#1# in #2#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + j_cry_oldcandy = { + name = "Nostalgic Candy", + text = { + "Sell this card to", + "permanently gain", + "{C:attention}+#1#{} hand size", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgic Invisible Joker", + text = { + "{C:attention}Duplicate{} a random", + "{C:attention}Joker{} every {C:attention}4", + "Joker cards sold", + "{s:0.8}Nostalgic Invisible Joker Excluded{}", + "{C:inactive}(Currently #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panopticon", + text = { + "All hands are considered the", + "{C:attention}last hand{} of each round", -- +$4 + }, + }, + j_cry_pickle = { + name = "Pickle", + text = { + "When {C:attention}Blind{} is skipped, create", + "{C:attention}#1#{} Tags, reduced by", + "{C:red}#2#{} when {C:attention}Blind{} is selected", + }, + }, + j_cry_pirate_dagger = { + name = "Pirate Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "and gain {C:attention}one-fourth{} of", + "its sell value as {X:chips,C:white} XChips {}", + "{C:inactive}(Currently {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + j_cry_pot_of_jokes = { + name = "Pot of Jokes", + text = { + "{C:attention}#1#{} hand size,", + "increases by", + "{C:blue}#2#{} every round", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "if all cards in played hand are", + "{C:attention}Aces{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, or {C:attention}7s{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "If {C:attention}poker hand{} is a", + "{C:attention}Royal Flush{}, destroy scored", + "{C:attention}Queen{} and create a", + "{C:dark_edition}Negative {}{C:red}Rare{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "The Quintet", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Red Bloon", + text = { + "Earn {C:money}$#1#{} in {C:attention}#2#{} round(s)", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante when", + "{C:money}$#2#{} {C:inactive}($#3#){} spent", + "{s:0.8}Requirements increase", + "{C:attention,s:0.8}exponentially{s:0.8} per use", + "{C:money,s:0.8}Next increase: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "When a {C:attention}Joker{} is sold,", + "add its effects to", + "every other Joker", + "{C:inactive,s:0.8}Does not affect other Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fill all empty Joker slots {C:inactive}(Max 100){}", + "with {C:dark_edition}Holographic{} {C:attention}Jolly Jokers{} if", + "{C:attention}discarded poker hand{} is a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Randomize abilities each {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Create an {C:green}Uncommon{} Joker", + "and 3 {C:attention}Jolly Jokers{} when", + "a {C:spectral}Spectral{} card is used", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sapling", + text = { + "After scoring {C:attention}#2#{} {C:inactive}[#1#]{} Enhanced", + "cards, sell this card to", + "create an {C:cry_epic}Epic{} {C:attention}Joker{}", + "{C:inactive,s:0.8}Will create a {C:red,s:0.8}Rare{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}if {C:cry_epic,s:0.8}Epic{} {C:inactive,s:0.8}Jokers are disabled{}", + }, + }, + j_cry_savvy = { + name = "Savvy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Scaling {C:attention}Jokers{} scale", + "as a degree-{C:attention}#1#{} polynomial", + "raise degree by {C:attention}#2#{}", + "at end of round", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluded)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Tile", + text = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Jolly {C:green}Uncommon{} Joker", + "when hand is played", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Add a {C:attention}random seal{} to each card", + "scored on {C:attention}final hand{} of round", + }, + }, + j_cry_shrewd = { + name = "Shrewd Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Silly Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Tiny", + text = { + "Create a {C:cry_jolly}Double M", + "tag if {C:attention}poker hand{}", + "is a {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker slot", + "{C:attention}+#1#{} Booster Pack slot", + "{C:attention}+#1#{} hand size", + "{C:attention}+#1#{} consumable slot", + "{C:attention}+#1#{} card in shop", + }, + }, + j_cry_spaceglobe = { + name = "Celestial Globe", + text = { + "This Joker gains {X:chips,C:white}X#2#{} Chips", + "if {C:attention}poker hand{} is a {C:attention}#3#{},", + "Hand changes after increase{}", + "{C:inactive}(Currently{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Creates a {C:dark_edition}Negative{} copy", + "of a random {C:attention}Joker{}", + "at the end of the {C:attention}shop", + "{C:inactive,s:0.8}Does not copy other Speculo{}", + }, + }, + j_cry_stardust = { + name = "Stardust", + text = { + "{C:dark_edition}Polychrome{} cards", + "each give {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "This Joker destroys a", + "random {C:planet}Planet{} card", + "and gains {X:dark_edition,C:white} ^#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_subtle = { + name = "Subtle Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Earn {C:money}$#3#{} at", + "end of round", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "At end of round, create", + "a {C:attention}copy{} of a random", + "card {C:attention}held in hand{},", + "destroy all others", + "{C:attention,s:0.8}Kings{s:0.8} of {C:hearts,s:0.8}Hearts{s:0.8} are prioritized", + }, + }, + j_cry_swarm = { + name = "The Swarm", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Catalyst", + text = { + "Balances {C:chips}Chips{} and {C:mult}Mult{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + "Earn {C:money}$#2#{} at end of round", + }, + }, + j_cry_translucent = { + name = "Translucent Joker", + text = { + "Sell this card to create", + "a {C:attention}Banana Perishable{} copy", + "of a random {C:attention}Joker{}", + "{s:0.8,C:inactive}(Copy bypasses perish compat)", + }, + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Triplet Rhythm", + text = { + "{X:mult,C:white} X#1# {} Mult if scoring hand", + "contains {C:attention}exactly{} three {C:attention}3s", + }, + }, + j_cry_unity = { + name = "The Unity", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Poker hands{} gain", + "{X:red,C:white} X#1# {} Mult and {X:blue,C:white} X#1# {} Chips", + "when leveled up", + }, + }, + j_cry_unjust_dagger = { + name = "Unjust Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and gain {C:attention}one-fifth{} of", + "its sell value as {X:mult,C:white} XMult {}", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "This Joker gains {C:money}$#1#{} of {C:attention}sell value{}", + "if {C:attention}poker hand{} contains a {C:attention}#2#{}", + "Sell this card to create a", + "{C:dark_edition}Polychrome{} {C:attention}Jolly Joker{} for", + "every {C:money}$4{} of {C:attention}sell value{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Wacky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "All Jokers give", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "All Jokers give", + "{C:money}$#1#{} when triggered", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "This Joker gains", + "{C:mult}+#2#{} Mult when each played", + "{C:attention}Ace{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "is scored", + "{C:inactive}(Currently {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Retrigger each played {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} additional time(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Wheel of Hope", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when failing", + "a {C:attention}Wheel of Fortune{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if {C:attention}played hand{} contains a", + "{C:attention}2{} and {C:attention}7{} of different suits", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutron Star", + text = { + "Upgrade a random", + "poker hand by", + "{C:attention}1{} level for each", + "{C:attention}Neutron Star{} used", + "in this run", + "{C:inactive}(Currently{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# in #2#{} chance to", + "upgrade every", + "{C:legendary,E:1}poker hand{}", + "by {C:attention}1{} level", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "CCD Sleeve", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Conveyor Sleeve", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Critical Sleeve", + text = { + "After each hand played,", + "{C:green}1 in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}1 in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Encoded Sleeve", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balanced Sleeve", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}+2 Shop Slots", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unlimited Sleeve", + text = { + "You can select {C:attention}any", + "number of cards", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Misprinted Sleeve", + text = { + "Values of cards", + "are {C:attention}randomized", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Redeemed Sleeve", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Wormhole Sleeve", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + }, + Spectral = { + c_cry_analog = { + name = "Analog", + text = { + "Create {C:attention}#1#{} copies of a", + "random {C:attention}Joker{}, destroy", + "all other Jokers, {C:attention}+#2#{} Ante", + }, + }, + c_cry_gateway = { + name = "Gateway", + text = { + "Create a random", + "{C:cry_exotic,E:1}Exotic{C:attention} Joker{}, destroy", + "all other Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Lock", + text = { + "Remove {C:red}all{} stickers", + "from {C:red}all{} Jokers,", + "then apply {C:purple,E:1}Eternal{}", + "to a random {C:attention}Joker{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Create a card", + "of {C:cry_code}your choice", + "{C:inactive,s:0.8}(Exotic Jokers #1#excluded)", + }, + }, + c_cry_replica = { + name = "Replica", + text = { + "Convert all cards", + "held in hand", + "to a {C:attention}random{}", + "card held in hand", + }, + }, + c_cry_source = { + name = "Source", + text = { + "Add a {C:cry_code}Green Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_summoning = { + name = "Summoning", + text = { + "Create a random", + "{C:cry_epic}Epic{} {C:joker}Joker{}, destroy", + "one random {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Trade", + text = { + "{C:attention}Lose{} a random Voucher,", + "gain {C:attention}2{} random Vouchers", + }, + }, + c_cry_typhoon = { + name = "Typhoon", + text = { + "Add an {C:cry_azure}Azure Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_vacuum = { + name = "Vacuum", + text = { + "Removes {C:red}all {C:green}modifications{}", + "from {C:red}all{} cards held in hand,", + "Earn {C:money}$#1#{} per {C:green}modification{} removed", + "{C:inactive,s:0.7}(ex. Enhancements, Seals, Editions)", + }, + }, + c_cry_white_hole = { + name = "White Hole", + text = { + "{C:attention}Remove{} all hand levels,", + "upgrade {C:legendary,E:1}most played{} poker hand", + "by {C:attention}3{} for each removed level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pink Stake", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brown Stake", + colour = "Brown", + text = { + "All {C:attention}stickers{} are compatible", + "with each other", + }, + }, + stake_cry_yellow = { + name = "Yellow Stake", + colour = "Yellow", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Jade Stake", + colour = "Jade", + text = { + "Cards can be drawn {C:attention}face down{}", + }, + }, + stake_cry_cyan = { + name = "Cyan Stake", + colour = "Cyan", + text = { + "{C:green}Uncommon{} and {C:red}Rare{} Jokers are", + "less likely to appear", + }, + }, + stake_cry_gray = { + name = "Gray Stake", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Crimson Stake", + colour = "Crimson", + text = { + "Vouchers restock on {C:attention}even{} Antes", + }, + }, + stake_cry_diamond = { + name = "Diamond Stake", + colour = "Diamond", + text = { + "Must beat Ante {C:attention}10{} to win", + }, + }, + stake_cry_amber = { + name = "Amber Stake", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Pack slot", + }, + }, + stake_cry_bronze = { + name = "Bronze Stake", + colour = "Bronze", + text = { + "Vouchers are {C:attention}50%{} more expensive", + }, + }, + stake_cry_quartz = { + name = "Quartz Stake", + colour = "Quartz", + text = { + "Jokers can be {C:attention}Pinned{}", + "{s:0.8,C:inactive}(Stays pinned to the leftmost position){}", + }, + }, + stake_cry_ruby = { + name = "Ruby Stake", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds can become", + "{C:attention}Boss{} Blinds", + }, + }, + stake_cry_glass = { + name = "Glass Stake", + colour = "Glass", + text = { + "Cards can {C:attention}shatter{} when scored", + }, + }, + stake_cry_sapphire = { + name = "Sapphire Stake", + colour = "Sapphire", + text = { + "Lose {C:attention}25%{} of current money", + "at end of Ante", + "{s:0.8,C:inactive}(Up to $10){}", + }, + }, + stake_cry_emerald = { + name = "Emerald Stake", + colour = "Emerald", + text = { + "Cards, packs, and vouchers", + "can be {C:attention}face down{}", + "{s:0.8,C:inactive}(Unable to be viewed until purchased){}", + }, + }, + stake_cry_platinum = { + name = "Platinum Stake", + colour = "Platinum", + text = { + "Small Blinds are {C:attention}removed{}", + }, + }, + stake_cry_twilight = { + name = "Twilight Stake", + colour = "Twilight", + text = { + "Cards can be {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 in 10 chance of being destroyed each round){}", + }, + }, + stake_cry_verdant = { + name = "Verdant Stake", + colour = "Verdant", + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Ember Stake", + colour = "Ember", + text = { + "All items give no money when sold", + }, + }, + stake_cry_dawn = { + name = "Dawn Stake", + colour = "Dawn", + text = { + "Tarots and Spectrals target {C:attention}1", + "fewer card", + "{s:0.8,C:inactive}(Minimum of 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizon Stake", + colour = "Horizon", + text = { + "When blind selected, add a", + "{C:attention}random card{} to deck", + }, + }, + stake_cry_blossom = { + name = "Blossom Stake", + colour = "Blossom", + text = { + "{C:attention}Final{} Boss Blinds can appear", + "in {C:attention}any{} Ante", + }, + }, + stake_cry_azure = { + name = "Azure Stake", + colour = "Azure", + text = { + "Values on Jokers are reduced", + "by {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Ascendant Stake", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop slot", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Banana Tag", + text = { + "Creates {C:attention}#1#", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_bettertop_up = { + name = "Better Top-up Tag", + text = { + "Creates up to {C:attention}#1#", + "{C:green}Uncommon{} Jokers", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_better_voucher = { + name = "Golden Voucher Tag", + text = { + "Adds one Tier {C:attention}#1#{} Voucher", + "to the next shop", + }, + }, + tag_cry_blur = { + name = "Blurred Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Blurred{}", + }, + }, + tag_cry_booster = { + name = "Booster Tag", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:attention}double{} cards and", + "{C:attention}double{} choices", + }, + }, + tag_cry_bundle = { + name = "Bundle Tag", + text = { + "Create a {C:attention}Standard Tag{}, {C:tarot}Charm Tag{},", + "{C:attention}Buffoon Tag{}, and {C:planet}Meteor Tag", + }, + }, + tag_cry_cat = { + name = "Cat Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Console Tag", + text = { + "Gives a free", + "{C:cry_code}Program Pack", + }, + }, + tag_cry_double_m = { + name = "Double M Tag", + text = { + "Shop has a", + "{C:dark_edition}Jolly {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Empowered Tag", + text = { + "Gives a free {C:spectral}Spectral Pack", + "with {C:legendary,E:1}The Soul{} and {C:cry_exotic,E:1}Gateway{}", + }, + }, + tag_cry_epic = { + name = "Epic Tag", + text = { + "Shop has a half-price", + "{C:cry_epic}Epic Joker", + }, + }, + tag_cry_gambler = { + name = "Gambler's Tag", + text = { + "{C:green}#1# in #2#{} chance to create", + "an {C:cry_exotic,E:1}Empowered Tag", + }, + }, + tag_cry_glass = { + name = "Fragile Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Fragile{}", + }, + }, + tag_cry_glitched = { + name = "Glitched Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Glitched{}", + }, + }, + tag_cry_gold = { + name = "Golden Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Golden{}", + }, + }, + tag_cry_gourmand = { + name = "Gourmand Tag", + text = { + "Shop has a free", + "{C:attention}Food Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gives a free", + "{C:cry_ascendant}Meme Pack", + }, + }, + tag_cry_m = { + name = "Jolly Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Jolly{}", + }, + }, + tag_cry_memory = { + name = "Memory Tag", + text = { + "Create {C:attention}#1#{} copies of", + "the last {C:attention}Tag{} used", + "during this run", + "{s:0.8,C:inactive}Copying Tags excluded", + "{s:0.8,C:inactive}Currently: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Mosaic{}", + }, + }, + tag_cry_oversat = { + name = "Oversaturated Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Oversaturated{}", + }, + }, + tag_cry_quadruple = { + name = "Quadruple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_quintuple = { + name = "Quintuple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_rework = { + name = "Rework Tag", + text = { + "Shop has a(n)", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schematic Tag", + text = { + "Shop has a", + "{C:attention}Brainstorm", + }, + }, + tag_cry_scope = { + name = "Scope Tag", + text = { + "{C:attention}+#1# {C:blue}hands{} and", + "{C:red}discards{} next round", + }, + }, + tag_cry_triple = { + name = "Triple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "The Automaton", + text = { + "Creates up to {C:attention}#1#", + "random {C:cry_code}Code{} card", + "{C:inactive}(Must have room)", + }, + }, + c_cry_eclipse = { + name = "The Eclipse", + text = { + "Enhances {C:attention}#1#{} selected card", + "into an {C:attention}Echo Card", + }, + }, + c_cry_meld = { + name = "Meld", + text = { + "Select a {C:attention}Joker{} or", + "{C:attention}playing card{} to", + "become {C:dark_edition}Double-Sided", + }, + }, + c_cry_theblessing = { + name = "The Blessing", + text = { + "Creates {C:attention}1{}", + "random {C:attention}consumable{}", + "{C:inactive}(Must have room){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglyph", + text = { + "Set Ante to {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Blank Canvas", + text = { + "{C:attention}+#1#{} hand size", + }, + }, + v_cry_clone_machine = { + name = "Clone Machine", + text = { + "Double Tags become", + "{C:attention}Quintuple Tags{} and", + "are {C:attention}4X{} as common", + }, + }, + v_cry_command_prompt = { + name = "Command Prompt", + text = { + "{C:cry_code}Code{} cards", + "can appear", + "in the {C:attention}shop{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Double Tags become", + "{C:attention}Triple Tags{} and are", + "{C:attention}2X{} as common", + }, + }, + v_cry_curate = { + name = "Curate", + text = { + "All cards", + "appear with", + "an {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Dexterity", + text = { + "Permanently", + "gain {C:blue}+#1#{} hand(s)", + "each round", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "After every round,", + "{X:dark_edition,C:white} X1.5 {} to all values", + "on the back of", + "{C:dark_edition}Double-Sided{} cards" + }, + }, + v_cry_double_slit = { + name = "Double Slit", + text = { + "{C:attention}Meld{} can appear", + "in the shop and", + "Arcana Packs", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "{C:dark_edition}Double-Sided{} cards appear", + "{C:attention}4X{} more frequently", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker slot(s)", + }, + }, + v_cry_massproduct = { + name = "Mass Production", + text = { + "All cards and packs", + "in shop cost {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Raise the cap on", + "interest earned in", + "each round to {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multistock", + text = { + "{C:attention}+#1#{} card slot(s) and", + "{C:attention}+#1#{} booster pack slot(s)", + "available in shop", + }, + }, + v_cry_pacclimator = { + name = "Planet Acclimator", + text = { + "{C:planet}Planet{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:planet}Planet{}", + "cards are {C:green}free{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Retrigger{} all M Jokers", + "once for every Pair", + "{C:attention}contained{} in played hand", + }, + }, + v_cry_pairing = { + name = "Pairing", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand is a {C:attention}Pair", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computing", + text = { + "{C:cry_code}Code{} cards can spawn", + "with {C:dark_edition}Negative{} edition", + }, + }, + v_cry_repair_man = { + name = "Repair Man", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand contains a {C:attention}Pair", + }, + }, + v_cry_rerollexchange = { + name = "Reroll Exchange", + text = { + "All rerolls", + "cost {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satellite Uplink", + text = { + "{C:cry_code}Code{} cards may", + "appear in any of", + "the {C:attention}Celestial Packs{}", + }, + }, + v_cry_scope = { + name = "Galactic Scope", + text = { + "Create the {C:planet}Planet", + "card for played", + "{C:attention}poker hand{}", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Tarot Acclimator", + text = { + "{C:tarot}Tarot{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:tarot}Tarot{}", + "cards are {C:green}free{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Printer", + text = { + "Double Tags become", + "{C:attention}Quadruple Tags{} and", + "are {C:attention}3X{} as common", + }, + }, + v_cry_threers = { + name = "The 3 Rs", + text = { + "Permanently", + "gain {C:red}+#1#{} discard(s)", + "each round", + }, + }, + v_cry_stickyhand = { + name = "Sticky Hand", + text = { + "{C:attention}+#1#{} card", + "selection limit", + }, + }, + v_cry_grapplinghook = { + name = "Grappling Hook", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperspace Tether", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "destroyed each round", + }, + }, + cry_rigged = { + name = "Rigged", + text = { + "All {C:cry_code}listed{} probabilities", + "are {C:cry_code}guaranteed", + }, + }, + cry_hooked = { + name = "Hooked", + text = { + "When this Joker is {C:cry_code}triggered{},", + "trigger {C:cry_code}#1#", + }, + }, + food_jokers = { + name = "Food Jokers", + text = { + "{s:0.8}Gros Michel, Egg, Ice Cream, Cavendish,", + "{s:0.8}Turtle Bean, Diet Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Pickle, Chili Pepper, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eternal", + text = { + "All cards in pack", + "are {C:attention}Eternal{}", + }, + }, + cry_perishable_booster = { + name = "Perishable", + text = { + "All cards in pack", + "are {C:attention}Perishable{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "All cards in pack", + "are {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Pinned", + text = { + "All cards in pack", + "are {C:attention}Pinned{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "All cards in pack", + "are {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eternal", + text = { + "Can't be traded", + }, + }, + cry_perishable_voucher = { + name = "Perishable", + text = { + "Debuffed after", + "{C:attention}#1#{} rounds", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)", + }, + }, + cry_rental_voucher = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at", + "end of round", + }, + }, + cry_pinned_voucher = { + name = "Pinned", + text = { + "Remains in shop", + "until redeemed", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "unredeemed each round", + }, + }, + cry_perishable_consumeable = { + name = "Perishable", + text = { + "Debuffed at", + "end of round", + }, + }, + cry_rental_consumeable = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at end of", + "round, and on use", + }, + }, + cry_pinned_consumeable = { + name = "Pinned", + text = { + "Can't use other", + "non-{C:attention}Pinned{} consumables", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance to do", + "nothing on use", + }, + }, + p_cry_code_normal_1 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_normal_2 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_two = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_three = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + undiscovered_code = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + cry_green_seal = { + name = "Green Seal", + text = { + "Creates a {C:cry_code}Code{} card", + "when played and unscoring", + "{C:inactive}(Must have room)", + }, + }, + cry_azure_seal = { + name = "Azure Seal", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planets{} for played", + "{C:attention}poker hand{}, then", + "{C:red}destroy{} this card", + }, + }, + }, + }, + misc = { + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Legally Blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Break Infinity", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Home Realtor", + ach_cry_jokes_on_you = "Joke's on You, Pal!", + ach_cry_niw_uoy = "!niW uoY", + ach_cry_now_the_fun_begins = "Now the Fun Begins", + ach_cry_patience_virtue = "Patience is a Virtue", + ach_cry_perfectly_balanced = "Perfectly Balanced", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Traffic Jam", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "We Told You Not To", + ach_cry_what_have_you_done = "WHAT HAVE YOU DONE?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtain Blurred Blurred Joker", + ach_cry_bullet_hell = "Have 15 AP Jokers", + ach_cry_break_infinity = "Score 1.79e308 Chips in a single hand", + ach_cry_cryptid_the_cryptid = "Use Cryptid on Cryptid", + ach_cry_exodia = "Have 5 Exotic Jokers", + ach_cry_freak_house = "Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice", + ach_cry_googol_play_pass = "Rig a Googol Play Card", + ach_cry_haxxor = "Use a cheat code", + ach_cry_home_realtor = "Activate Happy House before Ante 8 (without DoE/Antimatter)", + ach_cry_jokes_on_you = "Trigger The Joke's effect on Ante 1 and win the run", + ach_cry_niw_uoy = "Reach Ante -8", + ach_cry_now_the_fun_begins = "Obtain Canvas", + ach_cry_patience_virtue = "Wait out Lavender Loop for 2 minutes before playing first hand and beat the blind", + ach_cry_perfectly_balanced = "Beat Very Fair Deck on Ascendant Stake", + ach_cry_pull_request = "Have ://COMMIT spawn the same Joker that it destroyed", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Win in 1 round", + ach_cry_used_crash = "Use ://CRASH", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Music", + cry_set_enable_features = "Select features to enable (applies on game restart):", + cry_feat_achievements = "Achievements", + ["cry_feat_antimatter deck"] = "Antimatter Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Code Cards", + ["cry_feat_misc. decks"] = "Misc. Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechanics", + ["cry_feat_enhanced decks"] = "Enhanced Decks", + ["cry_feat_epic jokers"] = "Epic Jokers", + ["cry_feat_exotic jokers"] = "Exotic Jokers", + ["cry_feat_m jokers"] = "M Jokers", + cry_feat_menu = "Custom Main Menu", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Misc. Jokers", + cry_feat_planets = "Planets", + cry_feat_jokerdisplay = "JokerDisplay (Does Nothing)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Sleeves", + cry_feat_spectrals = "Spectrals", + ["cry_feat_more stakes"] = "Stakes", + cry_feat_vouchers = "Vouchers", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Pack", + k_cry_meme_pack = "Meme Pack", + + cry_critical_hit_ex = "Critical Hit!", + cry_critical_miss_ex = "Critical Miss!", + + cry_debuff_oldhouse = "No Full Houses", + cry_debuff_oldarm = "Must play 4 or fewer cards", + cry_debuff_oldpillar = "No Straights", + cry_debuff_oldflint = "No Flushes", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applies abilities of all defeated bosses", + + k_code = "Code", + b_code_cards = "Code Cards", + b_pull = "PULL", + cry_hooked_ex = "Hooked!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTER RANK", + cry_code_enh = "ENTER ENHANCEMENT", + cry_code_hand = "ENTER POKER HAND", + cry_code_enter_card = "ENTER A CARD", + cry_code_apply = "APPLY", + cry_code_apply_previous = "APPLY PREVIOUS", + cry_code_exploit = "EXPLOIT", + cry_code_exploit_previous = "EXPLOIT PREVIOUS", + cry_code_create = "CREATE", + cry_code_create_previous = "CREATE PREVIOUS", + cry_code_execute = "EXECUTE", + cry_code_cancel = "CANCEL", + + b_flip = "FLIP", + b_merge = "MERGE", + + cry_again_q = "Again?", + cry_curse = "Curse", + cry_curse_ex = "Curse!", + cry_sobbing = "Help me...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Round", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "No triggers left!", + cry_unredeemed = "Unredeemed...", + cry_active = "Active", + cry_inactive = "Inactive", + + k_disable_music = "Disable Music", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Copyright Notice", + cry_notif_jimball_d1 = "Jimball plays the song \"Funkytown\",", + cry_notif_jimball_d2 = "which is copyrighted and can't be", + cry_notif_jimball_d3 = "used for streams and videos.", + }, + labels = { + food_jokers = "Food Jokers", + banana = "Banana", + code = "Code", + cry_rigged = "Rigged", + cry_hooked = "Hooked", + + cry_green_seal = "Green Seal", + cry_azure_seal = "Azure Seal", + + cry_astral = "Astral", + cry_blur = "Blurred", + cry_double_sided = "Double-Sided", + cry_glass = "Fragile", + cry_glitched = "Glitched", + cry_gold = "Golden", + cry_m = "Jolly", + cry_mosaic = "Mosaic", + cry_noisy = "Noisy", + cry_oversat = "Oversaturated", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic" + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + }, + v_text = { + ch_c_cry_all_perishable = {"All Jokers are {C:eternal}Perishable{}"}, + ch_c_cry_all_rental = {"All Jokers are {C:eternal}Rental{}"}, + ch_c_cry_all_pinned = {"All Jokers are {C:eternal}Pinned{}"}, + ch_c_cry_all_banana = {"All Jokers are {C:eternal}Banana{}"}, + ch_c_all_rnj = {"All Jokers are {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"All purchasable items have all stickers"}, + ch_c_cry_rush_hour = {"All Boss Blinds are {C:attention}The Clock{} or {C:attention}Lavender Loop"}, + ch_c_cry_rush_hour_ii = {"All Blinds are {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}The Clock{} and {C:attention}Lavender Loop{} scale {C:attention}twice{} as fast"}, + ch_c_cry_no_tags = {"Skipping is {C:attention}disabled{}"}, + ch_c_cry_no_vouchers = {"{C:attention}Vouchers{} no longer appear in the shop"}, + ch_c_cry_no_boosters = {"{C:attention}Booster Packs{} no longer appear in the shop"}, + ch_c_cry_no_rerolls = {"Rerolling is {C:attention}disabled{}"}, + ch_c_cry_no_consumables = {"{C:attention}Consumables{} no longer appear"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/ja.lua b/Cryptid/localization/ja.lua new file mode 100644 index 0000000..c254527 --- /dev/null +++ b/Cryptid/localization/ja.lua @@ -0,0 +1,3314 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antimatter Deck", + text = { + "Applies the {C:legendary,E:1}upsides{}", + "of {C:attention}every{} deck", + }, + }, + b_cry_beta = { + name = "Nostalgic Deck", + text = { + "{C:attention}Joker{} and {C:attention}Consumable{}", + "slots are {C:attention}combined", + "{C:attention}Nostalgic{} Blinds replace", + "their updated Blind" + }, + }, + b_cry_blank = { + name = "Blank Deck", + text = { + "{C:inactive,E:1}Does nothing?", + }, + }, + b_cry_CCD = { + name = "CCD Deck", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + b_cry_conveyor = { + name = "Conveyor Deck", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + b_cry_critical = { + name = "Critical Deck", + text = { + "After each hand played,", + "{C:green}#1# in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}#1# in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + b_cry_encoded = { + name = "Encoded Deck", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + b_cry_equilibrium = { + name = "Deck of Equilibrium", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}Overstock Plus", + }, + }, + b_cry_glowing = { + name = "Glowing Deck", + text = { + "Multiply the values of", + "all Jokers by {X:dark_edition,C:white} X1.25 {}", + "when Boss Blind is defeated", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Infinite Deck", + text = { + "You can select {C:attention}any", + "number of cards", + "{C:attention}+1{} hand size", + }, + }, + b_cry_misprint = { + name = "Misprint Deck", + text = { + "Values of cards", + "and poker hands", + "are {C:attention}randomized", + }, + }, + b_cry_redeemed = { + name = "Redeemed Deck", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + b_cry_very_fair = { + name = "Very Fair Deck", + text = { + "{C:blue}-2{} hands, {C:red}-2{} discards", + "every round", + "{C:attention}Vouchers{} no longer", + "appear in the shop", + }, + }, + b_cry_wormhole = { + name = "Wormhole Deck", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + b_cry_legendary = { + name = "Legendary Deck", + text = { + "Start with an {C:legendary}Legendary{C:legendary} Joker", + "{C:green}1 in 5{} chance to create another", + "when Boss Blind is defeated {C:inactive}(must have room){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "The Box", + text = { + "All Common Jokers", + "are debuffed", + }, + }, + bl_cry_clock = { + name = "The Clock", + text = { + "+0.1X blind requirements every", + "3 seconds spent this ante", + }, + }, + bl_cry_hammer = { + name = "The Hammer", + text = { + "All cards with odd", + "rank are debuffed", + }, + }, + bl_cry_joke = { + name = "The Joke", + text = { + "If score is >2X requirements,", + "set ante to multiple of #1#", + }, + }, + bl_cry_magic = { + name = "The Magic", + text = { + "All cards with even", + "rank are debuffed", + }, + }, + bl_cry_lavender_loop = { + name = "Lavender Loop", + text = { + "1.25X blind requirements every", + "1.5 seconds spent this round", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidian Orb", + text = { + "Applies abilities of", + "all defeated bosses", + }, + }, + bl_cry_oldarm = { + name = "Nostalgic Arm", + text = { + "Must play 4", + "or fewer cards", + }, + }, + bl_cry_oldfish = { + name = "Nostalgic Fish", + text = { + "All hands start", + "with 1 Mult", + }, + }, + bl_cry_oldflint = { + name = "Nostalgic Flint", + text = { + "No Flushes", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgic House", + text = { + "No Full Houses", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgic Manacle", + text = { + "Divide Mult by discards", + }, + }, + bl_cry_oldmark = { + name = "Nostalgic Mark", + text = { + "No hands that", + "contain a Pair", + }, + }, + bl_cry_oldox = { + name = "Nostalgic Ox", + text = { + "All hands start", + "with 0 Chips", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgic Pillar", + text = { + "No Straights", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgic Serpent", + text = { + "Divide Mult by level", + "of played poker hand", + }, + }, + bl_cry_pin = { + name = "The Pin", + text = { + "Jokers with Epic or higher", + "rarity are debuffed", + }, + }, + bl_cry_pinkbow = { + name = "Pink Bow", + text = { + "Randomize rank of cards", + "held in hand on play", + }, + }, + bl_cry_sapphire_stamp = { + name = "Sapphire Stamp", + text = { + "Select an extra card, deselect", + "random card before scoring", + }, + }, + bl_cry_shackle = { + name = "The Shackle", + text = { + "All Negative Jokers", + "are debuffed", + }, + }, + bl_cry_striker = { + name = "The Striker", + text = { + "All Rare Jokers", + "are debuffed", + }, + }, + bl_cry_tax = { + name = "The Tax", + text = { + "Score per hand capped at", + "0.4X blind requirements", + }, + }, + bl_cry_tornado = { + name = "Turquoise Tornado", + text = { + "#1# in #2# chance for", + "played hand to not score", + }, + }, + bl_cry_trick = { + name = "The Trick", + text = { + "After each hand, flip all", + "face-up cards held in hand", + }, + }, + bl_cry_vermillion_virus = { + name = "Vermillion Virus", + text = { + "One random Joker", + "replaced every hand", + }, + }, + bl_cry_windmill = { + name = "The Windmill", + text = { + "All Uncommon Jokers", + "are debuffed", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Convert {C:cry_code}#1#{} selected card", + "to a {C:cry_code}chosen{} enhancement", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}new{} Joker", + "of the {C:cry_code}same rarity", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Don't.", + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Permanently{} remove a", + "{C:cry_code}selected{} shop item", + "{C:inactive,s:0.8}Item cannot appear again this run", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "{C:cry_code}Halve{} all listed prices", + "in current shop", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "The {C:cry_code}next{} hand played", + "is calculated as a", + "{C:cry_code}chosen{} poker hand", + "{C:inactive,s:0.8}Secret hands must be", + "{C:inactive,s:0.8}discovered to be valid", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Select two Jokers", + "to become {C:cry_code}Hooked", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Add {C:dark_edition}Glitched{} to all", "cards {C:cry_code}held in hand" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Merge a selected {C:cry_code}consumable", + "with a selected {C:cry_code}playing card", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Double{} all values of", + "a selected {C:cry_code}Joker{} until", + "end of round", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Next defeated Blind", + "gives {C:cry_code}X#1#{} interest", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:cry_code}#1#{} extra card and", + "{C:cry_code}#1#{} extra choice", + "{C:inactive}(Currently {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Replenish {C:blue}Hands{} and {C:red}Discards{},", + "return {C:cry_code}all{} cards to deck", + "and draw a {C:cry_code}new{} hand", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Set {C:cry_code}game state{} to", + "start of {C:cry_code}this Ante{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}Rework Tag{} with", + "an {C:cry_code}upgraded{} edition", + "{C:inactive,s:0.8}Upgrades using order in the Collection", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Visit a {C:cry_code}shop", + "during a {C:cry_code}Blind", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Select a Joker", + "or playing card", + "to become {C:cry_code}Rigged", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Ends current non-Boss {C:cry_code}Blind{}", "{C:cry_code}without{} cashing out" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Create a {C:cry_code}Glitched", + "Food Joker", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convert {C:cry_code}#1#{} selected cards", + "to a {C:cry_code}chosen{} rank", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} Mult", + }, + }, + e_cry_blur = { + name = "Blurred", + text = { + "{C:attention}Retrigger{} this", + "card {C:attention}1{} time", + "{C:green}#1# in #2#{} chance", + "to retrigger {C:attention}#3#{}", + "additional time", + }, + }, + e_cry_double_sided = { + name = "Double-Sided", + text = { + "This card can be", + "{C:attention}flipped{} to reveal", + "a different card", + "{C:inactive}(Blank side can be merged", + "{C:inactive}with another card)", + }, + }, + e_cry_glass = { + name = "Fragile", + label = "Fragile", + text = { + "{C:white,X:mult} X#3# {} Mult", + "{C:green}#1# in #2#{} chance this", + "card isn't {C:red}destroyed", + "when triggered", + }, + }, + e_cry_glitched = { + name = "Glitched", + text = { + "All values on this card", + "are {C:dark_edition}randomized{}", + "between {C:attention}X0.1{} and {C:attention}X10{}", + "{C:inactive}(If possible){}", + }, + }, + e_cry_gold = { + name = "Golden", + label = "Golden", + text = { + "Earn {C:money}$#1#{} when used", + "or triggered", + }, + }, + e_cry_m = { + name = "Jolly", + text = { + "{C:mult}+#1#{} Mult", + "This card is feeling", + "rather {C:attention}jolly{}", + }, + }, + e_cry_mosaic = { + name = "Mosaic", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + e_cry_noisy = { + name = "Noisy", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Oversaturated", + text = { + "All values", + "on this card", + "are {C:attention}doubled{}", + "{C:inactive}(If possible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Echo Card", + text = { + "{C:green}#2# in #3#{} chance to", + "{C:attention}retrigger{} #1# additional", + "times when scored", + }, + }, + }, + Joker = { + j_cry_altgoogol = { + name = "Nostalgic Googol Play Card", + text = { + "Sell this card to create", + "{C:attention}2{} copies of the leftmost {C:attention}Joker{}", + "{C:inactive,s:0.8}Does not copy Nostalgic Googol Play Cards{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Like Antennas to Heaven", + text = { + "This Joker gains", + "{X:chips,C:white} X#1# {} Chips when each", + "played {C:attention}7{} or {C:attention}4{} is scored", + "{C:inactive}(Currently {X:chips,C:white}X#2# {C:inactive} Chips)", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "{X:mult,C:white} X#1# {} Mult against {C:attention}Boss Blinds{}" }, + }, + j_cry_big_cube = { + name = "Big Cube", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_biggestm = { + name = "Huge", + text = { + "{X:mult,C:white} X#1# {} Mult until end", + "of round if {C:attention}poker hand{}", + "is a {C:attention}#2#{}", + "{C:inactive}(Currently {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}not fat, just big boned.", + }, + }, + j_cry_blender = { + name = "Blender", + text = { + "Create a {C:attention}random{}", + "consumable when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_blurred = { + name = "Blurred Joker", + text = { + "Gain {C:blue}+#1#{} hand(s) when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Each {C:attention}Joker{} gives {C:chips}+#1#{} Chips", + "Increase amount by {C:chips}+#2#{} if", + "{C:attention} poker hand{} is a {C:attention}#3#{}", + "{C:inactive,s:0.8}Jolly Jokers give{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips instead{}", + }, + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Bonus{} card to increase", + "{C:attention}Joker{} or {C:attention}Consumable slots", + "by {C:dark_edition}1{} when scored", + "{C:red}Works twice per round", + "{C:inactive,s:0.8}(Equal chance for each){}", + }, + }, + j_cry_booster = { + name = "Booster Joker", + text = { + "{C:attention}+#1#{} Booster Pack slot", + "available in shop", + }, + }, + j_cry_boredom = { + name = "Boredom", + text = { + "{C:green}#1# in #2#{} chance to", + "{C:attention}retrigger{} each {C:attention}Joker{}", + "or {C:attention}played card{}", + "{C:inactive,s:0.8}Does not affect other Boredom{}", + }, + }, + j_cry_bubblem = { + name = "Bubble M", + text = { + "Create a {C:dark_edition}Foil {C:attention}Jolly Joker{}", + "if played hand contains", + "a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_busdriver = { + name = "Bus Driver", + text = { + "{C:green}#1# in #3#{} chance", + "for {C:mult}+#2#{} Mult", + "{C:green}1 in 4{} chance", + "for {C:mult}-#2#{} Mult", + }, + }, + j_cry_canvas = { + name = "Canvas", + text = { + "{C:attention}Retrigger{} all {C:attention}Jokers{} to the left", + "once for {C:attention}every{} non-{C:blue}Common{C:attention} Joker{}", + "to the right of this Joker", + }, + }, + j_cry_caramel = { + name = "Caramel", + text = { + "Each played card gives", + "{X:mult,C:white}X#1#{} Mult when scored", + "for the next {C:attention}#2#{} rounds", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Retrigger {C:attention}leftmost{} Joker", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_chili_pepper = { + name = "Chili Pepper", + text = { + "This Joker gains {X:mult,C:white} X#2# {} Mult", + "at end of round,", + "{C:red,E:2}self destructs{} after {C:attention}#3#{} rounds", + "{C:inactive}(Currently{} {X:mult,C:white} X#1# {} {C:inactive}Mult){}", + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Chips and {X:dark_edition,C:white}^#1#{} Mult", + "if {C:attention}exactly{} #2#", + "hands remaining", + }, + }, + j_cry_circus = { + name = "Circus", + text = { + "{C:red}Rare{} Jokers each give {X:mult,C:white} X#1# {} Mult", + "{C:cry_epic}Epic{} Jokers each give {X:mult,C:white} X#2# {} Mult", + "{C:legendary}Legendary{} Jokers each give {X:mult,C:white} X#3# {} Mult", + "{C:cry_exotic}Exotic{} Jokers each give {X:mult,C:white} X#4# {} Mult", + }, + }, + j_cry_CodeJoker = { + name = "Code Joker", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:cry_code}Code Card{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Earn between", + "{C:money}$#1#{} and {C:money}$#2#{} for", + "each Joker {C:attention}sold{}", + }, + }, + j_cry_compound_interest = { + name = "Compound Interest", + text = { + "Earn {C:money}#1#%{} of total money", + "at end of round,", + "increases by {C:money}#2#%{} per", + "consecutive payout", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "When a {C:cry_code}Code{} card is used,", + "{C:green}#1# in #2#{} chance to add a copy", + "to your consumable area", + "{C:inactive}(Must have room)", + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "per {C:attention}reroll{} in the shop", + "{C:green}All rerolls are free{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Chain", + text = { + "Sell this card to", + "add {C:money}$#1#{} of {C:attention}sell value{}", + "to every {C:attention}Joker{} card", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob_sob = { + name = "Sob", + text = { + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}run...{}", + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}hide...{}", + "{C:dark_edition,E:1}you cannot escape...{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "for each card {C:attention}purchased{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Cut", + text = { + "This Joker destroys", + "a random {C:cry_code}Code{} card", + "and gains {X:mult,C:white} X#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Delirious Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "Doodle M", + text = { + "Create 2 {C:dark_edition}Negative{} {C:attention}consumables{}", + "when {C:attention}Blind{} is selected", + "Create 1 more {C:attention}consumable", + "for each {C:attention}Jolly Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Scale", + text = { + "Scaling {C:attention}Jokers{}", + "scale {C:attention}quadratically", + "{C:inactive,s:0.8}(ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grows by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult for", + "each played, {C:attention}nonscoring{} {V:1}#2#{} card,", + "suit changes every round", + "{C:inactive}(Currently {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubious Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_duos = { + name = "The Duos", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Every {C:attention}Joker{} gives", + "{X:dark_edition,C:white}^#1#{} Mult" + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Draw {C:green}full deck{} to hand", + "when {C:attention}Blind{} is selected", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "When a {C:attention}Tag{} is acquired,", + "create {C:attention}#1#{} copies of it", + "and {C:attention}increase{} the number of", + "copies by {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Jokers appear using the", + "order from the {C:attention}Collection{}", + "Create {C:attention}#1#{} {C:dark_edition}Negative{} Joker(s)", + "when hand is played", + "{C:cry_exotic,s:0.8}Exotic {C:inactive,s:0.8}or better Jokers cannot appear", + "{s:0.8}Last Joker Generated: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Eternal Flame", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "for each card {C:attention}sold{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographic{} cards", + "each give {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "when {X:red,C:white} XMult {} is triggered", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult if", + "played cards are scored", + "{C:attention}#2#{} or fewer times", + }, + }, + j_cry_filler = { + name = "The Filler", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fractal Fingers", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + j_cry_flip_side = { + name = "On the Flip Side", + text = { + "{C:dark_edition}Double-Sided{} Jokers use", + "their back side for effects", + "instead of the front side", + "{C:attention}Retrigger{} all {C:dark_edition}Double-Sided{} Jokers" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}self destructs{} in {C:attention}#2#{} round(s)", + "Increases by {C:attention}#3#{} round when", + "{C:attention}Jolly Joker{} is {C:attention}sold{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foxy = { + name = "Foxy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "if hand played is {C:attention}not{}", + "most played {C:attention}poker hand{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Earn {C:money}$#1#{} if {C:attention}played hand{}", + "contains an {C:attention}Ace{} and a {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} all values", + "of leftmost {C:attention}Joker", + "at end of round", + }, + }, + j_cry_giggly = { + name = "Absurd Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Earn {C:money}#1#%{} of total", + "money at end of round", + "Payout increases by {C:money}#2#%{}", + "when each played {C:attention}Gold{}", + "card is scored", + }, + }, + j_cry_googol_play = { + name = "Googol Play Card", + text = { + "{C:green}#1# in #2#{} chance for", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Create a random {C:attention}Joker{}", + "at end of round", + "Sell this card to", + "create a random {C:attention}Joker{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_happyhouse = { + name = "Happy House", + text = { + "{X:dark_edition,C:white}^#1#{} Mult only after", + "playing {C:attention}114{} hands{}", + "{C:inactive}(Currently #2#/114){}", + "{C:inactive,s:0.8}There is no place like home!{}", + }, + }, + j_cry_home = { + name = "The Home", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Earn {C:money}$#1#{} when", + "using a {C:attention}consumable{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Retrigger all cards played", + "{C:attention}#2#{} time(s),", + "each played card gives", + "{X:mult,C:white} X#1# {} Mult when scored", + }, + }, + j_cry_jimball = { + name = "Jimball", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "per {C:attention}consecutive{} hand played", + "while playing your", + "most played {C:attention}poker hand", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Jolly Joker?", + text = { + "Create a {C:dark_edition}Jolly{} Joker", + "when a Joker is {C:attention}sold{}", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{}", + "when a {C:attention}Type Mult{} or", + "{C:attention}Type Chips{} Joker is sold", + }, + }, + j_cry_kooky = { + name = "Kooky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty the Clown", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when", + "each played {C:attention}card{} is scored", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoscope", + text = { + "Add {C:dark_edition}Polychrome{} to", + "a random {C:attention}Joker{} when", + "{C:attention}Boss Blind{} is defeated", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Each played {C:attention}7{} or {C:attention}2{}", + "gives {X:mult,C:white}X#1#{} Mult when scored", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Give future copies of", + "this Joker {X:mult,C:white}X#1#{} Mult", + "at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Retrigger{} all Jokers", + "once for each {C:attention}Jolly{}", + "{C:attention}Joker{} sold this round", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Retrigger(s)){}", + "{C:inactive,s:0.8}There wasn't enough room...{}", + }, + }, + j_cry_lucky_joker = { + name = "Lucky Joker", + text = { + "Earn {C:money}$#1#{} every time a", + "{C:attention}Lucky{} card {C:green}successfully{}", + "triggers", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "All Jokers give", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "when {C:attention}Jolly Joker{} is sold", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:attention}Jolly Joker{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_macabre = { + name = "Macabre Joker", + text = { + "When {C:attention}Blind{} is selected,", + "destroys each {C:attention}Joker{} except", + "{C:legendary}M Jokers{} and {C:attention}Jolly Jokers{}", + "and create 1 {C:attention}Jolly Joker{}", + "for each destroyed card", + }, + }, + j_cry_magnet = { + name = "Fridge Magnet", + text = { + "Earn {C:money}$#1#{} at end of round", + "This earns {X:money,C:white} X#2# {} if there are", + "{C:attention}#3#{} or fewer {C:attention}Joker{} cards", + }, + }, + j_cry_manic = { + name = "Manic Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Retrigger all Jokers", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_maximized = { + name = "Maximized", + text = { + "All {C:attention}face{} cards", + "are considered {C:attention}Kings{},", + "all {C:attention}numbered{} cards", + "are considered {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Labyrinth", + text = { + "All hands are considered the", + "{C:attention}first hand{} of each round,", + "all discards are considered the", + "{C:attention}first discard{} of each round", + }, + }, + j_cry_Megg = { + name = "Megg", + text = { + "Sell this card to create", + "{C:attention}#2#{} Jolly #3#, increase", + "by {C:attention}#1#{} at end of round", + }, + }, + j_cry_membershipcard = { + name = "Membership Card", + text = { + "{X:mult,C:white}X#1#{} Mult for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Old Membership Card", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteor Shower", + text = { + "{C:dark_edition}Foil{} cards each", + "give {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by", + "{C:money}$#1#{} for each {C:attention}Jolly Joker{}", + "or {C:legendary}M Joker{} at", + "end of round", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if no {C:attention}discards{} were", + "used this round", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Monkey Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and permanently add {C:attention}ten times{}", + "its sell value to this {C:chips}Chips{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{} when", + "a card with an {C:attention}Edition{} is sold", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Create an {C:legendary}M Joker{} at end of round", + "Each {C:attention}Jolly Joker{} or {C:legendary}M Joker", + "gives {X:dark_edition,C:white}^#1#{} Mult", + "Increase amount by {X:dark_edition,C:white}^#2#{}", + "when a {C:attention}Jolly Joker{} is {C:attention}sold", + "{C:inactive,s:0.8}(Tredecim excluded)", + }, + }, + j_cry_mstack = { + name = "M Stack", + text = { + "Retrigger all cards played", + "once for every", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jolly Jokers{} sold", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} retriggers){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Mult{} card to create", + "a {C:spectral}Cryptid{} card when scored", + "{C:inactive}(Must have room)", + }, + }, + j_cry_negative = { + name = "Negative Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips if played hand", + "contains a {C:attention}6{} and a {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Night", + text = { + "{X:dark_edition,C:white}^#1#{} Mult on final", + "hand of round", + "{E:2,C:red}self destructs{} on", + "final hand of round", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Retrigger each played {C:attention}7{}", + "{C:attention:}#1#{} additional time(s)", + }, + }, + j_cry_notebook = { + name = "Notebook", + text = { + "{C:green} #1# in #2#{} chance to gain {C:dark_edition}+1{} Joker", + "slot per {C:attention}reroll{} in the shop", + "{C:green}Always triggers{} if there are", + "{C:attention}#5#{} or more {C:attention}Jolly Jokers{}", + "{C:red}Works once per round{}", + "{C:inactive}(Currently {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Number Blocks", + text = { + "Earn {C:money}$#1#{} at end of round", + "Increase payout by {C:money}$#2#{}", + "for each {C:attention}#3#{} held in hand,", + "rank changes every round", + }, + }, + j_cry_nuts = { + name = "The Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_oldblueprint = { + name = "Old Blueprint", + text = { + "Copies ability of", + "{C:attention}Joker{} to the right", + "{C:green}#1# in #2#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + j_cry_oldcandy = { + name = "Nostalgic Candy", + text = { + "Sell this card to", + "permanently gain", + "{C:attention}+#1#{} hand size", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgic Invisible Joker", + text = { + "{C:attention}Duplicate{} a random", + "{C:attention}Joker{} every {C:attention}4", + "Joker cards sold", + "{s:0.8}Nostalgic Invisible Joker Excluded{}", + "{C:inactive}(Currently #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panopticon", + text = { + "All hands are considered the", + "{C:attention}last hand{} of each round", -- +$4 + }, + }, + j_cry_pickle = { + name = "Pickle", + text = { + "When {C:attention}Blind{} is skipped, create", + "{C:attention}#1#{} Tags, reduced by", + "{C:red}#2#{} when {C:attention}Blind{} is selected", + }, + }, + j_cry_pirate_dagger = { + name = "Pirate Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "and gain {C:attention}one-fourth{} of", + "its sell value as {X:chips,C:white} XChips {}", + "{C:inactive}(Currently {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + j_cry_pot_of_jokes = { + name = "Pot of Jokes", + text = { + "{C:attention}#1#{} hand size,", + "increases by", + "{C:blue}#2#{} every round", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "if all cards in played hand are", + "{C:attention}Aces{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, or {C:attention}7s{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "If {C:attention}poker hand{} is a", + "{C:attention}Royal Flush{}, destroy scored", + "{C:attention}Queen{} and create a", + "{C:dark_edition}Negative {}{C:red}Rare{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "The Quintet", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Red Bloon", + text = { + "Earn {C:money}$#1#{} in {C:attention}#2#{} round(s)", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante when", + "{C:money}$#2#{} {C:inactive}($#3#){} spent", + "{s:0.8}Requirements increase", + "{C:attention,s:0.8}exponentially{s:0.8} per use", + "{C:money,s:0.8}Next increase: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "When a {C:attention}Joker{} is sold,", + "add its effects to", + "every other Joker", + "{C:inactive,s:0.8}Does not affect other Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fill all empty Joker slots {C:inactive}(Max 100){}", + "with {C:dark_edition}Holographic{} {C:attention}Jolly Jokers{} if", + "{C:attention}discarded poker hand{} is a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Randomize abilities each {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Create an {C:green}Uncommon{} Joker", + "and 3 {C:attention}Jolly Jokers{} when", + "a {C:spectral}Spectral{} card is used", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sapling", + text = { + "After scoring {C:attention}#2#{} {C:inactive}[#1#]{} Enhanced", + "cards, sell this card to", + "create an {C:cry_epic}Epic{} {C:attention}Joker{}", + "{C:inactive,s:0.8}Will create a {C:red,s:0.8}Rare{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}if {C:cry_epic,s:0.8}Epic{} {C:inactive,s:0.8}Jokers are disabled{}", + }, + }, + j_cry_savvy = { + name = "Savvy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Scaling {C:attention}Jokers{} scale", + "as a degree-{C:attention}#1#{} polynomial", + "raise degree by {C:attention}#2#{}", + "at end of round", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluded)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Tile", + text = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Jolly {C:green}Uncommon{} Joker", + "when hand is played", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Add a {C:attention}random seal{} to each card", + "scored on {C:attention}final hand{} of round", + }, + }, + j_cry_shrewd = { + name = "Shrewd Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Silly Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Tiny", + text = { + "Create a {C:cry_jolly}Double M", + "tag if {C:attention}poker hand{}", + "is a {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker slot", + "{C:attention}+#1#{} Booster Pack slot", + "{C:attention}+#1#{} hand size", + "{C:attention}+#1#{} consumable slot", + "{C:attention}+#1#{} card in shop", + }, + }, + j_cry_spaceglobe = { + name = "Celestial Globe", + text = { + "This Joker gains {X:chips,C:white}X#2#{} Chips", + "if {C:attention}poker hand{} is a {C:attention}#3#{},", + "Hand changes after increase{}", + "{C:inactive}(Currently{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Creates a {C:dark_edition}Negative{} copy", + "of a random {C:attention}Joker{}", + "at the end of the {C:attention}shop", + "{C:inactive,s:0.8}Does not copy other Speculo{}", + }, + }, + j_cry_stardust = { + name = "Stardust", + text = { + "{C:dark_edition}Polychrome{} cards", + "each give {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "This Joker destroys a", + "random {C:planet}Planet{} card", + "and gains {X:dark_edition,C:white} ^#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_subtle = { + name = "Subtle Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Earn {C:money}$#3#{} at", + "end of round", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "At end of round, create", + "a {C:attention}copy{} of a random", + "card {C:attention}held in hand{},", + "destroy all others", + "{C:attention,s:0.8}Kings{s:0.8} of {C:hearts,s:0.8}Hearts{s:0.8} are prioritized", + }, + }, + j_cry_swarm = { + name = "The Swarm", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Catalyst", + text = { + "Balances {C:chips}Chips{} and {C:mult}Mult{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + "Earn {C:money}$#2#{} at end of round", + }, + }, + j_cry_translucent = { + name = "Translucent Joker", + text = { + "Sell this card to create", + "a {C:attention}Banana Perishable{} copy", + "of a random {C:attention}Joker{}", + "{s:0.8,C:inactive}(Copy bypasses perish compat)", + }, + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Triplet Rhythm", + text = { + "{X:mult,C:white} X#1# {} Mult if scoring hand", + "contains {C:attention}exactly{} three {C:attention}3s", + }, + }, + j_cry_unity = { + name = "The Unity", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Poker hands{} gain", + "{X:red,C:white} X#1# {} Mult and {X:blue,C:white} X#1# {} Chips", + "when leveled up", + }, + }, + j_cry_unjust_dagger = { + name = "Unjust Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and gain {C:attention}one-fifth{} of", + "its sell value as {X:mult,C:white} XMult {}", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "This Joker gains {C:money}$#1#{} of {C:attention}sell value{}", + "if {C:attention}poker hand{} contains a {C:attention}#2#{}", + "Sell this card to create a", + "{C:dark_edition}Polychrome{} {C:attention}Jolly Joker{} for", + "every {C:money}$4{} of {C:attention}sell value{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Wacky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "All Jokers give", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "All Jokers give", + "{C:money}$#1#{} when triggered", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "This Joker gains", + "{C:mult}+#2#{} Mult when each played", + "{C:attention}Ace{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "is scored", + "{C:inactive}(Currently {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Retrigger each played {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} additional time(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Wheel of Hope", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when failing", + "a {C:attention}Wheel of Fortune{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if {C:attention}played hand{} contains a", + "{C:attention}2{} and {C:attention}7{} of different suits", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutron Star", + text = { + "Upgrade a random", + "poker hand by", + "{C:attention}1{} level for each", + "{C:attention}Neutron Star{} used", + "in this run", + "{C:inactive}(Currently{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# in #2#{} chance to", + "upgrade every", + "{C:legendary,E:1}poker hand{}", + "by {C:attention}1{} level", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "CCD Sleeve", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Conveyor Sleeve", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Critical Sleeve", + text = { + "After each hand played,", + "{C:green}1 in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}1 in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Encoded Sleeve", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balanced Sleeve", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}+2 Shop Slots", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unlimited Sleeve", + text = { + "You can select {C:attention}any", + "number of cards", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Misprinted Sleeve", + text = { + "Values of cards", + "are {C:attention}randomized", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Redeemed Sleeve", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Wormhole Sleeve", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + }, + Spectral = { + c_cry_analog = { + name = "Analog", + text = { + "Create {C:attention}#1#{} copies of a", + "random {C:attention}Joker{}, destroy", + "all other Jokers, {C:attention}+#2#{} Ante", + }, + }, + c_cry_gateway = { + name = "Gateway", + text = { + "Create a random", + "{C:cry_exotic,E:1}Exotic{C:attention} Joker{}, destroy", + "all other Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Lock", + text = { + "Remove {C:red}all{} stickers", + "from {C:red}all{} Jokers,", + "then apply {C:purple,E:1}Eternal{}", + "to a random {C:attention}Joker{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Create a card", + "of {C:cry_code}your choice", + "{C:inactive,s:0.8}(Exotic Jokers #1#excluded)", + }, + }, + c_cry_replica = { + name = "Replica", + text = { + "Convert all cards", + "held in hand", + "to a {C:attention}random{}", + "card held in hand", + }, + }, + c_cry_source = { + name = "Source", + text = { + "Add a {C:cry_code}Green Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_summoning = { + name = "Summoning", + text = { + "Create a random", + "{C:cry_epic}Epic{} {C:joker}Joker{}, destroy", + "one random {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Trade", + text = { + "{C:attention}Lose{} a random Voucher,", + "gain {C:attention}2{} random Vouchers", + }, + }, + c_cry_typhoon = { + name = "Typhoon", + text = { + "Add an {C:cry_azure}Azure Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_vacuum = { + name = "Vacuum", + text = { + "Removes {C:red}all {C:green}modifications{}", + "from {C:red}all{} cards held in hand,", + "Earn {C:money}$#1#{} per {C:green}modification{} removed", + "{C:inactive,s:0.7}(ex. Enhancements, Seals, Editions)", + }, + }, + c_cry_white_hole = { + name = "White Hole", + text = { + "{C:attention}Remove{} all hand levels,", + "upgrade {C:legendary,E:1}most played{} poker hand", + "by {C:attention}3{} for each removed level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pink Stake", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brown Stake", + colour = "Brown", + text = { + "All {C:attention}stickers{} are compatible", + "with each other", + }, + }, + stake_cry_yellow = { + name = "Yellow Stake", + colour = "Yellow", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Jade Stake", + colour = "Jade", + text = { + "Cards can be drawn {C:attention}face down{}", + }, + }, + stake_cry_cyan = { + name = "Cyan Stake", + colour = "Cyan", + text = { + "{C:green}Uncommon{} and {C:red}Rare{} Jokers are", + "less likely to appear", + }, + }, + stake_cry_gray = { + name = "Gray Stake", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Crimson Stake", + colour = "Crimson", + text = { + "Vouchers restock on {C:attention}even{} Antes", + }, + }, + stake_cry_diamond = { + name = "Diamond Stake", + colour = "Diamond", + text = { + "Must beat Ante {C:attention}10{} to win", + }, + }, + stake_cry_amber = { + name = "Amber Stake", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Pack slot", + }, + }, + stake_cry_bronze = { + name = "Bronze Stake", + colour = "Bronze", + text = { + "Vouchers are {C:attention}50%{} more expensive", + }, + }, + stake_cry_quartz = { + name = "Quartz Stake", + colour = "Quartz", + text = { + "Jokers can be {C:attention}Pinned{}", + "{s:0.8,C:inactive}(Stays pinned to the leftmost position){}", + }, + }, + stake_cry_ruby = { + name = "Ruby Stake", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds can become", + "{C:attention}Boss{} Blinds", + }, + }, + stake_cry_glass = { + name = "Glass Stake", + colour = "Glass", + text = { + "Cards can {C:attention}shatter{} when scored", + }, + }, + stake_cry_sapphire = { + name = "Sapphire Stake", + colour = "Sapphire", + text = { + "Lose {C:attention}25%{} of current money", + "at end of Ante", + "{s:0.8,C:inactive}(Up to $10){}", + }, + }, + stake_cry_emerald = { + name = "Emerald Stake", + colour = "Emerald", + text = { + "Cards, packs, and vouchers", + "can be {C:attention}face down{}", + "{s:0.8,C:inactive}(Unable to be viewed until purchased){}", + }, + }, + stake_cry_platinum = { + name = "Platinum Stake", + colour = "Platinum", + text = { + "Small Blinds are {C:attention}removed{}", + }, + }, + stake_cry_twilight = { + name = "Twilight Stake", + colour = "Twilight", + text = { + "Cards can be {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 in 10 chance of being destroyed each round){}", + }, + }, + stake_cry_verdant = { + name = "Verdant Stake", + colour = "Verdant", + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Ember Stake", + colour = "Ember", + text = { + "All items give no money when sold", + }, + }, + stake_cry_dawn = { + name = "Dawn Stake", + colour = "Dawn", + text = { + "Tarots and Spectrals target {C:attention}1", + "fewer card", + "{s:0.8,C:inactive}(Minimum of 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizon Stake", + colour = "Horizon", + text = { + "When blind selected, add a", + "{C:attention}random card{} to deck", + }, + }, + stake_cry_blossom = { + name = "Blossom Stake", + colour = "Blossom", + text = { + "{C:attention}Final{} Boss Blinds can appear", + "in {C:attention}any{} Ante", + }, + }, + stake_cry_azure = { + name = "Azure Stake", + colour = "Azure", + text = { + "Values on Jokers are reduced", + "by {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Ascendant Stake", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop slot", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Banana Tag", + text = { + "Creates {C:attention}#1#", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_bettertop_up = { + name = "Better Top-up Tag", + text = { + "Creates up to {C:attention}#1#", + "{C:green}Uncommon{} Jokers", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_better_voucher = { + name = "Golden Voucher Tag", + text = { + "Adds one Tier {C:attention}#1#{} Voucher", + "to the next shop", + }, + }, + tag_cry_blur = { + name = "Blurred Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Blurred{}", + }, + }, + tag_cry_booster = { + name = "Booster Tag", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:attention}double{} cards and", + "{C:attention}double{} choices", + }, + }, + tag_cry_bundle = { + name = "Bundle Tag", + text = { + "Create a {C:attention}Standard Tag{}, {C:tarot}Charm Tag{},", + "{C:attention}Buffoon Tag{}, and {C:planet}Meteor Tag", + }, + }, + tag_cry_cat = { + name = "Cat Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Console Tag", + text = { + "Gives a free", + "{C:cry_code}Program Pack", + }, + }, + tag_cry_double_m = { + name = "Double M Tag", + text = { + "Shop has a", + "{C:dark_edition}Jolly {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Empowered Tag", + text = { + "Gives a free {C:spectral}Spectral Pack", + "with {C:legendary,E:1}The Soul{} and {C:cry_exotic,E:1}Gateway{}", + }, + }, + tag_cry_epic = { + name = "Epic Tag", + text = { + "Shop has a half-price", + "{C:cry_epic}Epic Joker", + }, + }, + tag_cry_gambler = { + name = "Gambler's Tag", + text = { + "{C:green}#1# in #2#{} chance to create", + "an {C:cry_exotic,E:1}Empowered Tag", + }, + }, + tag_cry_glass = { + name = "Fragile Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Fragile{}", + }, + }, + tag_cry_glitched = { + name = "Glitched Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Glitched{}", + }, + }, + tag_cry_gold = { + name = "Golden Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Golden{}", + }, + }, + tag_cry_gourmand = { + name = "Gourmand Tag", + text = { + "Shop has a free", + "{C:attention}Food Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gives a free", + "{C:cry_ascendant}Meme Pack", + }, + }, + tag_cry_m = { + name = "Jolly Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Jolly{}", + }, + }, + tag_cry_memory = { + name = "Memory Tag", + text = { + "Create {C:attention}#1#{} copies of", + "the last {C:attention}Tag{} used", + "during this run", + "{s:0.8,C:inactive}Copying Tags excluded", + "{s:0.8,C:inactive}Currently: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Mosaic{}", + }, + }, + tag_cry_oversat = { + name = "Oversaturated Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Oversaturated{}", + }, + }, + tag_cry_quadruple = { + name = "Quadruple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_quintuple = { + name = "Quintuple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_rework = { + name = "Rework Tag", + text = { + "Shop has a(n)", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schematic Tag", + text = { + "Shop has a", + "{C:attention}Brainstorm", + }, + }, + tag_cry_scope = { + name = "Scope Tag", + text = { + "{C:attention}+#1# {C:blue}hands{} and", + "{C:red}discards{} next round", + }, + }, + tag_cry_triple = { + name = "Triple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "The Automaton", + text = { + "Creates up to {C:attention}#1#", + "random {C:cry_code}Code{} card", + "{C:inactive}(Must have room)", + }, + }, + c_cry_eclipse = { + name = "The Eclipse", + text = { + "Enhances {C:attention}#1#{} selected card", + "into an {C:attention}Echo Card", + }, + }, + c_cry_meld = { + name = "Meld", + text = { + "Select a {C:attention}Joker{} or", + "{C:attention}playing card{} to", + "become {C:dark_edition}Double-Sided", + }, + }, + c_cry_theblessing = { + name = "The Blessing", + text = { + "Creates {C:attention}1{}", + "random {C:attention}consumable{}", + "{C:inactive}(Must have room){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglyph", + text = { + "Set Ante to {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Blank Canvas", + text = { + "{C:attention}+#1#{} hand size", + }, + }, + v_cry_clone_machine = { + name = "Clone Machine", + text = { + "Double Tags become", + "{C:attention}Quintuple Tags{} and", + "are {C:attention}4X{} as common", + }, + }, + v_cry_command_prompt = { + name = "Command Prompt", + text = { + "{C:cry_code}Code{} cards", + "can appear", + "in the {C:attention}shop{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Double Tags become", + "{C:attention}Triple Tags{} and are", + "{C:attention}2X{} as common", + }, + }, + v_cry_curate = { + name = "Curate", + text = { + "All cards", + "appear with", + "an {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Dexterity", + text = { + "Permanently", + "gain {C:blue}+#1#{} hand(s)", + "each round", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "After every round,", + "{X:dark_edition,C:white} X1.5 {} to all values", + "on the back of", + "{C:dark_edition}Double-Sided{} cards" + }, + }, + v_cry_double_slit = { + name = "Double Slit", + text = { + "{C:attention}Meld{} can appear", + "in the shop and", + "Arcana Packs", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "{C:dark_edition}Double-Sided{} cards appear", + "{C:attention}4X{} more frequently", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker slot(s)", + }, + }, + v_cry_massproduct = { + name = "Mass Production", + text = { + "All cards and packs", + "in shop cost {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Raise the cap on", + "interest earned in", + "each round to {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multistock", + text = { + "{C:attention}+#1#{} card slot(s) and", + "{C:attention}+#1#{} booster pack slot(s)", + "available in shop", + }, + }, + v_cry_pacclimator = { + name = "Planet Acclimator", + text = { + "{C:planet}Planet{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:planet}Planet{}", + "cards are {C:green}free{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Retrigger{} all M Jokers", + "once for every Pair", + "{C:attention}contained{} in played hand", + }, + }, + v_cry_pairing = { + name = "Pairing", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand is a {C:attention}Pair", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computing", + text = { + "{C:cry_code}Code{} cards can spawn", + "with {C:dark_edition}Negative{} edition", + }, + }, + v_cry_repair_man = { + name = "Repair Man", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand contains a {C:attention}Pair", + }, + }, + v_cry_rerollexchange = { + name = "Reroll Exchange", + text = { + "All rerolls", + "cost {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satellite Uplink", + text = { + "{C:cry_code}Code{} cards may", + "appear in any of", + "the {C:attention}Celestial Packs{}", + }, + }, + v_cry_scope = { + name = "Galactic Scope", + text = { + "Create the {C:planet}Planet", + "card for played", + "{C:attention}poker hand{}", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Tarot Acclimator", + text = { + "{C:tarot}Tarot{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:tarot}Tarot{}", + "cards are {C:green}free{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Printer", + text = { + "Double Tags become", + "{C:attention}Quadruple Tags{} and", + "are {C:attention}3X{} as common", + }, + }, + v_cry_threers = { + name = "The 3 Rs", + text = { + "Permanently", + "gain {C:red}+#1#{} discard(s)", + "each round", + }, + }, + v_cry_stickyhand = { + name = "Sticky Hand", + text = { + "{C:attention}+#1#{} card", + "selection limit", + }, + }, + v_cry_grapplinghook = { + name = "Grappling Hook", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperspace Tether", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "destroyed each round", + }, + }, + cry_rigged = { + name = "Rigged", + text = { + "All {C:cry_code}listed{} probabilities", + "are {C:cry_code}guaranteed", + }, + }, + cry_hooked = { + name = "Hooked", + text = { + "When this Joker is {C:cry_code}triggered{},", + "trigger {C:cry_code}#1#", + }, + }, + food_jokers = { + name = "Food Jokers", + text = { + "{s:0.8}Gros Michel, Egg, Ice Cream, Cavendish,", + "{s:0.8}Turtle Bean, Diet Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Pickle, Chili Pepper, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eternal", + text = { + "All cards in pack", + "are {C:attention}Eternal{}", + }, + }, + cry_perishable_booster = { + name = "Perishable", + text = { + "All cards in pack", + "are {C:attention}Perishable{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "All cards in pack", + "are {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Pinned", + text = { + "All cards in pack", + "are {C:attention}Pinned{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "All cards in pack", + "are {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eternal", + text = { + "Can't be traded", + }, + }, + cry_perishable_voucher = { + name = "Perishable", + text = { + "Debuffed after", + "{C:attention}#1#{} rounds", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)", + }, + }, + cry_rental_voucher = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at", + "end of round", + }, + }, + cry_pinned_voucher = { + name = "Pinned", + text = { + "Remains in shop", + "until redeemed", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "unredeemed each round", + }, + }, + cry_perishable_consumeable = { + name = "Perishable", + text = { + "Debuffed at", + "end of round", + }, + }, + cry_rental_consumeable = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at end of", + "round, and on use", + }, + }, + cry_pinned_consumeable = { + name = "Pinned", + text = { + "Can't use other", + "non-{C:attention}Pinned{} consumables", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance to do", + "nothing on use", + }, + }, + p_cry_code_normal_1 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_normal_2 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_two = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_three = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + undiscovered_code = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + cry_green_seal = { + name = "Green Seal", + text = { + "Creates a {C:cry_code}Code{} card", + "when played and unscoring", + "{C:inactive}(Must have room)", + }, + }, + cry_azure_seal = { + name = "Azure Seal", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planets{} for played", + "{C:attention}poker hand{}, then", + "{C:red}destroy{} this card", + }, + }, + }, + }, + misc = { + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Legally Blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Break Infinity", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Home Realtor", + ach_cry_jokes_on_you = "Joke's on You, Pal!", + ach_cry_niw_uoy = "!niW uoY", + ach_cry_now_the_fun_begins = "Now the Fun Begins", + ach_cry_patience_virtue = "Patience is a Virtue", + ach_cry_perfectly_balanced = "Perfectly Balanced", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Traffic Jam", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "We Told You Not To", + ach_cry_what_have_you_done = "WHAT HAVE YOU DONE?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtain Blurred Blurred Joker", + ach_cry_bullet_hell = "Have 15 AP Jokers", + ach_cry_break_infinity = "Score 1.79e308 Chips in a single hand", + ach_cry_cryptid_the_cryptid = "Use Cryptid on Cryptid", + ach_cry_exodia = "Have 5 Exotic Jokers", + ach_cry_freak_house = "Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice", + ach_cry_googol_play_pass = "Rig a Googol Play Card", + ach_cry_haxxor = "Use a cheat code", + ach_cry_home_realtor = "Activate Happy House before Ante 8 (without DoE/Antimatter)", + ach_cry_jokes_on_you = "Trigger The Joke's effect on Ante 1 and win the run", + ach_cry_niw_uoy = "Reach Ante -8", + ach_cry_now_the_fun_begins = "Obtain Canvas", + ach_cry_patience_virtue = "Wait out Lavender Loop for 2 minutes before playing first hand and beat the blind", + ach_cry_perfectly_balanced = "Beat Very Fair Deck on Ascendant Stake", + ach_cry_pull_request = "Have ://COMMIT spawn the same Joker that it destroyed", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Win in 1 round", + ach_cry_used_crash = "Use ://CRASH", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Music", + cry_set_enable_features = "Select features to enable (applies on game restart):", + cry_feat_achievements = "Achievements", + ["cry_feat_antimatter deck"] = "Antimatter Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Code Cards", + ["cry_feat_misc. decks"] = "Misc. Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechanics", + ["cry_feat_enhanced decks"] = "Enhanced Decks", + ["cry_feat_epic jokers"] = "Epic Jokers", + ["cry_feat_exotic jokers"] = "Exotic Jokers", + ["cry_feat_m jokers"] = "M Jokers", + cry_feat_menu = "Custom Main Menu", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Misc. Jokers", + cry_feat_planets = "Planets", + cry_feat_jokerdisplay = "JokerDisplay (Does Nothing)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Sleeves", + cry_feat_spectrals = "Spectrals", + ["cry_feat_more stakes"] = "Stakes", + cry_feat_vouchers = "Vouchers", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Pack", + k_cry_meme_pack = "Meme Pack", + + cry_critical_hit_ex = "Critical Hit!", + cry_critical_miss_ex = "Critical Miss!", + + cry_debuff_oldhouse = "No Full Houses", + cry_debuff_oldarm = "Must play 4 or fewer cards", + cry_debuff_oldpillar = "No Straights", + cry_debuff_oldflint = "No Flushes", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applies abilities of all defeated bosses", + + k_code = "Code", + b_code_cards = "Code Cards", + b_pull = "PULL", + cry_hooked_ex = "Hooked!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTER RANK", + cry_code_enh = "ENTER ENHANCEMENT", + cry_code_hand = "ENTER POKER HAND", + cry_code_enter_card = "ENTER A CARD", + cry_code_apply = "APPLY", + cry_code_apply_previous = "APPLY PREVIOUS", + cry_code_exploit = "EXPLOIT", + cry_code_exploit_previous = "EXPLOIT PREVIOUS", + cry_code_create = "CREATE", + cry_code_create_previous = "CREATE PREVIOUS", + cry_code_execute = "EXECUTE", + cry_code_cancel = "CANCEL", + + b_flip = "FLIP", + b_merge = "MERGE", + + cry_again_q = "Again?", + cry_curse = "Curse", + cry_curse_ex = "Curse!", + cry_sobbing = "Help me...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Round", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "No triggers left!", + cry_unredeemed = "Unredeemed...", + cry_active = "Active", + cry_inactive = "Inactive", + + k_disable_music = "Disable Music", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Copyright Notice", + cry_notif_jimball_d1 = "Jimball plays the song \"Funkytown\",", + cry_notif_jimball_d2 = "which is copyrighted and can't be", + cry_notif_jimball_d3 = "used for streams and videos.", + }, + labels = { + food_jokers = "Food Jokers", + banana = "Banana", + code = "Code", + cry_rigged = "Rigged", + cry_hooked = "Hooked", + + cry_green_seal = "Green Seal", + cry_azure_seal = "Azure Seal", + + cry_astral = "Astral", + cry_blur = "Blurred", + cry_double_sided = "Double-Sided", + cry_glass = "Fragile", + cry_glitched = "Glitched", + cry_gold = "Golden", + cry_m = "Jolly", + cry_mosaic = "Mosaic", + cry_noisy = "Noisy", + cry_oversat = "Oversaturated", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic" + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + }, + v_text = { + ch_c_cry_all_perishable = {"All Jokers are {C:eternal}Perishable{}"}, + ch_c_cry_all_rental = {"All Jokers are {C:eternal}Rental{}"}, + ch_c_cry_all_pinned = {"All Jokers are {C:eternal}Pinned{}"}, + ch_c_cry_all_banana = {"All Jokers are {C:eternal}Banana{}"}, + ch_c_all_rnj = {"All Jokers are {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"All purchasable items have all stickers"}, + ch_c_cry_rush_hour = {"All Boss Blinds are {C:attention}The Clock{} or {C:attention}Lavender Loop"}, + ch_c_cry_rush_hour_ii = {"All Blinds are {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}The Clock{} and {C:attention}Lavender Loop{} scale {C:attention}twice{} as fast"}, + ch_c_cry_no_tags = {"Skipping is {C:attention}disabled{}"}, + ch_c_cry_no_vouchers = {"{C:attention}Vouchers{} no longer appear in the shop"}, + ch_c_cry_no_boosters = {"{C:attention}Booster Packs{} no longer appear in the shop"}, + ch_c_cry_no_rerolls = {"Rerolling is {C:attention}disabled{}"}, + ch_c_cry_no_consumables = {"{C:attention}Consumables{} no longer appear"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/ko.lua b/Cryptid/localization/ko.lua new file mode 100644 index 0000000..65b3b86 --- /dev/null +++ b/Cryptid/localization/ko.lua @@ -0,0 +1,3314 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antimatter Deck", + text = { + "Applies the {C:legendary,E:1}upsides{}", + "of {C:attention}every{} deck", + }, + }, + b_cry_beta = { + name = "Nostalgic Deck", + text = { + "{C:attention}Joker{} and {C:attention}Consumable{}", + "slots are {C:attention}combined", + "{C:attention}Nostalgic{} Blinds replace", + "their updated Blind" + }, + }, + b_cry_blank = { + name = "Blank Deck", + text = { + "{C:inactive,E:1}Does nothing?", + }, + }, + b_cry_CCD = { + name = "CCD Deck", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + b_cry_conveyor = { + name = "Conveyor Deck", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + b_cry_critical = { + name = "Critical Deck", + text = { + "After each hand played,", + "{C:green}#1# in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}#1# in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + b_cry_encoded = { + name = "Encoded Deck", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + b_cry_equilibrium = { + name = "Deck of Equilibrium", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}Overstock Plus", + }, + }, + b_cry_glowing = { + name = "Glowing Deck", + text = { + "Multiply the values of", + "all Jokers by {X:dark_edition,C:white} X1.25 {}", + "when Boss Blind is defeated", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Infinite Deck", + text = { + "You can select {C:attention}any", + "number of cards", + "{C:attention}+1{} hand size", + }, + }, + b_cry_misprint = { + name = "Misprint Deck", + text = { + "Values of cards", + "and poker hands", + "are {C:attention}randomized", + }, + }, + b_cry_redeemed = { + name = "Redeemed Deck", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + b_cry_very_fair = { + name = "Very Fair Deck", + text = { + "{C:blue}-2{} hands, {C:red}-2{} discards", + "every round", + "{C:attention}Vouchers{} no longer", + "appear in the shop", + }, + }, + b_cry_wormhole = { + name = "Wormhole Deck", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + b_cry_legendary = { + name = "Legendary Deck", + text = { + "Start with an {C:legendary}Legendary{C:legendary} Joker", + "{C:green}1 in 5{} chance to create another", + "when Boss Blind is defeated {C:inactive}(must have room){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "The Box", + text = { + "All Common Jokers", + "are debuffed", + }, + }, + bl_cry_clock = { + name = "The Clock", + text = { + "+0.1X blind requirements every", + "3 seconds spent this ante", + }, + }, + bl_cry_hammer = { + name = "The Hammer", + text = { + "All cards with odd", + "rank are debuffed", + }, + }, + bl_cry_joke = { + name = "The Joke", + text = { + "If score is >2X requirements,", + "set ante to multiple of #1#", + }, + }, + bl_cry_magic = { + name = "The Magic", + text = { + "All cards with even", + "rank are debuffed", + }, + }, + bl_cry_lavender_loop = { + name = "Lavender Loop", + text = { + "1.25X blind requirements every", + "1.5 seconds spent this round", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidian Orb", + text = { + "Applies abilities of", + "all defeated bosses", + }, + }, + bl_cry_oldarm = { + name = "Nostalgic Arm", + text = { + "Must play 4", + "or fewer cards", + }, + }, + bl_cry_oldfish = { + name = "Nostalgic Fish", + text = { + "All hands start", + "with 1 Mult", + }, + }, + bl_cry_oldflint = { + name = "Nostalgic Flint", + text = { + "No Flushes", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgic House", + text = { + "No Full Houses", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgic Manacle", + text = { + "Divide Mult by discards", + }, + }, + bl_cry_oldmark = { + name = "Nostalgic Mark", + text = { + "No hands that", + "contain a Pair", + }, + }, + bl_cry_oldox = { + name = "Nostalgic Ox", + text = { + "All hands start", + "with 0 Chips", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgic Pillar", + text = { + "No Straights", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgic Serpent", + text = { + "Divide Mult by level", + "of played poker hand", + }, + }, + bl_cry_pin = { + name = "The Pin", + text = { + "Jokers with Epic or higher", + "rarity are debuffed", + }, + }, + bl_cry_pinkbow = { + name = "Pink Bow", + text = { + "Randomize rank of cards", + "held in hand on play", + }, + }, + bl_cry_sapphire_stamp = { + name = "Sapphire Stamp", + text = { + "Select an extra card, deselect", + "random card before scoring", + }, + }, + bl_cry_shackle = { + name = "The Shackle", + text = { + "All Negative Jokers", + "are debuffed", + }, + }, + bl_cry_striker = { + name = "The Striker", + text = { + "All Rare Jokers", + "are debuffed", + }, + }, + bl_cry_tax = { + name = "The Tax", + text = { + "Score per hand capped at", + "0.4X blind requirements", + }, + }, + bl_cry_tornado = { + name = "Turquoise Tornado", + text = { + "#1# in #2# chance for", + "played hand to not score", + }, + }, + bl_cry_trick = { + name = "The Trick", + text = { + "After each hand, flip all", + "face-up cards held in hand", + }, + }, + bl_cry_vermillion_virus = { + name = "Vermillion Virus", + text = { + "One random Joker", + "replaced every hand", + }, + }, + bl_cry_windmill = { + name = "The Windmill", + text = { + "All Uncommon Jokers", + "are debuffed", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Convert {C:cry_code}#1#{} selected card", + "to a {C:cry_code}chosen{} enhancement", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}new{} Joker", + "of the {C:cry_code}same rarity", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Don't.", + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Permanently{} remove a", + "{C:cry_code}selected{} shop item", + "{C:inactive,s:0.8}Item cannot appear again this run", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "{C:cry_code}Halve{} all listed prices", + "in current shop", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "The {C:cry_code}next{} hand played", + "is calculated as a", + "{C:cry_code}chosen{} poker hand", + "{C:inactive,s:0.8}Secret hands must be", + "{C:inactive,s:0.8}discovered to be valid", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Select two Jokers", + "to become {C:cry_code}Hooked", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Add {C:dark_edition}Glitched{} to all", "cards {C:cry_code}held in hand" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Merge a selected {C:cry_code}consumable", + "with a selected {C:cry_code}playing card", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Double{} all values of", + "a selected {C:cry_code}Joker{} until", + "end of round", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Next defeated Blind", + "gives {C:cry_code}X#1#{} interest", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:cry_code}#1#{} extra card and", + "{C:cry_code}#1#{} extra choice", + "{C:inactive}(Currently {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Replenish {C:blue}Hands{} and {C:red}Discards{},", + "return {C:cry_code}all{} cards to deck", + "and draw a {C:cry_code}new{} hand", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Set {C:cry_code}game state{} to", + "start of {C:cry_code}this Ante{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}Rework Tag{} with", + "an {C:cry_code}upgraded{} edition", + "{C:inactive,s:0.8}Upgrades using order in the Collection", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Visit a {C:cry_code}shop", + "during a {C:cry_code}Blind", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Select a Joker", + "or playing card", + "to become {C:cry_code}Rigged", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Ends current non-Boss {C:cry_code}Blind{}", "{C:cry_code}without{} cashing out" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Create a {C:cry_code}Glitched", + "Food Joker", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convert {C:cry_code}#1#{} selected cards", + "to a {C:cry_code}chosen{} rank", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} Mult", + }, + }, + e_cry_blur = { + name = "Blurred", + text = { + "{C:attention}Retrigger{} this", + "card {C:attention}1{} time", + "{C:green}#1# in #2#{} chance", + "to retrigger {C:attention}#3#{}", + "additional time", + }, + }, + e_cry_double_sided = { + name = "Double-Sided", + text = { + "This card can be", + "{C:attention}flipped{} to reveal", + "a different card", + "{C:inactive}(Blank side can be merged", + "{C:inactive}with another card)", + }, + }, + e_cry_glass = { + name = "Fragile", + label = "Fragile", + text = { + "{C:white,X:mult} X#3# {} Mult", + "{C:green}#1# in #2#{} chance this", + "card isn't {C:red}destroyed", + "when triggered", + }, + }, + e_cry_glitched = { + name = "Glitched", + text = { + "All values on this card", + "are {C:dark_edition}randomized{}", + "between {C:attention}X0.1{} and {C:attention}X10{}", + "{C:inactive}(If possible){}", + }, + }, + e_cry_gold = { + name = "Golden", + label = "Golden", + text = { + "Earn {C:money}$#1#{} when used", + "or triggered", + }, + }, + e_cry_m = { + name = "Jolly", + text = { + "{C:mult}+#1#{} Mult", + "This card is feeling", + "rather {C:attention}jolly{}", + }, + }, + e_cry_mosaic = { + name = "Mosaic", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + e_cry_noisy = { + name = "Noisy", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Oversaturated", + text = { + "All values", + "on this card", + "are {C:attention}doubled{}", + "{C:inactive}(If possible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Echo Card", + text = { + "{C:green}#2# in #3#{} chance to", + "{C:attention}retrigger{} #1# additional", + "times when scored", + }, + }, + }, + Joker = { + j_cry_altgoogol = { + name = "Nostalgic Googol Play Card", + text = { + "Sell this card to create", + "{C:attention}2{} copies of the leftmost {C:attention}Joker{}", + "{C:inactive,s:0.8}Does not copy Nostalgic Googol Play Cards{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Like Antennas to Heaven", + text = { + "This Joker gains", + "{X:chips,C:white} X#1# {} Chips when each", + "played {C:attention}7{} or {C:attention}4{} is scored", + "{C:inactive}(Currently {X:chips,C:white}X#2# {C:inactive} Chips)", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "{X:mult,C:white} X#1# {} Mult against {C:attention}Boss Blinds{}" }, + }, + j_cry_big_cube = { + name = "Big Cube", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_biggestm = { + name = "Huge", + text = { + "{X:mult,C:white} X#1# {} Mult until end", + "of round if {C:attention}poker hand{}", + "is a {C:attention}#2#{}", + "{C:inactive}(Currently {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}not fat, just big boned.", + }, + }, + j_cry_blender = { + name = "Blender", + text = { + "Create a {C:attention}random{}", + "consumable when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_blurred = { + name = "Blurred Joker", + text = { + "Gain {C:blue}+#1#{} hand(s) when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Each {C:attention}Joker{} gives {C:chips}+#1#{} Chips", + "Increase amount by {C:chips}+#2#{} if", + "{C:attention} poker hand{} is a {C:attention}#3#{}", + "{C:inactive,s:0.8}Jolly Jokers give{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips instead{}", + }, + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Bonus{} card to increase", + "{C:attention}Joker{} or {C:attention}Consumable slots", + "by {C:dark_edition}1{} when scored", + "{C:red}Works twice per round", + "{C:inactive,s:0.8}(Equal chance for each){}", + }, + }, + j_cry_booster = { + name = "Booster Joker", + text = { + "{C:attention}+#1#{} Booster Pack slot", + "available in shop", + }, + }, + j_cry_boredom = { + name = "Boredom", + text = { + "{C:green}#1# in #2#{} chance to", + "{C:attention}retrigger{} each {C:attention}Joker{}", + "or {C:attention}played card{}", + "{C:inactive,s:0.8}Does not affect other Boredom{}", + }, + }, + j_cry_bubblem = { + name = "Bubble M", + text = { + "Create a {C:dark_edition}Foil {C:attention}Jolly Joker{}", + "if played hand contains", + "a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_busdriver = { + name = "Bus Driver", + text = { + "{C:green}#1# in #3#{} chance", + "for {C:mult}+#2#{} Mult", + "{C:green}1 in 4{} chance", + "for {C:mult}-#2#{} Mult", + }, + }, + j_cry_canvas = { + name = "Canvas", + text = { + "{C:attention}Retrigger{} all {C:attention}Jokers{} to the left", + "once for {C:attention}every{} non-{C:blue}Common{C:attention} Joker{}", + "to the right of this Joker", + }, + }, + j_cry_caramel = { + name = "Caramel", + text = { + "Each played card gives", + "{X:mult,C:white}X#1#{} Mult when scored", + "for the next {C:attention}#2#{} rounds", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Retrigger {C:attention}leftmost{} Joker", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_chili_pepper = { + name = "Chili Pepper", + text = { + "This Joker gains {X:mult,C:white} X#2# {} Mult", + "at end of round,", + "{C:red,E:2}self destructs{} after {C:attention}#3#{} rounds", + "{C:inactive}(Currently{} {X:mult,C:white} X#1# {} {C:inactive}Mult){}", + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Chips and {X:dark_edition,C:white}^#1#{} Mult", + "if {C:attention}exactly{} #2#", + "hands remaining", + }, + }, + j_cry_circus = { + name = "Circus", + text = { + "{C:red}Rare{} Jokers each give {X:mult,C:white} X#1# {} Mult", + "{C:cry_epic}Epic{} Jokers each give {X:mult,C:white} X#2# {} Mult", + "{C:legendary}Legendary{} Jokers each give {X:mult,C:white} X#3# {} Mult", + "{C:cry_exotic}Exotic{} Jokers each give {X:mult,C:white} X#4# {} Mult", + }, + }, + j_cry_CodeJoker = { + name = "Code Joker", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:cry_code}Code Card{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Earn between", + "{C:money}$#1#{} and {C:money}$#2#{} for", + "each Joker {C:attention}sold{}", + }, + }, + j_cry_compound_interest = { + name = "Compound Interest", + text = { + "Earn {C:money}#1#%{} of total money", + "at end of round,", + "increases by {C:money}#2#%{} per", + "consecutive payout", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "When a {C:cry_code}Code{} card is used,", + "{C:green}#1# in #2#{} chance to add a copy", + "to your consumable area", + "{C:inactive}(Must have room)", + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "per {C:attention}reroll{} in the shop", + "{C:green}All rerolls are free{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Chain", + text = { + "Sell this card to", + "add {C:money}$#1#{} of {C:attention}sell value{}", + "to every {C:attention}Joker{} card", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob = { + name = "Sob", + text = { + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}run...{}", + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}hide...{}", + "{C:dark_edition,E:1}you cannot escape...{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "for each card {C:attention}purchased{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Cut", + text = { + "This Joker destroys", + "a random {C:cry_code}Code{} card", + "and gains {X:mult,C:white} X#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Delirious Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "Doodle M", + text = { + "Create 2 {C:dark_edition}Negative{} {C:attention}consumables{}", + "when {C:attention}Blind{} is selected", + "Create 1 more {C:attention}consumable", + "for each {C:attention}Jolly Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Scale", + text = { + "Scaling {C:attention}Jokers{}", + "scale {C:attention}quadratically", + "{C:inactive,s:0.8}(ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grows by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult for", + "each played, {C:attention}nonscoring{} {V:1}#2#{} card,", + "suit changes every round", + "{C:inactive}(Currently {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubious Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_duos = { + name = "The Duos", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Every {C:attention}Joker{} gives", + "{X:dark_edition,C:white}^#1#{} Mult" + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Draw {C:green}full deck{} to hand", + "when {C:attention}Blind{} is selected", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "When a {C:attention}Tag{} is acquired,", + "create {C:attention}#1#{} copies of it", + "and {C:attention}increase{} the number of", + "copies by {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Jokers appear using the", + "order from the {C:attention}Collection{}", + "Create {C:attention}#1#{} {C:dark_edition}Negative{} Joker(s)", + "when hand is played", + "{C:cry_exotic,s:0.8}Exotic {C:inactive,s:0.8}or better Jokers cannot appear", + "{s:0.8}Last Joker Generated: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Eternal Flame", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "for each card {C:attention}sold{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographic{} cards", + "each give {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "when {X:red,C:white} XMult {} is triggered", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult if", + "played cards are scored", + "{C:attention}#2#{} or fewer times", + }, + }, + j_cry_filler = { + name = "The Filler", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fractal Fingers", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + j_cry_flip_side = { + name = "On the Flip Side", + text = { + "{C:dark_edition}Double-Sided{} Jokers use", + "their back side for effects", + "instead of the front side", + "{C:attention}Retrigger{} all {C:dark_edition}Double-Sided{} Jokers" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}self destructs{} in {C:attention}#2#{} round(s)", + "Increases by {C:attention}#3#{} round when", + "{C:attention}Jolly Joker{} is {C:attention}sold{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foxy = { + name = "Foxy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "if hand played is {C:attention}not{}", + "most played {C:attention}poker hand{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Earn {C:money}$#1#{} if {C:attention}played hand{}", + "contains an {C:attention}Ace{} and a {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} all values", + "of leftmost {C:attention}Joker", + "at end of round", + }, + }, + j_cry_giggly = { + name = "Absurd Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Earn {C:money}#1#%{} of total", + "money at end of round", + "Payout increases by {C:money}#2#%{}", + "when each played {C:attention}Gold{}", + "card is scored", + }, + }, + j_cry_googol_play = { + name = "Googol Play Card", + text = { + "{C:green}#1# in #2#{} chance for", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Create a random {C:attention}Joker{}", + "at end of round", + "Sell this card to", + "create a random {C:attention}Joker{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_happyhouse = { + name = "Happy House", + text = { + "{X:dark_edition,C:white}^#1#{} Mult only after", + "playing {C:attention}114{} hands{}", + "{C:inactive}(Currently #2#/114){}", + "{C:inactive,s:0.8}There is no place like home!{}", + }, + }, + j_cry_home = { + name = "The Home", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Earn {C:money}$#1#{} when", + "using a {C:attention}consumable{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Retrigger all cards played", + "{C:attention}#2#{} time(s),", + "each played card gives", + "{X:mult,C:white} X#1# {} Mult when scored", + }, + }, + j_cry_jimball = { + name = "Jimball", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "per {C:attention}consecutive{} hand played", + "while playing your", + "most played {C:attention}poker hand", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Jolly Joker?", + text = { + "Create a {C:dark_edition}Jolly{} Joker", + "when a Joker is {C:attention}sold{}", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{}", + "when a {C:attention}Type Mult{} or", + "{C:attention}Type Chips{} Joker is sold", + }, + }, + j_cry_kooky = { + name = "Kooky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty the Clown", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when", + "each played {C:attention}card{} is scored", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoscope", + text = { + "Add {C:dark_edition}Polychrome{} to", + "a random {C:attention}Joker{} when", + "{C:attention}Boss Blind{} is defeated", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Each played {C:attention}7{} or {C:attention}2{}", + "gives {X:mult,C:white}X#1#{} Mult when scored", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Give future copies of", + "this Joker {X:mult,C:white}X#1#{} Mult", + "at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Retrigger{} all Jokers", + "once for each {C:attention}Jolly{}", + "{C:attention}Joker{} sold this round", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Retrigger(s)){}", + "{C:inactive,s:0.8}There wasn't enough room...{}", + }, + }, + j_cry_lucky_joker = { + name = "Lucky Joker", + text = { + "Earn {C:money}$#1#{} every time a", + "{C:attention}Lucky{} card {C:green}successfully{}", + "triggers", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "All Jokers give", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "when {C:attention}Jolly Joker{} is sold", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:attention}Jolly Joker{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_macabre = { + name = "Macabre Joker", + text = { + "When {C:attention}Blind{} is selected,", + "destroys each {C:attention}Joker{} except", + "{C:legendary}M Jokers{} and {C:attention}Jolly Jokers{}", + "and create 1 {C:attention}Jolly Joker{}", + "for each destroyed card", + }, + }, + j_cry_magnet = { + name = "Fridge Magnet", + text = { + "Earn {C:money}$#1#{} at end of round", + "This earns {X:money,C:white} X#2# {} if there are", + "{C:attention}#3#{} or fewer {C:attention}Joker{} cards", + }, + }, + j_cry_manic = { + name = "Manic Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Retrigger all Jokers", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_maximized = { + name = "Maximized", + text = { + "All {C:attention}face{} cards", + "are considered {C:attention}Kings{},", + "all {C:attention}numbered{} cards", + "are considered {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Labyrinth", + text = { + "All hands are considered the", + "{C:attention}first hand{} of each round,", + "all discards are considered the", + "{C:attention}first discard{} of each round", + }, + }, + j_cry_Megg = { + name = "Megg", + text = { + "Sell this card to create", + "{C:attention}#2#{} Jolly #3#, increase", + "by {C:attention}#1#{} at end of round", + }, + }, + j_cry_membershipcard = { + name = "Membership Card", + text = { + "{X:mult,C:white}X#1#{} Mult for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Old Membership Card", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteor Shower", + text = { + "{C:dark_edition}Foil{} cards each", + "give {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by", + "{C:money}$#1#{} for each {C:attention}Jolly Joker{}", + "or {C:legendary}M Joker{} at", + "end of round", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if no {C:attention}discards{} were", + "used this round", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Monkey Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and permanently add {C:attention}ten times{}", + "its sell value to this {C:chips}Chips{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{} when", + "a card with an {C:attention}Edition{} is sold", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Create an {C:legendary}M Joker{} at end of round", + "Each {C:attention}Jolly Joker{} or {C:legendary}M Joker", + "gives {X:dark_edition,C:white}^#1#{} Mult", + "Increase amount by {X:dark_edition,C:white}^#2#{}", + "when a {C:attention}Jolly Joker{} is {C:attention}sold", + "{C:inactive,s:0.8}(Tredecim excluded)", + }, + }, + j_cry_mstack = { + name = "M Stack", + text = { + "Retrigger all cards played", + "once for every", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jolly Jokers{} sold", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} retriggers){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Mult{} card to create", + "a {C:spectral}Cryptid{} card when scored", + "{C:inactive}(Must have room)", + }, + }, + j_cry_negative = { + name = "Negative Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips if played hand", + "contains a {C:attention}6{} and a {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Night", + text = { + "{X:dark_edition,C:white}^#1#{} Mult on final", + "hand of round", + "{E:2,C:red}self destructs{} on", + "final hand of round", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Retrigger each played {C:attention}7{}", + "{C:attention:}#1#{} additional time(s)", + }, + }, + j_cry_notebook = { + name = "Notebook", + text = { + "{C:green} #1# in #2#{} chance to gain {C:dark_edition}+1{} Joker", + "slot per {C:attention}reroll{} in the shop", + "{C:green}Always triggers{} if there are", + "{C:attention}#5#{} or more {C:attention}Jolly Jokers{}", + "{C:red}Works once per round{}", + "{C:inactive}(Currently {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Number Blocks", + text = { + "Earn {C:money}$#1#{} at end of round", + "Increase payout by {C:money}$#2#{}", + "for each {C:attention}#3#{} held in hand,", + "rank changes every round", + }, + }, + j_cry_nuts = { + name = "The Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_oldblueprint = { + name = "Old Blueprint", + text = { + "Copies ability of", + "{C:attention}Joker{} to the right", + "{C:green}#1# in #2#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + j_cry_oldcandy = { + name = "Nostalgic Candy", + text = { + "Sell this card to", + "permanently gain", + "{C:attention}+#1#{} hand size", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgic Invisible Joker", + text = { + "{C:attention}Duplicate{} a random", + "{C:attention}Joker{} every {C:attention}4", + "Joker cards sold", + "{s:0.8}Nostalgic Invisible Joker Excluded{}", + "{C:inactive}(Currently #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panopticon", + text = { + "All hands are considered the", + "{C:attention}last hand{} of each round", -- +$4 + }, + }, + j_cry_pickle = { + name = "Pickle", + text = { + "When {C:attention}Blind{} is skipped, create", + "{C:attention}#1#{} Tags, reduced by", + "{C:red}#2#{} when {C:attention}Blind{} is selected", + }, + }, + j_cry_pirate_dagger = { + name = "Pirate Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "and gain {C:attention}one-fourth{} of", + "its sell value as {X:chips,C:white} XChips {}", + "{C:inactive}(Currently {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + j_cry_pot_of_jokes = { + name = "Pot of Jokes", + text = { + "{C:attention}#1#{} hand size,", + "increases by", + "{C:blue}#2#{} every round", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "if all cards in played hand are", + "{C:attention}Aces{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, or {C:attention}7s{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "If {C:attention}poker hand{} is a", + "{C:attention}Royal Flush{}, destroy scored", + "{C:attention}Queen{} and create a", + "{C:dark_edition}Negative {}{C:red}Rare{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "The Quintet", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Red Bloon", + text = { + "Earn {C:money}$#1#{} in {C:attention}#2#{} round(s)", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante when", + "{C:money}$#2#{} {C:inactive}($#3#){} spent", + "{s:0.8}Requirements increase", + "{C:attention,s:0.8}exponentially{s:0.8} per use", + "{C:money,s:0.8}Next increase: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "When a {C:attention}Joker{} is sold,", + "add its effects to", + "every other Joker", + "{C:inactive,s:0.8}Does not affect other Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fill all empty Joker slots {C:inactive}(Max 100){}", + "with {C:dark_edition}Holographic{} {C:attention}Jolly Jokers{} if", + "{C:attention}discarded poker hand{} is a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Randomize abilities each {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Create an {C:green}Uncommon{} Joker", + "and 3 {C:attention}Jolly Jokers{} when", + "a {C:spectral}Spectral{} card is used", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sapling", + text = { + "After scoring {C:attention}#2#{} {C:inactive}[#1#]{} Enhanced", + "cards, sell this card to", + "create an {C:cry_epic}Epic{} {C:attention}Joker{}", + "{C:inactive,s:0.8}Will create a {C:red,s:0.8}Rare{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}if {C:cry_epic,s:0.8}Epic{} {C:inactive,s:0.8}Jokers are disabled{}", + }, + }, + j_cry_savvy = { + name = "Savvy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Scaling {C:attention}Jokers{} scale", + "as a degree-{C:attention}#1#{} polynomial", + "raise degree by {C:attention}#2#{}", + "at end of round", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluded)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Tile", + text = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Jolly {C:green}Uncommon{} Joker", + "when hand is played", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Add a {C:attention}random seal{} to each card", + "scored on {C:attention}final hand{} of round", + }, + }, + j_cry_shrewd = { + name = "Shrewd Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Silly Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Tiny", + text = { + "Create a {C:cry_jolly}Double M", + "tag if {C:attention}poker hand{}", + "is a {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker slot", + "{C:attention}+#1#{} Booster Pack slot", + "{C:attention}+#1#{} hand size", + "{C:attention}+#1#{} consumable slot", + "{C:attention}+#1#{} card in shop", + }, + }, + j_cry_spaceglobe = { + name = "Celestial Globe", + text = { + "This Joker gains {X:chips,C:white}X#2#{} Chips", + "if {C:attention}poker hand{} is a {C:attention}#3#{},", + "Hand changes after increase{}", + "{C:inactive}(Currently{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Creates a {C:dark_edition}Negative{} copy", + "of a random {C:attention}Joker{}", + "at the end of the {C:attention}shop", + "{C:inactive,s:0.8}Does not copy other Speculo{}", + }, + }, + j_cry_stardust = { + name = "Stardust", + text = { + "{C:dark_edition}Polychrome{} cards", + "each give {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "This Joker destroys a", + "random {C:planet}Planet{} card", + "and gains {X:dark_edition,C:white} ^#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_subtle = { + name = "Subtle Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Earn {C:money}$#3#{} at", + "end of round", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "At end of round, create", + "a {C:attention}copy{} of a random", + "card {C:attention}held in hand{},", + "destroy all others", + "{C:attention,s:0.8}Kings{s:0.8} of {C:hearts,s:0.8}Hearts{s:0.8} are prioritized", + }, + }, + j_cry_swarm = { + name = "The Swarm", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Catalyst", + text = { + "Balances {C:chips}Chips{} and {C:mult}Mult{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + "Earn {C:money}$#2#{} at end of round", + }, + }, + j_cry_translucent = { + name = "Translucent Joker", + text = { + "Sell this card to create", + "a {C:attention}Banana Perishable{} copy", + "of a random {C:attention}Joker{}", + "{s:0.8,C:inactive}(Copy bypasses perish compat)", + }, + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Triplet Rhythm", + text = { + "{X:mult,C:white} X#1# {} Mult if scoring hand", + "contains {C:attention}exactly{} three {C:attention}3s", + }, + }, + j_cry_unity = { + name = "The Unity", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Poker hands{} gain", + "{X:red,C:white} X#1# {} Mult and {X:blue,C:white} X#1# {} Chips", + "when leveled up", + }, + }, + j_cry_unjust_dagger = { + name = "Unjust Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and gain {C:attention}one-fifth{} of", + "its sell value as {X:mult,C:white} XMult {}", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "This Joker gains {C:money}$#1#{} of {C:attention}sell value{}", + "if {C:attention}poker hand{} contains a {C:attention}#2#{}", + "Sell this card to create a", + "{C:dark_edition}Polychrome{} {C:attention}Jolly Joker{} for", + "every {C:money}$4{} of {C:attention}sell value{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Wacky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "All Jokers give", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "All Jokers give", + "{C:money}$#1#{} when triggered", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "This Joker gains", + "{C:mult}+#2#{} Mult when each played", + "{C:attention}Ace{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "is scored", + "{C:inactive}(Currently {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Retrigger each played {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} additional time(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Wheel of Hope", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when failing", + "a {C:attention}Wheel of Fortune{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if {C:attention}played hand{} contains a", + "{C:attention}2{} and {C:attention}7{} of different suits", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutron Star", + text = { + "Upgrade a random", + "poker hand by", + "{C:attention}1{} level for each", + "{C:attention}Neutron Star{} used", + "in this run", + "{C:inactive}(Currently{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# in #2#{} chance to", + "upgrade every", + "{C:legendary,E:1}poker hand{}", + "by {C:attention}1{} level", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "CCD Sleeve", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Conveyor Sleeve", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Critical Sleeve", + text = { + "After each hand played,", + "{C:green}1 in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}1 in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Encoded Sleeve", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balanced Sleeve", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}+2 Shop Slots", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unlimited Sleeve", + text = { + "You can select {C:attention}any", + "number of cards", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Misprinted Sleeve", + text = { + "Values of cards", + "are {C:attention}randomized", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Redeemed Sleeve", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Wormhole Sleeve", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + }, + Spectral = { + c_cry_analog = { + name = "Analog", + text = { + "Create {C:attention}#1#{} copies of a", + "random {C:attention}Joker{}, destroy", + "all other Jokers, {C:attention}+#2#{} Ante", + }, + }, + c_cry_gateway = { + name = "Gateway", + text = { + "Create a random", + "{C:cry_exotic,E:1}Exotic{C:attention} Joker{}, destroy", + "all other Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Lock", + text = { + "Remove {C:red}all{} stickers", + "from {C:red}all{} Jokers,", + "then apply {C:purple,E:1}Eternal{}", + "to a random {C:attention}Joker{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Create a card", + "of {C:cry_code}your choice", + "{C:inactive,s:0.8}(Exotic Jokers #1#excluded)", + }, + }, + c_cry_replica = { + name = "Replica", + text = { + "Convert all cards", + "held in hand", + "to a {C:attention}random{}", + "card held in hand", + }, + }, + c_cry_source = { + name = "Source", + text = { + "Add a {C:cry_code}Green Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_summoning = { + name = "Summoning", + text = { + "Create a random", + "{C:cry_epic}Epic{} {C:joker}Joker{}, destroy", + "one random {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Trade", + text = { + "{C:attention}Lose{} a random Voucher,", + "gain {C:attention}2{} random Vouchers", + }, + }, + c_cry_typhoon = { + name = "Typhoon", + text = { + "Add an {C:cry_azure}Azure Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_vacuum = { + name = "Vacuum", + text = { + "Removes {C:red}all {C:green}modifications{}", + "from {C:red}all{} cards held in hand,", + "Earn {C:money}$#1#{} per {C:green}modification{} removed", + "{C:inactive,s:0.7}(ex. Enhancements, Seals, Editions)", + }, + }, + c_cry_white_hole = { + name = "White Hole", + text = { + "{C:attention}Remove{} all hand levels,", + "upgrade {C:legendary,E:1}most played{} poker hand", + "by {C:attention}3{} for each removed level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pink Stake", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brown Stake", + colour = "Brown", + text = { + "All {C:attention}stickers{} are compatible", + "with each other", + }, + }, + stake_cry_yellow = { + name = "Yellow Stake", + colour = "Yellow", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Jade Stake", + colour = "Jade", + text = { + "Cards can be drawn {C:attention}face down{}", + }, + }, + stake_cry_cyan = { + name = "Cyan Stake", + colour = "Cyan", + text = { + "{C:green}Uncommon{} and {C:red}Rare{} Jokers are", + "less likely to appear", + }, + }, + stake_cry_gray = { + name = "Gray Stake", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Crimson Stake", + colour = "Crimson", + text = { + "Vouchers restock on {C:attention}even{} Antes", + }, + }, + stake_cry_diamond = { + name = "Diamond Stake", + colour = "Diamond", + text = { + "Must beat Ante {C:attention}10{} to win", + }, + }, + stake_cry_amber = { + name = "Amber Stake", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Pack slot", + }, + }, + stake_cry_bronze = { + name = "Bronze Stake", + colour = "Bronze", + text = { + "Vouchers are {C:attention}50%{} more expensive", + }, + }, + stake_cry_quartz = { + name = "Quartz Stake", + colour = "Quartz", + text = { + "Jokers can be {C:attention}Pinned{}", + "{s:0.8,C:inactive}(Stays pinned to the leftmost position){}", + }, + }, + stake_cry_ruby = { + name = "Ruby Stake", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds can become", + "{C:attention}Boss{} Blinds", + }, + }, + stake_cry_glass = { + name = "Glass Stake", + colour = "Glass", + text = { + "Cards can {C:attention}shatter{} when scored", + }, + }, + stake_cry_sapphire = { + name = "Sapphire Stake", + colour = "Sapphire", + text = { + "Lose {C:attention}25%{} of current money", + "at end of Ante", + "{s:0.8,C:inactive}(Up to $10){}", + }, + }, + stake_cry_emerald = { + name = "Emerald Stake", + colour = "Emerald", + text = { + "Cards, packs, and vouchers", + "can be {C:attention}face down{}", + "{s:0.8,C:inactive}(Unable to be viewed until purchased){}", + }, + }, + stake_cry_platinum = { + name = "Platinum Stake", + colour = "Platinum", + text = { + "Small Blinds are {C:attention}removed{}", + }, + }, + stake_cry_twilight = { + name = "Twilight Stake", + colour = "Twilight", + text = { + "Cards can be {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 in 10 chance of being destroyed each round){}", + }, + }, + stake_cry_verdant = { + name = "Verdant Stake", + colour = "Verdant", + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Ember Stake", + colour = "Ember", + text = { + "All items give no money when sold", + }, + }, + stake_cry_dawn = { + name = "Dawn Stake", + colour = "Dawn", + text = { + "Tarots and Spectrals target {C:attention}1", + "fewer card", + "{s:0.8,C:inactive}(Minimum of 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizon Stake", + colour = "Horizon", + text = { + "When blind selected, add a", + "{C:attention}random card{} to deck", + }, + }, + stake_cry_blossom = { + name = "Blossom Stake", + colour = "Blossom", + text = { + "{C:attention}Final{} Boss Blinds can appear", + "in {C:attention}any{} Ante", + }, + }, + stake_cry_azure = { + name = "Azure Stake", + colour = "Azure", + text = { + "Values on Jokers are reduced", + "by {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Ascendant Stake", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop slot", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Banana Tag", + text = { + "Creates {C:attention}#1#", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_bettertop_up = { + name = "Better Top-up Tag", + text = { + "Creates up to {C:attention}#1#", + "{C:green}Uncommon{} Jokers", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_better_voucher = { + name = "Golden Voucher Tag", + text = { + "Adds one Tier {C:attention}#1#{} Voucher", + "to the next shop", + }, + }, + tag_cry_blur = { + name = "Blurred Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Blurred{}", + }, + }, + tag_cry_booster = { + name = "Booster Tag", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:attention}double{} cards and", + "{C:attention}double{} choices", + }, + }, + tag_cry_bundle = { + name = "Bundle Tag", + text = { + "Create a {C:attention}Standard Tag{}, {C:tarot}Charm Tag{},", + "{C:attention}Buffoon Tag{}, and {C:planet}Meteor Tag", + }, + }, + tag_cry_cat = { + name = "Cat Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Console Tag", + text = { + "Gives a free", + "{C:cry_code}Program Pack", + }, + }, + tag_cry_double_m = { + name = "Double M Tag", + text = { + "Shop has a", + "{C:dark_edition}Jolly {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Empowered Tag", + text = { + "Gives a free {C:spectral}Spectral Pack", + "with {C:legendary,E:1}The Soul{} and {C:cry_exotic,E:1}Gateway{}", + }, + }, + tag_cry_epic = { + name = "Epic Tag", + text = { + "Shop has a half-price", + "{C:cry_epic}Epic Joker", + }, + }, + tag_cry_gambler = { + name = "Gambler's Tag", + text = { + "{C:green}#1# in #2#{} chance to create", + "an {C:cry_exotic,E:1}Empowered Tag", + }, + }, + tag_cry_glass = { + name = "Fragile Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Fragile{}", + }, + }, + tag_cry_glitched = { + name = "Glitched Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Glitched{}", + }, + }, + tag_cry_gold = { + name = "Golden Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Golden{}", + }, + }, + tag_cry_gourmand = { + name = "Gourmand Tag", + text = { + "Shop has a free", + "{C:attention}Food Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gives a free", + "{C:cry_ascendant}Meme Pack", + }, + }, + tag_cry_m = { + name = "Jolly Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Jolly{}", + }, + }, + tag_cry_memory = { + name = "Memory Tag", + text = { + "Create {C:attention}#1#{} copies of", + "the last {C:attention}Tag{} used", + "during this run", + "{s:0.8,C:inactive}Copying Tags excluded", + "{s:0.8,C:inactive}Currently: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Mosaic{}", + }, + }, + tag_cry_oversat = { + name = "Oversaturated Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Oversaturated{}", + }, + }, + tag_cry_quadruple = { + name = "Quadruple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_quintuple = { + name = "Quintuple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_rework = { + name = "Rework Tag", + text = { + "Shop has a(n)", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schematic Tag", + text = { + "Shop has a", + "{C:attention}Brainstorm", + }, + }, + tag_cry_scope = { + name = "Scope Tag", + text = { + "{C:attention}+#1# {C:blue}hands{} and", + "{C:red}discards{} next round", + }, + }, + tag_cry_triple = { + name = "Triple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "The Automaton", + text = { + "Creates up to {C:attention}#1#", + "random {C:cry_code}Code{} card", + "{C:inactive}(Must have room)", + }, + }, + c_cry_eclipse = { + name = "The Eclipse", + text = { + "Enhances {C:attention}#1#{} selected card", + "into an {C:attention}Echo Card", + }, + }, + c_cry_meld = { + name = "Meld", + text = { + "Select a {C:attention}Joker{} or", + "{C:attention}playing card{} to", + "become {C:dark_edition}Double-Sided", + }, + }, + c_cry_theblessing = { + name = "The Blessing", + text = { + "Creates {C:attention}1{}", + "random {C:attention}consumable{}", + "{C:inactive}(Must have room){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglyph", + text = { + "Set Ante to {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Blank Canvas", + text = { + "{C:attention}+#1#{} hand size", + }, + }, + v_cry_clone_machine = { + name = "Clone Machine", + text = { + "Double Tags become", + "{C:attention}Quintuple Tags{} and", + "are {C:attention}4X{} as common", + }, + }, + v_cry_command_prompt = { + name = "Command Prompt", + text = { + "{C:cry_code}Code{} cards", + "can appear", + "in the {C:attention}shop{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Double Tags become", + "{C:attention}Triple Tags{} and are", + "{C:attention}2X{} as common", + }, + }, + v_cry_curate = { + name = "Curate", + text = { + "All cards", + "appear with", + "an {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Dexterity", + text = { + "Permanently", + "gain {C:blue}+#1#{} hand(s)", + "each round", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "After every round,", + "{X:dark_edition,C:white} X1.5 {} to all values", + "on the back of", + "{C:dark_edition}Double-Sided{} cards" + }, + }, + v_cry_double_slit = { + name = "Double Slit", + text = { + "{C:attention}Meld{} can appear", + "in the shop and", + "Arcana Packs", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "{C:dark_edition}Double-Sided{} cards appear", + "{C:attention}4X{} more frequently", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker slot(s)", + }, + }, + v_cry_massproduct = { + name = "Mass Production", + text = { + "All cards and packs", + "in shop cost {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Raise the cap on", + "interest earned in", + "each round to {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multistock", + text = { + "{C:attention}+#1#{} card slot(s) and", + "{C:attention}+#1#{} booster pack slot(s)", + "available in shop", + }, + }, + v_cry_pacclimator = { + name = "Planet Acclimator", + text = { + "{C:planet}Planet{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:planet}Planet{}", + "cards are {C:green}free{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Retrigger{} all M Jokers", + "once for every Pair", + "{C:attention}contained{} in played hand", + }, + }, + v_cry_pairing = { + name = "Pairing", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand is a {C:attention}Pair", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computing", + text = { + "{C:cry_code}Code{} cards can spawn", + "with {C:dark_edition}Negative{} edition", + }, + }, + v_cry_repair_man = { + name = "Repair Man", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand contains a {C:attention}Pair", + }, + }, + v_cry_rerollexchange = { + name = "Reroll Exchange", + text = { + "All rerolls", + "cost {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satellite Uplink", + text = { + "{C:cry_code}Code{} cards may", + "appear in any of", + "the {C:attention}Celestial Packs{}", + }, + }, + v_cry_scope = { + name = "Galactic Scope", + text = { + "Create the {C:planet}Planet", + "card for played", + "{C:attention}poker hand{}", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Tarot Acclimator", + text = { + "{C:tarot}Tarot{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:tarot}Tarot{}", + "cards are {C:green}free{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Printer", + text = { + "Double Tags become", + "{C:attention}Quadruple Tags{} and", + "are {C:attention}3X{} as common", + }, + }, + v_cry_threers = { + name = "The 3 Rs", + text = { + "Permanently", + "gain {C:red}+#1#{} discard(s)", + "each round", + }, + }, + v_cry_stickyhand = { + name = "Sticky Hand", + text = { + "{C:attention}+#1#{} card", + "selection limit", + }, + }, + v_cry_grapplinghook = { + name = "Grappling Hook", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperspace Tether", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "destroyed each round", + }, + }, + cry_rigged = { + name = "Rigged", + text = { + "All {C:cry_code}listed{} probabilities", + "are {C:cry_code}guaranteed", + }, + }, + cry_hooked = { + name = "Hooked", + text = { + "When this Joker is {C:cry_code}triggered{},", + "trigger {C:cry_code}#1#", + }, + }, + food_jokers = { + name = "Food Jokers", + text = { + "{s:0.8}Gros Michel, Egg, Ice Cream, Cavendish,", + "{s:0.8}Turtle Bean, Diet Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Pickle, Chili Pepper, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eternal", + text = { + "All cards in pack", + "are {C:attention}Eternal{}", + }, + }, + cry_perishable_booster = { + name = "Perishable", + text = { + "All cards in pack", + "are {C:attention}Perishable{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "All cards in pack", + "are {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Pinned", + text = { + "All cards in pack", + "are {C:attention}Pinned{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "All cards in pack", + "are {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eternal", + text = { + "Can't be traded", + }, + }, + cry_perishable_voucher = { + name = "Perishable", + text = { + "Debuffed after", + "{C:attention}#1#{} rounds", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)", + }, + }, + cry_rental_voucher = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at", + "end of round", + }, + }, + cry_pinned_voucher = { + name = "Pinned", + text = { + "Remains in shop", + "until redeemed", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "unredeemed each round", + }, + }, + cry_perishable_consumeable = { + name = "Perishable", + text = { + "Debuffed at", + "end of round", + }, + }, + cry_rental_consumeable = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at end of", + "round, and on use", + }, + }, + cry_pinned_consumeable = { + name = "Pinned", + text = { + "Can't use other", + "non-{C:attention}Pinned{} consumables", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance to do", + "nothing on use", + }, + }, + p_cry_code_normal_1 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_normal_2 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_two = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_three = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + undiscovered_code = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + cry_green_seal = { + name = "Green Seal", + text = { + "Creates a {C:cry_code}Code{} card", + "when played and unscoring", + "{C:inactive}(Must have room)", + }, + }, + cry_azure_seal = { + name = "Azure Seal", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planets{} for played", + "{C:attention}poker hand{}, then", + "{C:red}destroy{} this card", + }, + }, + }, + }, + misc = { + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Legally Blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Break Infinity", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Home Realtor", + ach_cry_jokes_on_you = "Joke's on You, Pal!", + ach_cry_niw_uoy = "!niW uoY", + ach_cry_now_the_fun_begins = "Now the Fun Begins", + ach_cry_patience_virtue = "Patience is a Virtue", + ach_cry_perfectly_balanced = "Perfectly Balanced", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Traffic Jam", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "We Told You Not To", + ach_cry_what_have_you_done = "WHAT HAVE YOU DONE?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtain Blurred Blurred Joker", + ach_cry_bullet_hell = "Have 15 AP Jokers", + ach_cry_break_infinity = "Score 1.79e308 Chips in a single hand", + ach_cry_cryptid_the_cryptid = "Use Cryptid on Cryptid", + ach_cry_exodia = "Have 5 Exotic Jokers", + ach_cry_freak_house = "Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice", + ach_cry_googol_play_pass = "Rig a Googol Play Card", + ach_cry_haxxor = "Use a cheat code", + ach_cry_home_realtor = "Activate Happy House before Ante 8 (without DoE/Antimatter)", + ach_cry_jokes_on_you = "Trigger The Joke's effect on Ante 1 and win the run", + ach_cry_niw_uoy = "Reach Ante -8", + ach_cry_now_the_fun_begins = "Obtain Canvas", + ach_cry_patience_virtue = "Wait out Lavender Loop for 2 minutes before playing first hand and beat the blind", + ach_cry_perfectly_balanced = "Beat Very Fair Deck on Ascendant Stake", + ach_cry_pull_request = "Have ://COMMIT spawn the same Joker that it destroyed", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Win in 1 round", + ach_cry_used_crash = "Use ://CRASH", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Music", + cry_set_enable_features = "Select features to enable (applies on game restart):", + cry_feat_achievements = "Achievements", + ["cry_feat_antimatter deck"] = "Antimatter Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Code Cards", + ["cry_feat_misc. decks"] = "Misc. Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechanics", + ["cry_feat_enhanced decks"] = "Enhanced Decks", + ["cry_feat_epic jokers"] = "Epic Jokers", + ["cry_feat_exotic jokers"] = "Exotic Jokers", + ["cry_feat_m jokers"] = "M Jokers", + cry_feat_menu = "Custom Main Menu", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Misc. Jokers", + cry_feat_planets = "Planets", + cry_feat_jokerdisplay = "JokerDisplay (Does Nothing)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Sleeves", + cry_feat_spectrals = "Spectrals", + ["cry_feat_more stakes"] = "Stakes", + cry_feat_vouchers = "Vouchers", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Pack", + k_cry_meme_pack = "Meme Pack", + + cry_critical_hit_ex = "Critical Hit!", + cry_critical_miss_ex = "Critical Miss!", + + cry_debuff_oldhouse = "No Full Houses", + cry_debuff_oldarm = "Must play 4 or fewer cards", + cry_debuff_oldpillar = "No Straights", + cry_debuff_oldflint = "No Flushes", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applies abilities of all defeated bosses", + + k_code = "Code", + b_code_cards = "Code Cards", + b_pull = "PULL", + cry_hooked_ex = "Hooked!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTER RANK", + cry_code_enh = "ENTER ENHANCEMENT", + cry_code_hand = "ENTER POKER HAND", + cry_code_enter_card = "ENTER A CARD", + cry_code_apply = "APPLY", + cry_code_apply_previous = "APPLY PREVIOUS", + cry_code_exploit = "EXPLOIT", + cry_code_exploit_previous = "EXPLOIT PREVIOUS", + cry_code_create = "CREATE", + cry_code_create_previous = "CREATE PREVIOUS", + cry_code_execute = "EXECUTE", + cry_code_cancel = "CANCEL", + + b_flip = "FLIP", + b_merge = "MERGE", + + cry_again_q = "Again?", + cry_curse = "Curse", + cry_curse_ex = "Curse!", + cry_sobbing = "Help me...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Round", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "No triggers left!", + cry_unredeemed = "Unredeemed...", + cry_active = "Active", + cry_inactive = "Inactive", + + k_disable_music = "Disable Music", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Copyright Notice", + cry_notif_jimball_d1 = "Jimball plays the song \"Funkytown\",", + cry_notif_jimball_d2 = "which is copyrighted and can't be", + cry_notif_jimball_d3 = "used for streams and videos.", + }, + labels = { + food_jokers = "Food Jokers", + banana = "Banana", + code = "Code", + cry_rigged = "Rigged", + cry_hooked = "Hooked", + + cry_green_seal = "Green Seal", + cry_azure_seal = "Azure Seal", + + cry_astral = "Astral", + cry_blur = "Blurred", + cry_double_sided = "Double-Sided", + cry_glass = "Fragile", + cry_glitched = "Glitched", + cry_gold = "Golden", + cry_m = "Jolly", + cry_mosaic = "Mosaic", + cry_noisy = "Noisy", + cry_oversat = "Oversaturated", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic" + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + }, + v_text = { + ch_c_cry_all_perishable = {"All Jokers are {C:eternal}Perishable{}"}, + ch_c_cry_all_rental = {"All Jokers are {C:eternal}Rental{}"}, + ch_c_cry_all_pinned = {"All Jokers are {C:eternal}Pinned{}"}, + ch_c_cry_all_banana = {"All Jokers are {C:eternal}Banana{}"}, + ch_c_all_rnj = {"All Jokers are {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"All purchasable items have all stickers"}, + ch_c_cry_rush_hour = {"All Boss Blinds are {C:attention}The Clock{} or {C:attention}Lavender Loop"}, + ch_c_cry_rush_hour_ii = {"All Blinds are {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}The Clock{} and {C:attention}Lavender Loop{} scale {C:attention}twice{} as fast"}, + ch_c_cry_no_tags = {"Skipping is {C:attention}disabled{}"}, + ch_c_cry_no_vouchers = {"{C:attention}Vouchers{} no longer appear in the shop"}, + ch_c_cry_no_boosters = {"{C:attention}Booster Packs{} no longer appear in the shop"}, + ch_c_cry_no_rerolls = {"Rerolling is {C:attention}disabled{}"}, + ch_c_cry_no_consumables = {"{C:attention}Consumables{} no longer appear"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/nl.lua b/Cryptid/localization/nl.lua new file mode 100644 index 0000000..77dca8e --- /dev/null +++ b/Cryptid/localization/nl.lua @@ -0,0 +1,3298 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +--Wip Localization, putting this here since people have expressed interst in this +--I'm Assuming de is the file for dutch? just rename it if it isn't + +--[[ +Progress: + +Decks: No +Jokers: No +Code Cards: No +Deck Sleeves (requires Decksleeves Mod): No +Boss Blinds: No +Spectrals: No +Tarots: No +Dictonary: No +Editions: No +Vouchers: No +Enhancements (aka echo card): No +Tags: No +Other (packs, stickers, etc): No +Misc: No + +]]-- +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antimatter Deck", + text = { + "Applies the {C:legendary,E:1}upsides{}", + "of {C:attention}every{} deck", + }, + }, + b_cry_blank = { + name = "Blank Deck", + text = { + "{C:inactive,E:1}Does nothing?", + }, + }, + b_cry_CCD = { + name = "CCD Deck", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + b_cry_conveyor = { + name = "Conveyor Deck", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + b_cry_critical = { + name = "Critical Deck", + text = { + "After each hand played,", + "{C:green}#1# in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}#1# in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + b_cry_encoded = { + name = "Encoded Deck", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + b_cry_equilibrium = { + name = "Deck of Equilibrium", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}Overstock Plus", + }, + }, + b_cry_glowing = { + name = "Glowing Deck", + text = { + "Multiply the values of", + "all Jokers by {X:dark_edition,C:white} X1.25 {}", + "when Boss Blind is defeated", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Infinite Deck", + text = { + "You can select {C:attention}any", + "number of cards", + "{C:attention}+1{} hand size", + }, + }, + b_cry_misprint = { + name = "Misprint Deck", + text = { + "Values of cards", + "and poker hands", + "are {C:attention}randomized", + }, + }, + b_cry_redeemed = { + name = "Redeemed Deck", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + b_cry_very_fair = { + name = "Very Fair Deck", + text = { + "{C:blue}-2{} hands, {C:red}-2{} discards", + "every round", + "{C:attention}Vouchers{} no longer", + "appear in the shop", + }, + }, + b_cry_wormhole = { + name = "Wormhole Deck", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "The Box", + text = { + "All Common Jokers", + "are debuffed", + }, + }, + bl_cry_clock = { + name = "The Clock", + text = { + "+0.1X blind requirements every", + "3 seconds spent this ante", + }, + }, + bl_cry_hammer = { + name = "The Hammer", + text = { + "All cards with odd", + "rank are debuffed", + }, + }, + bl_cry_joke = { + name = "The Joke", + text = { + "If score is >2X requirements,", + "set ante to multiple of #1#", + }, + }, + bl_cry_magic = { + name = "The Magic", + text = { + "All cards with even", + "rank are debuffed", + }, + }, + bl_cry_lavender_loop = { + name = "Lavender Loop", + text = { + "1.25X blind requirements every", + "1.5 seconds spent this round", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidian Orb", + text = { + "Applies abilities of", + "all defeated bosses", + }, + }, + bl_cry_oldarm = { + name = "Nostalgic Arm", + text = { + "Must play 4", + "or fewer cards", + }, + }, + bl_cry_oldfish = { + name = "Nostalgic Fish", + text = { + "All hands start", + "with 1 Mult", + }, + }, + bl_cry_oldflint = { + name = "Nostalgic Flint", + text = { + "No Flushes", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgic House", + text = { + "No Full Houses", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgic Manacle", + text = { + "Divide Mult by discards", + }, + }, + bl_cry_oldmark = { + name = "Nostalgic Mark", + text = { + "No hands that", + "contain a Pair", + }, + }, + bl_cry_oldox = { + name = "Nostalgic Ox", + text = { + "All hands start", + "with 0 Chips", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgic Pillar", + text = { + "No Straights", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgic Serpent", + text = { + "Divide Mult by level", + "of played poker hand", + }, + }, + bl_cry_pin = { + name = "The Pin", + text = { + "Jokers with Epic or higher", + "rarity are debuffed", + }, + }, + bl_cry_pinkbow = { + name = "Pink Bow", + text = { + "Randomize rank of cards", + "held in hand on play", + }, + }, + bl_cry_sapphire_stamp = { + name = "Sapphire Stamp", + text = { + "Select an extra card, deselect", + "random card before scoring", + }, + }, + bl_cry_shackle = { + name = "The Shackle", + text = { + "All Negative Jokers", + "are debuffed", + }, + }, + bl_cry_striker = { + name = "The Striker", + text = { + "All Rare Jokers", + "are debuffed", + }, + }, + bl_cry_tax = { + name = "The Tax", + text = { + "Score per hand capped at", + "0.4X blind requirements", + }, + }, + bl_cry_tornado = { + name = "Turquoise Tornado", + text = { + "#1# in #2# chance for", + "played hand to not score", + }, + }, + bl_cry_trick = { + name = "The Trick", + text = { + "After each hand, flip all", + "face-up cards held in hand", + }, + }, + bl_cry_vermillion_virus = { + name = "Vermillion Virus", + text = { + "One random Joker", + "replaced every hand", + }, + }, + bl_cry_windmill = { + name = "The Windmill", + text = { + "All Uncommon Jokers", + "are debuffed", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Convert {C:cry_code}#1#{} selected card", + "to a {C:cry_code}chosen{} enhancement", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}new{} Joker", + "of the {C:cry_code}same rarity", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Don't.", + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Permanently{} remove a", + "{C:cry_code}selected{} shop item", + "{C:inactive,s:0.8}Item cannot appear again this run", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "{C:cry_code}Halve{} all listed prices", + "in current shop", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "The {C:cry_code}next{} hand played", + "is calculated as a", + "{C:cry_code}chosen{} poker hand", + "{C:inactive,s:0.8}Secret hands must be", + "{C:inactive,s:0.8}discovered to be valid", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Select two Jokers", + "to become {C:cry_code}Hooked", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Add {C:dark_edition}Glitched{} to all", "cards {C:cry_code}held in hand" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Merge a selected {C:cry_code}consumable", + "with a selected {C:cry_code}playing card", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Double{} all values of", + "a selected {C:cry_code}Joker{} until", + "end of round", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Next defeated Blind", + "gives {C:cry_code}X#1#{} interest", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:cry_code}#1#{} extra card and", + "{C:cry_code}#1#{} extra choice", + "{C:inactive}(Currently {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Replenish {C:blue}Hands{} and {C:red}Discards{},", + "return {C:cry_code}all{} cards to deck", + "and draw a {C:cry_code}new{} hand", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Set {C:cry_code}game state{} to", + "start of {C:cry_code}this Ante{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}Rework Tag{} with", + "an {C:cry_code}upgraded{} edition", + "{C:inactive,s:0.8}Upgrades using order in the Collection", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Visit a {C:cry_code}shop", + "during a {C:cry_code}Blind", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Select a Joker", + "or playing card", + "to become {C:cry_code}Rigged", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Ends current non-Boss {C:cry_code}Blind{}", "{C:cry_code}without{} cashing out" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Create a {C:cry_code}Glitched", + "Food Joker", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convert {C:cry_code}#1#{} selected cards", + "to a {C:cry_code}chosen{} rank", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} Mult", + }, + }, + e_cry_blur = { + name = "Blurred", + text = { + "{C:attention}Retrigger{} this", + "card {C:attention}1{} time", + "{C:green}#1# in #2#{} chance", + "to retrigger {C:attention}#3#{}", + "additional time", + }, + }, + e_cry_double_sided = { + name = "Double-Sided", + text = { + "This card can be", + "{C:attention}flipped{} to reveal", + "a different card", + }, + }, + e_cry_glass = { + name = "Fragile", + label = "Fragile", + text = { + "{C:white,X:mult} X#3# {} Mult", + "{C:green}#1# in #2#{} chance this", + "card isn't {C:red}destroyed", + "when triggered", + }, + }, + e_cry_glitched = { + name = "Glitched", + text = { + "All values on this card", + "are {C:dark_edition}randomized{}", + "between {C:attention}X0.1{} and {C:attention}X10{}", + "{C:inactive}(If possible){}", + }, + }, + e_cry_gold = { + name = "Golden", + label = "Golden", + text = { + "Earn {C:money}$#1#{} when used", + "or triggered", + }, + }, + e_cry_m = { + name = "Jolly", + text = { + "{C:mult}+#1#{} Mult", + "This card is feeling", + "rather {C:attention}jolly{}", + }, + }, + e_cry_mosaic = { + name = "Mosaic", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + e_cry_noisy = { + name = "Noisy", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Oversaturated", + text = { + "All values", + "on this card", + "are {C:attention}doubled{}", + "{C:inactive}(If possible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Echo Card", + text = { + "{C:green}#2# in #3#{} chance to", + "{C:attention}retrigger{} #1# additional", + "times when scored", + }, + }, + }, + Joker = { + j_cry_altgoogol = { + name = "Nostalgic Googol Play Card", + text = { + "Sell this card to create", + "{C:attention}2{} copies of the leftmost {C:attention}Joker{}", + "{C:inactive,s:0.8}Does not copy Nostalgic Googol Play Cards{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Like Antennas to Heaven", + text = { + "This Joker gains", + "{X:chips,C:white} X#1# {} Chips when each", + "played {C:attention}7{} or {C:attention}4{} is scored", + "{C:inactive}(Currently {X:chips,C:white}X#2# {C:inactive} Chips)", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "{X:mult,C:white} X#1# {} Mult against {C:attention}Boss Blinds{}" }, + }, + j_cry_big_cube = { + name = "Big Cube", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_biggestm = { + name = "Huge", + text = { + "{X:mult,C:white} X#1# {} Mult until end", + "of round if {C:attention}poker hand{}", + "is a {C:attention}#2#{}", + "{C:inactive}(Currently {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}not fat, just big boned.", + }, + }, + j_cry_blender = { + name = "Blender", + text = { + "Create a {C:attention}random{}", + "consumable when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_blurred = { + name = "Blurred Joker", + text = { + "Gain {C:blue}+#1#{} hand(s) when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Each {C:attention}Joker{} gives {C:chips}+#1#{} Chips", + "Increase amount by {C:chips}+#2#{} if", + "{C:attention} poker hand{} is a {C:attention}#3#{}", + "{C:inactive,s:0.8}Jolly Jokers give{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips instead{}", + }, + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Bonus{} card to increase", + "{C:attention}Joker{} or {C:attention}Consumable slots", + "by {C:dark_edition}1{} when scored", + "{C:red}Works twice per round", + "{C:inactive,s:0.8}(Equal chance for each){}", + }, + }, + j_cry_booster = { + name = "Booster Joker", + text = { + "{C:attention}+#1#{} Booster Pack slot", + "available in shop", + }, + }, + j_cry_boredom = { + name = "Boredom", + text = { + "{C:green}#1# in #2#{} chance to", + "{C:attention}retrigger{} each {C:attention}Joker{}", + "or {C:attention}played card{}", + "{C:inactive,s:0.8}Does not affect other Boredom{}", + }, + }, + j_cry_bubblem = { + name = "Bubble M", + text = { + "Create a {C:dark_edition}Foil {C:attention}Jolly Joker{}", + "if played hand contains", + "a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_busdriver = { + name = "Bus Driver", + text = { + "{C:green}#1# in #3#{} chance", + "for {C:mult}+#2#{} Mult", + "{C:green}1 in 4{} chance", + "for {C:mult}-#2#{} Mult", + }, + }, + j_cry_canvas = { + name = "Canvas", + text = { + "{C:attention}Retrigger{} all {C:attention}Jokers{} to the left", + "once for {C:attention}every{} non-{C:blue}Common{C:attention} Joker{}", + "to the right of this Joker", + }, + }, + j_cry_caramel = { + name = "Caramel", + text = { + "Each played card gives", + "{X:mult,C:white}X#1#{} Mult when scored", + "for the next {C:attention}#2#{} rounds", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Retrigger {C:attention}leftmost{} Joker", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_chili_pepper = { + name = "Chili Pepper", + text = { + "This Joker gains {X:mult,C:white} X#2# {} Mult", + "at end of round,", + "{C:red,E:2}self destructs{} after {C:attention}#3#{} rounds", + "{C:inactive}(Currently{} {X:mult,C:white} X#1# {} {C:inactive}Mult){}", + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Chips, {X:dark_edition,C:white}^#1#{} Mult", + "if {C:attention}exactly{} #2#", + "hands remaining", + }, + }, + j_cry_circus = { + name = "Circus", + text = { + "{C:red}Rare{} Jokers each give {X:mult,C:white} X#1# {} Mult", + "{C:cry_epic}Epic{} Jokers each give {X:mult,C:white} X#2# {} Mult", + "{C:legendary}Legendary{} Jokers each give {X:mult,C:white} X#3# {} Mult", + "{C:cry_exotic}Exotic{} Jokers each give {X:mult,C:white} X#4# {} Mult", + }, + }, + j_cry_CodeJoker = { + name = "Code Joker", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:cry_code}Code Card{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Earn between", + "{C:money}$#1#{} and {C:money}$#2#{} for", + "each Joker {C:attention}sold{}", + }, + }, + j_cry_compound_interest = { + name = "Compound Interest", + text = { + "Earn {C:money}#1#%{} of total money", + "at end of round,", + "increases by {C:money}#2#%{} per", + "consecutive payout", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "When a {C:cry_code}Code{} card is used,", + "{C:green}#1# in #2#{} chance to add a copy", + "to your consumable area", + "{C:inactive}(Must have room)", + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "per {C:attention}reroll{} in the shop", + "{C:green}All rerolls are free{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Chain", + text = { + "Sell this card to", + "add {C:money}$#1#{} of {C:attention}sell value{}", + "to every {C:attention}Joker{} card", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob = { + name = "Sob", + text = { + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}run...{}", + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}hide...{}", + "{C:dark_edition,E:1}you cannot escape...{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "for each card {C:attention}purchased{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Cut", + text = { + "This Joker destroys", + "a random {C:cry_code}Code{} card", + "and gains {X:mult,C:white} X#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Delirious Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "Doodle M", + text = { + "Create 2 {C:dark_edition}Negative{} {C:attention}consumables{}", + "when {C:attention}Blind{} is selected", + "Create 1 more {C:attention}consumable", + "for each {C:attention}Jolly Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Scale", + text = { + "Scaling {C:attention}Jokers{}", + "scale {C:attention}quadratically", + "{C:inactive,s:0.8}(ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grows by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult for", + "each played, {C:attention}nonscoring{} {V:1}#2#{} card,", + "suit changes every round", + "{C:inactive}(Currently {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubious Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_duos = { + name = "The Duos", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Every {C:attention}Joker{} gives", + "{X:dark_edition,C:white}^#1#{} Mult" + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Draw {C:green}full deck{} to hand", + "when {C:attention}Blind{} is selected", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "When a {C:attention}Tag{} is acquired,", + "create {C:attention}#1#{} copies of it", + "and {C:attention}increase{} the number of", + "copies by {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Jokers appear using the", + "order from the {C:attention}Collection{}", + "Create {C:attention}#1#{} {C:dark_edition}Negative{} Joker(s)", + "when hand is played", + "{C:cry_exotic,s:0.8}Exotic {C:inactive,s:0.8}or better Jokers cannot appear", + "{s:0.8}Last Joker Generated: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Eternal Flame", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "for each card {C:attention}sold{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographic{} cards", + "each give {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "when {X:red,C:white} XMult {} is triggered", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult if", + "played cards are scored", + "{C:attention}#2#{} or fewer times", + }, + }, + j_cry_filler = { + name = "The Filler", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fractal Fingers", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + j_cry_flip_side = { + name = "On the Flip Side", + text = { + "{C:dark_edition}Double-Sided{} Jokers use", + "their back side for effects", + "{C:attention}Retrigger{} all {C:dark_edition}Double-Sided{} Jokers" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}self destructs{} in {C:attention}#2#{} round(s)", + "Increases by {C:attention}#3#{} round when", + "{C:attention}Jolly Joker{} is {C:attention}sold{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foxy = { + name = "Foxy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "if hand played is {C:attention}not{}", + "most played {C:attention}poker hand{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Earn {C:money}$#1#{} if {C:attention}played hand{}", + "contains an {C:attention}Ace{} and a {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} all values", + "of leftmost {C:attention}Joker", + "at end of round", + }, + }, + j_cry_giggly = { + name = "Absurd Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Earn {C:money}#1#%{} of total", + "money at end of round", + "Payout increases by {C:money}#2#%{}", + "when each played {C:attention}Gold{}", + "card is scored", + }, + }, + j_cry_googol_play = { + name = "Googol Play Card", + text = { + "{C:green}#1# in #2#{} chance for", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Create a random {C:attention}Joker{}", + "at end of round", + "Sell this card to", + "create a random {C:attention}Joker{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_happyhouse = { + name = "Happy House", + text = { + "{X:dark_edition,C:white}^#1#{} Mult only after", + "playing {C:attention}114{} hands{}", + "{C:inactive}(Currently #2#/114){}", + "{C:inactive,s:0.8}There is no place like home!{}", + }, + }, + j_cry_home = { + name = "The Home", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Earn {C:money}$#1#{} when", + "using a {C:attention}consumable{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Retrigger all cards played", + "{C:attention}#2#{} time(s),", + "each played card gives", + "{X:mult,C:white} X#1# {} Mult when scored", + }, + }, + j_cry_jimball = { + name = "Jimball", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "per {C:attention}consecutive{} hand played", + "while playing your", + "most played {C:attention}poker hand", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Jolly Joker?", + text = { + "Create a {C:dark_edition}Jolly{} Joker", + "when a Joker is {C:attention}sold{}", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{}", + "when a {C:attention}Type Mult{} or", + "{C:attention}Type Chips{} Joker is sold", + }, + }, + j_cry_kooky = { + name = "Kooky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty the Clown", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when", + "each played {C:attention}card{} is scored", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoscope", + text = { + "Add {C:dark_edition}Polychrome{} to", + "a random {C:attention}Joker{} when", + "{C:attention}Boss Blind{} is defeated", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Each played {C:attention}7{} or {C:attention}2{}", + "gives {X:mult,C:white}X#1#{} Mult when scored", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Give future copies of", + "this Joker {X:mult,C:white}X#1#{} Mult", + "at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Retrigger{} all Jokers", + "once for each {C:attention}Jolly{}", + "{C:attention}Joker{} sold this round", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Retrigger(s)){}", + "{C:inactive,s:0.8}There wasn't enough room...{}", + }, + }, + j_cry_lucky_joker = { + name = "Lucky Joker", + text = { + "Earn {C:money}$#1#{} every time a", + "{C:attention}Lucky{} card {C:green}successfully{}", + "triggers", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "All Jokers give", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "when {C:attention}Jolly Joker{} is sold", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:attention}Jolly Joker{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_macabre = { + name = "Macabre Joker", + text = { + "When {C:attention}Blind{} is selected,", + "destroys each {C:attention}Joker{} except", + "{C:legendary}M-Jokers{} and {C:attention}Jolly Jokers{}", + "and create 1 {C:attention}Jolly Joker{}", + "for each destroyed card", + }, + }, + j_cry_magnet = { + name = "Fridge Magnet", + text = { + "Earn {C:money}$#1#{} at end of round", + "This earns {X:money,C:white} X#2# {} if there are", + "{C:attention}#3#{} or fewer {C:attention}Joker{} cards", + }, + }, + j_cry_manic = { + name = "Manic Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Retrigger all Jokers", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_maximized = { + name = "Maximized", + text = { + "All {C:attention}face{} cards", + "are considered {C:attention}Kings{},", + "all {C:attention}numbered{} cards", + "are considered {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Labyrinth", + text = { + "All hands are considered the", + "{C:attention}first hand{} of each round,", + "all discards are considered the", + "{C:attention}first discard{} of each round", + }, + }, + j_cry_Megg = { + name = "Megg", + text = { + "Sell this card to create", + "{C:attention}#2#{} Jolly #3#, increase", + "by {C:attention}#1#{} at end of round", + }, + }, + j_cry_membershipcard = { + name = "Membership Card", + text = { + "{X:mult,C:white}X#1#{} Mult for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Old Membership Card", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteor Shower", + text = { + "{C:dark_edition}Foil{} cards each", + "give {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by", + "{C:money}$#1#{} for each {C:attention}Jolly Joker{}", + "or {C:legendary}M Joker at", + "end of round", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if no {C:attention}discards{} were", + "used this round", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Monkey Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and permanently add {C:attention}ten times{}", + "its sell value to this {C:chips}Chips{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{} when", + "a card with an {C:attention}Edition{} is sold", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Create up to 4 {C:attention}Jolly Jokers{} when obtained", + "Each {C:attention}Jolly Joker{} gives {X:dark_edition,C:white}^#1#{} Mult", + "Increase amount by {X:dark_edition,C:white}^#2#{} and", + "create an {C:legendary}M Joker{} {C:red}once per round", + "when a {C:attention}Jolly Joker{} is {C:attention}sold", + "{C:inactive,s:0.8}(Tredecim excluded)", + }, + }, + j_cry_mstack = { + name = "M Stack", + text = { + "Retrigger all cards played", + "once for every", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jolly Jokers{} sold", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} retriggers){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Mult{} card to create", + "a {C:spectral}Cryptid{} card when scored", + "{C:inactive}(Must have room)", + }, + }, + j_cry_negative = { + name = "Negative Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips if played hand", + "contains a {C:attention}6{} and a {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Night", + text = { + "{X:dark_edition,C:white}^#1#{} Mult on final", + "hand of round", + "{E:2,C:red}self destructs{} on", + "final hand of round", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Retrigger each played {C:attention}7{}", + "{C:attention:}#1#{} additional time(s)", + }, + }, + j_cry_notebook = { + name = "Notebook", + text = { + "{C:green} #1# in #2#{} chance to gain {C:dark_edition}+1{} Joker", + "slot per {C:attention}reroll{} in the shop", + "{C:green}Always triggers{} if there are", + "{C:attention}#5#{} or more {C:attention}Jolly Jokers{}", + "{C:red}Works once per round{}", + "{C:inactive}(Currently {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Number Blocks", + text = { + "Earn {C:money}$#1#{} at end of round", + "Increase payout by {C:money}$#2#{}", + "for each {C:attention}#3#{} held in hand,", + "rank changes every round", + }, + }, + j_cry_nuts = { + name = "The Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_oldblueprint = { + name = "Old Blueprint", + text = { + "Copies ability of", + "{C:attention}Joker{} to the right", + "{C:green}#1# in #2#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + j_cry_oldcandy = { + name = "Nostalgic Candy", + text = { + "Sell this card to", + "permanently gain", + "{C:attention}+#1#{} hand size", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgic Invisible Joker", + text = { + "{C:attention}Duplicate{} a random", + "{C:attention}Joker{} every {C:attention}4", + "Joker cards sold", + "{s:0.8}Nostalgic Invisible Joker Excluded{}", + "{C:inactive}(Currently #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panopticon", + text = { + "All hands are considered the", + "{C:attention}last hand{} of each round", -- +$4 + }, + }, + j_cry_pickle = { + name = "Pickle", + text = { + "When {C:attention}Blind{} is skipped, create", + "{C:attention}#1#{} Tags, reduced by", + "{C:red}#2#{} when {C:attention}Blind{} is selected", + }, + }, + j_cry_pirate_dagger = { + name = "Pirate Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "and gain {C:attention}one-fourth{} of", + "its sell value as {X:chips,C:white} XChips {}", + "{C:inactive}(Currently {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + j_cry_pot_of_jokes = { + name = "Pot of Jokes", + text = { + "{C:attention}#1#{} hand size,", + "increases by", + "{C:blue}#2#{} every round", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "if all cards in played hand are", + "{C:attention}Aces{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, or {C:attention}7s{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "If {C:attention}poker hand{} is a", + "{C:attention}Royal Flush{}, destroy scored", + "{C:attention}Queen{} and create a", + "{C:dark_edition}Negative {}{C:red}Rare{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "The Quintet", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Red Bloon", + text = { + "Earn {C:money}$#1#{} in {C:attention}#2#{} round#3#", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante when", + "{C:money}$#2#{} {C:inactive}($#3#){} spent", + "{s:0.8}Requirements increase", + "{C:attention,s:0.8}exponentially{s:0.8} per use", + "{C:money,s:0.8}Next increase: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "When a {C:attention}Joker{} is sold,", + "add its effects to", + "every other Joker", + "{C:inactive,s:0.8}Does not affect other Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fill all empty Joker slots {C:inactive}(Max 100){}", + "with {C:dark_edition}Holographic{} {C:attention}Jolly Jokers{} if", + "{C:attention}discarded poker hand{} is a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Randomize abilities each {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Create an {C:green}Uncommon{} Joker", + "and 3 {C:attention}Jolly Jokers{} when", + "a {C:spectral}Spectral{} card is used", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sapling", + text = { + "After scoring {C:attention}#2#{} {C:inactive}[#1#]{} Enhanced", + "cards, sell this card to", + "create an {C:cry_epic}Epic{} {C:attention}Joker{}", + "{C:inactive,s:0.8}Will create a {C:red,s:0.8}Rare{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}if {C:cry_epic,s:0.8}Epic{} {C:inactive,s:0.8}Jokers are disabled{}", + }, + }, + j_cry_savvy = { + name = "Savvy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Scaling {C:attention}Jokers{} scale", + "as a degree-{C:attention}#1#{} polynomial", + "raise degree by {C:attention}#2#{}", + "at end of round", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluded)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Tile", + text = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Jolly {C:green}Uncommon{} Joker", + "when hand is played", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Add a {C:attention}random seal{} to each card", + "scored on {C:attention}final hand{} of round", + }, + }, + j_cry_shrewd = { + name = "Shrewd Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Silly Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Tiny", + text = { + "{X:chips,C:white} X#1# {} Chips until end", + "of round if {C:attention}poker hand{}", + "is a {C:attention}#2#{}", + "{C:inactive}(Currently {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker slot", + "{C:attention}+#1#{} Booster Pack slot", + "{C:attention}+#1#{} hand size", + "{C:attention}+#1#{} consumable slot", + "{C:attention}+#1#{} card in shop", + }, + }, + j_cry_spaceglobe = { + name = "Celestial Globe", + text = { + "This Joker gains {X:chips,C:white}X#2#{} Chips", + "if {C:attention}poker hand{} is a {C:attention}#3#{},", + "Hand changes after increase{}", + "{C:inactive}(Currently{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Creates a {C:dark_edition}Negative{} copy", + "of a random {C:attention}Joker{}", + "at the end of the {C:attention}shop", + "{C:inactive,s:0.8}Does not copy other Speculo{}", + }, + }, + j_cry_stardust = { + name = "Stardust", + text = { + "{C:dark_edition}Polychrome{} cards", + "each give {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "This Joker destroys a", + "random {C:planet}Planet{} card", + "and gains {X:dark_edition,C:white} ^#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_subtle = { + name = "Subtle Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Earn {C:money}$#3#{} at", + "end of round", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "At end of round, create", + "a {C:attention}copy{} of a random", + "card {C:attention}held in hand{},", + "destroy all others", + "{C:attention,s:0.8}Kings{s:0.8} of {C:hearts,s:0.8}Hearts{s:0.8} are prioritized", + }, + }, + j_cry_swarm = { + name = "The Swarm", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Catalyst", + text = { + "Balances {C:chips}Chips{} and {C:mult}Mult{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + "Earn {C:money}$#2#{} at end of round", + }, + }, + j_cry_translucent = { + name = "Translucent Joker", + text = { + "Sell this card to create", + "a {C:attention}Banana Perishable{} copy", + "of a random {C:attention}Joker{}", + "{s:0.8,C:inactive}(Copy bypasses perish compat)", + }, + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Triplet Rhythm", + text = { + "{X:mult,C:white} X#1# {} Mult if scoring hand", + "contains {C:attention}exactly{} three {C:attention}3s", + }, + }, + j_cry_unity = { + name = "The Unity", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Poker hands{} gain", + "{X:red,C:white} X#1# {} Mult and {X:blue,C:white} X#1# {} Chips", + "when leveled up", + }, + }, + j_cry_unjust_dagger = { + name = "Unjust Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and gain {C:attention}one-fifth{} of", + "its sell value as {X:mult,C:white} XMult {}", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "This Joker gains {C:money}$#1#{} of {C:attention}sell value{}", + "if {C:attention}poker hand{} contains a {C:attention}#2#{}", + "Sell this card to create a", + "{C:dark_edition}Polychrome{} {C:attention}Jolly Joker{} for", + "every {C:money}$4{} of {C:attention}sell value{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Wacky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "All Jokers give", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "All Jokers give", + "{C:money}$#1#{} when triggered", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "This Joker gains", + "{C:mult}+#2#{} Mult when each played", + "{C:attention}Ace{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "is scored", + "{C:inactive}(Currently {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Retrigger each played {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} additional time(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Wheel of Hope", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when failing", + "a {C:attention}Wheel of Fortune{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if {C:attention}played hand{} contains a", + "{C:attention}2{} and {C:attention}7{} of different suits", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutron Star", + text = { + "Upgrade a random", + "poker hand by", + "{C:attention}1{} level for each", + "{C:attention}Neutron Star{} used", + "in this run", + "{C:inactive}(Currently{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# in #2#{} chance to", + "upgrade every", + "{C:legendary,E:1}poker hand{}", + "by {C:attention}1{} level", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "CCD Sleeve", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Conveyor Sleeve", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Critical Sleeve", + text = { + "After each hand played,", + "{C:green}1 in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}1 in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Encoded Sleeve", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balanced Sleeve", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}+2 Shop Slots", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unlimited Sleeve", + text = { + "You can select {C:attention}any", + "number of cards", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Misprinted Sleeve", + text = { + "Values of cards", + "are {C:attention}randomized", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Redeemed Sleeve", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Wormhole Sleeve", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + }, + Spectral = { + c_cry_analog = { + name = "Analog", + text = { + "Create {C:attention}#1#{} copies of a", + "random {C:attention}Joker{}, destroy", + "all other Jokers, {C:attention}+#2#{} Ante", + }, + }, + c_cry_gateway = { + name = "Gateway", + text = { + "Create a random", + "{C:cry_exotic,E:1}Exotic{C:attention} Joker{}, destroy", + "all other Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Lock", + text = { + "Remove {C:red}all{} stickers", + "from {C:red}all{} Jokers,", + "then apply {C:purple,E:1}Eternal{}", + "to a random {C:attention}Joker{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Create a card", + "of {C:cry_code}your choice", + "{C:inactive,s:0.8}(Exotic Jokers #1#excluded)", + }, + }, + c_cry_replica = { + name = "Replica", + text = { + "Convert all cards", + "held in hand", + "to a {C:attention}random{}", + "card held in hand", + }, + }, + c_cry_source = { + name = "Source", + text = { + "Add a {C:cry_code}Green Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_summoning = { + name = "Summoning", + text = { + "Create a random", + "{C:cry_epic}Epic{} {C:joker}Joker{}, destroy", + "one random {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Trade", + text = { + "{C:attention}Lose{} a random Voucher,", + "gain {C:attention}2{} random Vouchers", + }, + }, + c_cry_typhoon = { + name = "Typhoon", + text = { + "Add an {C:cry_azure}Azure Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_vacuum = { + name = "Vacuum", + text = { + "Removes {C:red}all {C:green}modifications{}", + "from {C:red}all{} cards held in hand,", + "Earn {C:money}$#1#{} per {C:green}modification{} removed", + "{C:inactive,s:0.7}(ex. Enhancements, Seals, Editions)", + }, + }, + c_cry_white_hole = { + name = "White Hole", + text = { + "{C:attention}Remove{} all hand levels,", + "upgrade {C:legendary,E:1}most played{} poker hand", + "by {C:attention}3{} for each removed level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pink Stake", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brown Stake", + colour = "Brown", + text = { + "All {C:attention}stickers{} are compatible", + "with each other", + }, + }, + stake_cry_yellow = { + name = "Yellow Stake", + colour = "Yellow", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Jade Stake", + colour = "Jade", + text = { + "Cards can be drawn {C:attention}face down{}", + }, + }, + stake_cry_cyan = { + name = "Cyan Stake", + colour = "Cyan", + text = { + "{C:green}Uncommon{} and {C:red}Rare{} Jokers are", + "less likely to appear", + }, + }, + stake_cry_gray = { + name = "Gray Stake", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Crimson Stake", + colour = "Crimson", + text = { + "Vouchers restock on {C:attention}even{} Antes", + }, + }, + stake_cry_diamond = { + name = "Diamond Stake", + colour = "Diamond", + text = { + "Must beat Ante {C:attention}10{} to win", + }, + }, + stake_cry_amber = { + name = "Amber Stake", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Pack slot", + }, + }, + stake_cry_bronze = { + name = "Bronze Stake", + colour = "Bronze", + text = { + "Vouchers are {C:attention}50%{} more expensive", + }, + }, + stake_cry_quartz = { + name = "Quartz Stake", + colour = "Quartz", + text = { + "Jokers can be {C:attention}Pinned{}", + "{s:0.8,C:inactive}(Stays pinned to the leftmost position){}", + }, + }, + stake_cry_ruby = { + name = "Ruby Stake", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds can become", + "{C:attention}Boss{} Blinds", + }, + }, + stake_cry_glass = { + name = "Glass Stake", + colour = "Glass", + text = { + "Cards can {C:attention}shatter{} when scored", + }, + }, + stake_cry_sapphire = { + name = "Sapphire Stake", + colour = "Sapphire", + text = { + "Lose {C:attention}25%{} of current money", + "at end of Ante", + "{s:0.8,C:inactive}(Up to $10){}", + }, + }, + stake_cry_emerald = { + name = "Emerald Stake", + colour = "Emerald", + text = { + "Cards, packs, and vouchers", + "can be {C:attention}face down{}", + "{s:0.8,C:inactive}(Unable to be viewed until purchased){}", + }, + }, + stake_cry_platinum = { + name = "Platinum Stake", + colour = "Platinum", + text = { + "Small Blinds are {C:attention}removed{}", + }, + }, + stake_cry_twilight = { + name = "Twilight Stake", + colour = "Twilight", + text = { + "Cards can be {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 in 10 chance of being destroyed each round){}", + }, + }, + stake_cry_verdant = { + name = "Verdant Stake", + colour = "Verdant", + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Ember Stake", + colour = "Ember", + text = { + "All items give no money when sold", + }, + }, + stake_cry_dawn = { + name = "Dawn Stake", + colour = "Dawn", + text = { + "Tarots and Spectrals target {C:attention}1", + "fewer card", + "{s:0.8,C:inactive}(Minimum of 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizon Stake", + colour = "Horizon", + text = { + "When blind selected, add a", + "{C:attention}random card{} to deck", + }, + }, + stake_cry_blossom = { + name = "Blossom Stake", + colour = "Blossom", + text = { + "{C:attention}Final{} Boss Blinds can appear", + "in {C:attention}any{} Ante", + }, + }, + stake_cry_azure = { + name = "Azure Stake", + colour = "Azure", + text = { + "Values on Jokers are reduced", + "by {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Ascendant Stake", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop slot", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Banana Tag", + text = { + "Creates {C:attention}#1#", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_bettertop_up = { + name = "Better Top-up Tag", + text = { + "Creates up to {C:attention}#1#", + "{C:green}Uncommon{} Jokers", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_better_voucher = { + name = "Golden Voucher Tag", + text = { + "Adds one Tier {C:attention}#1#{} Voucher", + "to the next shop", + }, + }, + tag_cry_blur = { + name = "Blurred Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Blurred{}", + }, + }, + tag_cry_booster = { + name = "Booster Tag", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:attention}double{} cards and", + "{C:attention}double{} choices", + }, + }, + tag_cry_bundle = { + name = "Bundle Tag", + text = { + "Create a {C:attention}Standard Tag{}, {C:tarot}Charm Tag{},", + "{C:attention}Buffoon Tag{}, and {C:planet}Meteor Tag", + }, + }, + tag_cry_cat = { + name = "Cat Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Console Tag", + text = { + "Gives a free", + "{C:cry_code}Program Pack", + }, + }, + tag_cry_double_m = { + name = "Double M Tag", + text = { + "Shop has a", + "{C:dark_edition}Jolly {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Empowered Tag", + text = { + "Gives a free {C:spectral}Spectral Pack", + "with {C:legendary,E:1}The Soul{} and {C:cry_exotic,E:1}Gateway{}", + }, + }, + tag_cry_epic = { + name = "Epic Tag", + text = { + "Shop has a half-price", + "{C:cry_epic}Epic Joker", + }, + }, + tag_cry_gambler = { + name = "Gambler's Tag", + text = { + "{C:green}#1# in #2#{} chance to create", + "an {C:cry_exotic,E:1}Empowered Tag", + }, + }, + tag_cry_glass = { + name = "Fragile Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Fragile{}", + }, + }, + tag_cry_glitched = { + name = "Glitched Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Glitched{}", + }, + }, + tag_cry_gold = { + name = "Golden Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Golden{}", + }, + }, + tag_cry_gourmand = { + name = "Gourmand Tag", + text = { + "Shop has a free", + "{C:attention}Food Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gives a free", + "{C:cry_ascendant}Meme Pack", + }, + }, + tag_cry_m = { + name = "Jolly Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Jolly{}", + }, + }, + tag_cry_memory = { + name = "Memory Tag", + text = { + "Create {C:attention}#1#{} copies of", + "the last {C:attention}Tag{} used", + "during this run", + "{s:0.8,C:inactive}Copying Tags excluded", + "{s:0.8,C:inactive}Currently: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Mosaic{}", + }, + }, + tag_cry_oversat = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Oversaturated{}", + }, + }, + tag_cry_quadruple = { + name = "Quadruple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_quintuple = { + name = "Quintuple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_rework = { + name = "Rework Tag", + text = { + "Shop has a(n)", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schematic Tag", + text = { + "Shop has a", + "{C:attention}Brainstorm", + }, + }, + tag_cry_scope = { + name = "Scope Tag", + text = { + "{C:attention}+#1# {C:blue}hands{} and", + "{C:red}discards{} next round", + }, + }, + tag_cry_triple = { + name = "Triple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "The Automaton", + text = { + "Creates up to {C:attention}#1#", + "random {C:cry_code}Code{} card", + "{C:inactive}(Must have room)", + }, + }, + c_cry_eclipse = { + name = "The Eclipse", + text = { + "Enhances {C:attention}#1#{} selected card", + "into an {C:attention}Echo Card", + }, + }, + c_cry_meld = { + name = "Meld", + text = { + "Select a {C:attention}Joker{} or", + "{C:attention}playing card{} to", + "become {C:dark_edition}Double-Sided", + }, + }, + c_cry_theblessing = { + name = "The Blessing", + text = { + "Creates {C:attention}1{}", + "random {C:attention}consumable{}", + "{C:inactive}(Must have room){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglyph", + text = { + "Set Ante to {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Blank Canvas", + text = { + "{C:attention}+#1#{} hand size", + }, + }, + v_cry_clone_machine = { + name = "Clone Machine", + text = { + "Double Tags become", + "{C:attention}Quintuple Tags{} and", + "are {C:attention}4X{} as common", + }, + }, + v_cry_command_prompt = { + name = "Command Prompt", + text = { + "{C:cry_code}Code{} cards", + "can appear", + "in the {C:attention}shop{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Double Tags become", + "{C:attention}Triple Tags{} and are", + "{C:attention}2X{} as common", + }, + }, + v_cry_curate = { + name = "Curate", + text = { + "All cards", + "appear with", + "an {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Dexterity", + text = { + "Permanently", + "gain {C:blue}+#1#{} hand(s)", + "each round", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "After every round,", + "{X:dark_edition,C:white} X1.5 {} to all values", + "on the back of", + "{C:dark_edition}Double-Sided{} cards" + }, + }, + v_cry_double_slit = { + name = "Double Slit", + text = { + "{C:attention}Meld{} can appear", + "in the shop and", + "Arcana Packs", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "{C:dark_edition}Double-Sided{} cards appear", + "{C:attention}4X{} more frequently", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker slot(s)", + }, + }, + v_cry_massproduct = { + name = "Mass Production", + text = { + "All cards and packs", + "in shop cost {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Raise the cap on", + "interest earned in", + "each round to {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multistock", + text = { + "{C:attention}+#1#{} card slot(s) and", + "{C:attention}+#1#{} booster pack slot(s)", + "available in shop", + }, + }, + v_cry_pacclimator = { + name = "Planet Acclimator", + text = { + "{C:planet}Planet{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:planet}Planet{}", + "cards are {C:green}free{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Retrigger{} all M Jokers", + "once for every Pair", + "{C:attention}contained{} in played hand", + }, + }, + v_cry_pairing = { + name = "Pairing", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand is a {C:attention}Pair", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computing", + text = { + "{C:cry_code}Code{} cards can spawn", + "with {C:dark_edition}Negative{} edition", + }, + }, + v_cry_repair_man = { + name = "Repair Man", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand contains a {C:attention}Pair", + }, + }, + v_cry_rerollexchange = { + name = "Reroll Exchange", + text = { + "All rerolls", + "cost {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satellite Uplink", + text = { + "{C:cry_code}Code{} cards may", + "appear in any of", + "the {C:attention}Celestial Packs{}", + }, + }, + v_cry_scope = { + name = "Galactic Scope", + text = { + "Create the {C:planet}Planet", + "card for played", + "{C:attention}poker hand{}", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Tarot Acclimator", + text = { + "{C:tarot}Tarot{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:tarot}Tarot{}", + "cards are {C:green}free{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Printer", + text = { + "Double Tags become", + "{C:attention}Quadruple Tags{} and", + "are {C:attention}3X{} as common", + }, + }, + v_cry_threers = { + name = "The 3 Rs", + text = { + "Permanently", + "gain {C:red}+#1#{} discard(s)", + "each round", + }, + }, + v_cry_stickyhand = { + name = "Sticky Hand", + text = { + "{C:attention}+#1#{} card", + "selection limit", + }, + }, + v_cry_grapplinghook = { + name = "Grappling Hook", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperspace Tether", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "destroyed each round", + }, + }, + cry_rigged = { + name = "Rigged", + text = { + "All {C:cry_code}listed{} probabilities", + "are {C:cry_code}guaranteed", + }, + }, + cry_hooked = { + name = "Hooked", + text = { + "When this Joker is {C:cry_code}triggered{},", + "trigger {C:cry_code}#1#", + }, + }, + food_jokers = { + name = "Food Jokers", + text = { + "{s:0.8}Gros Michel, Egg, Ice Cream, Cavendish,", + "{s:0.8}Turtle Bean, Diet Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Pickle, Chili Pepper, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eternal", + text = { + "All cards in pack", + "are {C:attention}Eternal{}", + }, + }, + cry_perishable_booster = { + name = "Perishable", + text = { + "All cards in pack", + "are {C:attention}Perishable{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "All cards in pack", + "are {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Pinned", + text = { + "All cards in pack", + "are {C:attention}Pinned{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "All cards in pack", + "are {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eternal", + text = { + "Can't be traded", + }, + }, + cry_perishable_voucher = { + name = "Perishable", + text = { + "Debuffed after", + "{C:attention}#1#{} rounds", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)", + }, + }, + cry_rental_voucher = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at", + "end of round", + }, + }, + cry_pinned_voucher = { + name = "Pinned", + text = { + "Remains in shop", + "until redeemed", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "unredeemed each round", + }, + }, + cry_perishable_consumeable = { + name = "Perishable", + text = { + "Debuffed at", + "end of round", + }, + }, + cry_rental_consumeable = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at end of", + "round, and on use", + }, + }, + cry_pinned_consumeable = { + name = "Pinned", + text = { + "Can't use other", + "non-{C:attention}Pinned{} consumables", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance to do", + "nothing on use", + }, + }, + p_cry_code_normal_1 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_normal_2 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_two = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_three = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + undiscovered_code = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + cry_green_seal = { + name = "Green Seal", + text = { + "Creates a {C:cry_code}Code{} card", + "when played and unscoring", + "{C:inactive}(Must have room)", + }, + }, + cry_azure_seal = { + name = "Azure Seal", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planets{} for played", + "{C:attention}poker hand{}, then", + "{C:red}destroy{} this card", + }, + }, + }, + }, + misc = { + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Legally Blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Break Infinity", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Home Realtor", + ach_cry_jokes_on_you = "Joke's on You, Pal!", + ach_cry_niw_uoy = "!niW uoY", + ach_cry_now_the_fun_begins = "Now the Fun Begins", + ach_cry_patience_virtue = "Patience is a Virtue", + ach_cry_perfectly_balanced = "Perfectly Balanced", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Traffic Jam", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "We Told You Not To", + ach_cry_what_have_you_done = "WHAT HAVE YOU DONE?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtain Blurred Blurred Joker", + ach_cry_bullet_hell = "Have 15 AP Jokers", + ach_cry_break_infinity = "Score 1.79e308 Chips in a single hand", + ach_cry_cryptid_the_cryptid = "Use Cryptid on Cryptid", + ach_cry_exodia = "Have 5 Exotic Jokers", + ach_cry_freak_house = "Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice", + ach_cry_googol_play_pass = "Rig a Googol Play Card", + ach_cry_haxxor = "Use a cheat code", + ach_cry_home_realtor = "Activate Happy House before Ante 8 (without DoE/Antimatter)", + ach_cry_jokes_on_you = "Trigger The Joke's effect on Ante 1 and win the run", + ach_cry_niw_uoy = "Reach Ante -8", + ach_cry_now_the_fun_begins = "Obtain Canvas", + ach_cry_patience_virtue = "Wait out Lavender Loop for 2 minutes before playing first hand and beat the blind", + ach_cry_perfectly_balanced = "Beat Very Fair Deck on Ascendant Stake", + ach_cry_pull_request = "Have ://COMMIT spawn the same Joker that it destroyed", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Win in 1 round", + ach_cry_used_crash = "Use ://CRASH", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Music", + cry_set_enable_features = "Select features to enable (applies on game restart):", + cry_feat_achievements = "Achievements", + ["cry_feat_antimatter deck"] = "Antimatter Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Code Cards", + ["cry_feat_misc. decks"] = "Misc. Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechanics", + ["cry_feat_enhanced decks"] = "Enhanced Decks", + ["cry_feat_epic jokers"] = "Epic Jokers", + ["cry_feat_exotic jokers"] = "Exotic Jokers", + ["cry_feat_m jokers"] = "M Jokers", + cry_feat_menu = "Custom Main Menu", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Misc. Jokers", + cry_feat_planets = "Planets", + cry_feat_jokerdisplay = "JokerDisplay (Does Nothing)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Sleeves", + cry_feat_spectrals = "Spectrals", + ["cry_feat_more stakes"] = "Stakes", + cry_feat_vouchers = "Vouchers", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Pack", + k_cry_meme_pack = "Meme Pack", + + cry_critical_hit_ex = "Critical Hit!", + cry_critical_miss_ex = "Critical Miss!", + + cry_debuff_oldhouse = "No Full Houses", + cry_debuff_oldarm = "Must play 4 or fewer cards", + cry_debuff_oldpillar = "No Straights", + cry_debuff_oldflint = "No Flushes", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applies abilities of all defeated bosses", + + k_code = "Code", + b_code_cards = "Code Cards", + b_pull = "PULL", + cry_hooked_ex = "Hooked!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTER RANK", + cry_code_enh = "ENTER ENHANCEMENT", + cry_code_hand = "ENTER POKER HAND", + cry_code_enter_card = "ENTER A CARD", + cry_code_apply = "APPLY", + cry_code_apply_previous = "APPLY PREVIOUS", + cry_code_exploit = "EXPLOIT", + cry_code_exploit_previous = "EXPLOIT PREVIOUS", + cry_code_create = "CREATE", + cry_code_create_previous = "CREATE PREVIOUS", + cry_code_execute = "EXECUTE", + cry_code_cancel = "CANCEL", + + b_flip = "FLIP", + b_merge = "MERGE", + + cry_again_q = "Again?", + cry_curse = "Curse", + cry_curse_ex = "Curse!", + cry_sobbing = "Help me...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Round", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "No triggers left!", + cry_unredeemed = "Unredeemed...", + cry_active = "Active", + cry_inactive = "Inactive", + }, + labels = { + food_jokers = "Food Jokers", + banana = "Banana", + code = "Code", + cry_rigged = "Rigged", + cry_hooked = "Hooked", + + cry_green_seal = "Green Seal", + cry_azure_seal = "Azure Seal", + + cry_astral = "Astral", + cry_blur = "Blurred", + cry_double_sided = "Double-Sided", + cry_glass = "Fragile", + cry_glitched = "Glitched", + cry_gold = "Golden", + cry_m = "Jolly", + cry_mosaic = "Mosaic", + cry_noisy = "Noisy", + cry_oversat = "Oversaturated", + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + }, + v_text = { + ch_c_cry_all_perishable = {"All Jokers are {C:eternal}Perishable{}"}, + ch_c_cry_all_rental = {"All Jokers are {C:eternal}Rental{}"}, + ch_c_cry_all_pinned = {"All Jokers are {C:eternal}Pinned{}"}, + ch_c_cry_all_banana = {"All Jokers are {C:eternal}Banana{}"}, + ch_c_all_rnj = {"All Jokers are {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"All purchasable items have all stickers"}, + ch_c_cry_rush_hour = {"All Boss Blinds are {C:attention}The Clock{} or {C:attention}Lavender Loop"}, + ch_c_cry_rush_hour_ii = {"All Blinds are {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}The Clock{} and {C:attention}Lavender Loop{} scale {C:attention}twice{} as fast"}, + ch_c_cry_no_tags = {"Skipping is {C:attention}disabled{}"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/pl.lua b/Cryptid/localization/pl.lua new file mode 100644 index 0000000..461b80f --- /dev/null +++ b/Cryptid/localization/pl.lua @@ -0,0 +1,3946 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antymateryjna Talia", + text = { + "Aplikuje {C:legendary,E:1}zalety{}", + "{C:attention}każdej{} talii", + }, + }, + b_cry_beta = { + name = "Nostalgiczna Talia", + text = { + "Miejsce na {C:attention}jokery{}", + "i i {C:attention}przedmioty zużywalne{} są połączone,", + "a {C:attention}Nostalgiczne{} przeszkadzajki", + "zastępują ich zaktualizowane wersje" + }, + }, + b_cry_blank = { + name = "Pusta Talia", + text = { + "{C:inactive,E:1}Nie robi nic?", + }, + }, + b_cry_bountiful = { + name = "Obfita Talia", + text = { + "Po {C:blue}zagraniu{} lub {C:red}zrzutce{},", + "zawsze dobierasz {C:attention}5{} kart" + }, + }, + b_cry_CCD = { + name = "Talia CCD", + text = { + "Każda karta jest jednocześnie", + "{C:attention}losowym{} przedmiotem zużywalnym", + }, + }, + b_cry_conveyor = { + name = "Taśmociągowa Talia", + text = { + "Jokery {C:attention}nie mogą{} zmieniać pozycji", + "Na początku rundy,", + "{C:attention}duplikuje{} jokera najbardziej wysuniętego na prawo", + "i {C:attention}niszczy{} najbardziej wysuniętego na lewo", + }, + }, + b_cry_critical = { + name = "Krytyczna Talia", + text = { + "Po każdej zagranej ręce,", + "{C:green}#1# na 4{} szans na mnożnik {X:dark_edition,C:white} ^2 {}", + "{C:green}#1# na 8{} szans na mnożnik {X:dark_edition,C:white} ^0.5 {}", + }, + }, + b_cry_encoded = { + name = "Zakodowana Talia", + text = { + "Rozpoczynasz podejście z", + "{C:cry_code,T:j_cry_CodeJoker}Kodowym Jokerem{} i {C:cry_code,T:j_cry_copypaste}Kopiuj/Wklej{}", + "Tylko {C:cry_code}Karty kodowe{} pojawiają się w sklepie", + }, + }, + b_cry_equilibrium = { + name = "Talia Równowagi", + text = { + "Wszystkie karty mają", + "{C:attention}równą{} szansę", + "pojawienia się w sklepie,", + "rozpoczynasz podejście z kuponem", + "{C:attention,T:v_overstock_plus}Nadmiar Zapasów+", + }, + }, + b_cry_glowing = { + name = "Świecąca Talia", + text = { + "Po pokonaniu przeszkadzajki bossa,", + "wszystkie wartości jokerów", + "są mnożone przez {X:dark_edition,C:white} X1.25 {}", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Nieskończona Talia", + text = { + "Możesz wybrać {C:attention}dowolną", + "liczbę kart", + "{C:attention}+1{} do rozmiaru ręki", + }, + }, + b_cry_misprint = { + name = "Błędna Talia", + text = { + "Wartości kart", + "i układów pokerowych", + "są {C:attention}losowe", + }, + }, + b_cry_redeemed = { + name = "Odkupiona Talia", + text = { + "Przy zakupie {C:attention}kuponu{},", + "otrzymujesz jego {C:attention}dodatkowe poziomy", + }, + }, + b_cry_spooky = { + name = "Upiorna Talia", + text = { + "Rozpoczynasz podejście z {C:eternal}Wieczną{} {C:attention,T:j_cry_chocolate_dice}Czekoladową Kostką", + "Po każdym {C:attention}wejściu{}, tworzy", + "{C:cry_candy}Cukierka{} lub {X:cry_cursed,C:white}Przeklętego{} jokera", + } + }, + b_cry_very_fair = { + name = "Bardzo Uczciwa Talia", + text = { + "{C:blue}-2{} od ręki, {C:red}-2{} od zrzutek", + "w każdej rundzie", + "{C:attention}Kupony{} nie pojawiają", + "się w sklepie", + }, + }, + b_cry_wormhole = { + name = "Tuneloczasowa Talia", + text = { + "Rozpoczynasz podejście z {C:cry_exotic}egzotycznym{C:attention} jokerem", + "{C:attention}X20{} prawdopodobieństwa", + "na {C:dark_edition}Negatywy{} jokerów", + "{C:attention}-2{} sloty na jokera", + }, + }, + b_cry_legendary = { + name = "Legendarna Talia", + text = { + "Rozpoczynasz podejście z {C:legendary}legendarnym{C:legendary} jokerem", + "{C:green}1 in 5{} szans na dodanie kolejnego", + "po pokonaniu przeszkadzajki bossa", + "{C:inactive}(wymaga miejsca){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "Pudełko", + text = { + "Wszystkie pospolite jokery", + "zostają osłabione", + }, + }, + bl_cry_clock = { + name = "Zegar", + text = { + "+0.1X wymagań przeszkadzajki", + "co 3 sekundy tego wejścia", + }, + }, + bl_cry_hammer = { + name = "Młot", + text = { + "AWszystkie karty z nieparzystą", + "rangą zostają osłabione", + }, + }, + bl_cry_joke = { + name = "Żart", + text = { + "Jeśli końcowy wynik przekracza >2X wymagań przeszkadzajki", + "ustawia wejście na #1#", + }, + }, + bl_cry_magic = { + name = "Magia", + text = { + "Wszystkie karty z parzystą", + "rangą zostają osłabione", + }, + }, + bl_cry_lavender_loop = { + name = "Lawendowa Pętla", + text = { + "1,25x wymagań przeszkadzajki", + "co 1,5 sekundy tego wejścia", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidianowa Kula", + text = { + "Aplikuje zdolności", + "wszystkich pokonanych bossów", + }, + }, + bl_cry_oldarm = { + name = "Nostalgiczne Ramię", + text = { + "Należy zagrać 4", + "albo mniej kart", + }, + }, + bl_cry_oldfish = { + name = "Nostalgiczna Ryba", + text = { + "Wszystkie ręce zaczynają", + "z mnożnikiem 1", + }, + }, + bl_cry_oldflint = { + name = "Nostalgiczny Flint", + text = { + "Kolor nie może być zagrany", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgiczny Dom", + text = { + "Full House nie może być zagrany", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgiczne Kajdany", + text = { + "Mnożnik dzieli się przez zrzutki", + }, + }, + bl_cry_oldmark = { + name = "Nostalgiczny Znak", + text = { + "Układy z Parą", + "nie mogą być zagrane", + }, + }, + bl_cry_oldox = { + name = "Nostalgiczny Wół", + text = { + "Wszystkie ręce", + "zaczynają bez żetonów", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgiczny Filar", + text = { + "Strit nie może być zagrany", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgiczny Wąż", + text = { + "Mnożnik dzieli się", + "przez poziom układu pokerowego", + }, + }, + bl_cry_pin = { + name = "Szpilka", + text = { + "Epickie i egzotyczne jokery", + "zostają osłabione", + }, + }, + bl_cry_pinkbow = { + name = "Różowa Kokardka", + text = { + "Losuje rangi kart", + "trzymanych w ręce podczas zagrania", + }, + }, + bl_cry_sapphire_stamp = { + name = "Szafirowy Znaczek", + text = { + "Wybiera dodatkową kartę", + "usuwa losową przed punktacją", + }, + }, + bl_cry_shackle = { + name = "Szekla", + text = { + "Wszystkie negatywy jokerów", + "zostają osłabione", + }, + }, + bl_cry_striker = { + name = "Napastnik", + text = { + "Wszystkie rzadkie jokery", + "zostają osłabione", + }, + }, + bl_cry_tax = { + name = "Podatek", + text = { + "Wynik ręki ograniczony", + "do 0,4-krotności przeszkadzajki", + }, + }, + bl_cry_tornado = { + name = "Turkusowe Tornado", + text = { + "#1# na #2# szans, że", + "zagrana ręka nie zdobędzie punktów", + }, + }, + bl_cry_trick = { + name = "Sztuczka", + text = { + "Po każdej ręce, wszystkie karty w dłoni", + "zostają obrócone", + }, + }, + bl_cry_vermillion_virus = { + name = "Cynobrowy Wirus", + text = { + "Podczas każdej ręki, jeden losowy joker", + "zostaje permanentnie zastąpiony innym jokerem", + }, + }, + bl_cry_windmill = { + name = "Wiatrak", + text = { + "Wszystkie niepospolite jokery", + "zostają osłabione", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Przekształca {C:cry_code}#1#{} wybraną kartę", + "na {C:cry_code}dowolne{} ulepszenie", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Niszczy {C:cry_code}wybranego{} jokera,", + "tworzy {C:cry_code}nowego{} jokera", + "o tej samej {C:cry_code}rzadkości", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Nie.", + }, + }, + c_cry_ctrl_v = { + name = "://CTRL+V", + text = { + "Tworzy {C:cry_code}kopię{} wybranej karty", + "lub przedmiotu zużywalnego" + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Permanentnie{} usuwa", + "{C:cry_code}wybrany{} przedmiot ze sklepu", + "{C:inactive,s:0.8}Przedmiot nie może ponownie pojawić się w trakcie podejścia", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "Obniża o {C:cry_code}połowę{} wszystkie ceny", + "w obecnym sklepie", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "Następna ręka jest liczona jako", + "{C:cry_code}wybrany{} układ pokerowy,", + "resetuje się na koniec rundy", + "{C:inactive,s:0.8}Sekretne ręce muszą być", + "{C:inactive,s:0.8}odkryte aby można było z nich skorzystać", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Wybierz dwa jokery, które zostaną {C:cry_code}Zahaczone", + "{C:inactive,s:0.8}Działają poprawnie jeśli jokery aktywują się w tym samym kontekście,", + "{C:inactive,s:0.8}jak na przykład Joker i Duet (oba po zdobyciu punktów)", + }, + }, + c_cry_inst = { + name = "://INSTANTIATE", + text = { + "Dobiera kartę z {C:cry_code}rangą{}", + "wybranej karty i jedną z jej {C:cry_code}kolorem{}", + "{C:inactive}(jeśli to możliwe){}", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "{C:cry_code}Wszystkie karty w ręcę", "zostają {C:dark_edition}zglitchowane{}"}, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Scala wybrany {C:cry_code}przedmiot zużywalny", + "z wybraną {C:cry_code}kartą rozgrywającą", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Podwaja{} wartości", + "wybranego {C:cry_code}jokera{}", + "do końca rundy", + }, + }, + c_cry_patch = { + name = "://PATCH", + text = { + "Usuwa wszystkie osłabienia i", + "naklejki z obecnych kart", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Następna pokonana przeszkadzajka ", + "daje {C:cry_code}potrójne{} odsetki", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Następna {C:cry_code}paczka wzmacniająca{} ma", + "{C:cry_code}dodatkową{} kartę i", + "{C:cry_code}dodatkowy{} wybór", + "{C:inactive}(Obecnie {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Uzupełnia {C:blue}ręce{} i {C:red}zrzutki{},", + "{C:cry_code}wszystkie{} karty wracają do talii,", + "runda rozpoczyna się {C:cry_code}od nowa{}", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Cofa {C:cry_code}stan gry{} do", + "początku {C:cry_code}tego wejścia{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Niszczy {C:cry_code}wybranego{} jokera,", + "tworzy {C:cry_code}znacznik przeróbki{} z", + "{C:cry_code}ulepszoną{} edycją", + "{C:inactive,s:0.8}Ulepsza w kolejności z Kolekcji", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Odwiedź {C:cry_code}sklep", + "w trakcie {C:cry_code}przeszkadzajki", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Wybrany joker", + "lub karta rozgrywająca", + "zostaje {C:cry_code}zawsze aktywowana", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Kończy obecną przeszkadzajkę {C:cry_code}w ciemno{}", "{C:cry_code}bez{} wypłaty" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Tworzy {C:cry_code}zglitchowanego", + "kulinarnego jokera", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Przekształca {C:cry_code}#1#{} wybrane karty", + "na {C:cry_code}wybraną{} rangę", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astralna", + text = { + "Mnoż. {X:dark_edition,C:white}^#1#{}", + }, + }, + e_cry_blur = { + name = "Niewyraźna", + text = { + "{C:attention}Aktywuje ponownie{} kartę", + "z tą {C:dark_edition}edycją{}", + "{C:green}#1# na #2#{} szans", + "na aktyowanie ponownie", + "po raz kolejny", + }, + }, + e_cry_double_sided = { + name = "Dwustronna", + text = { + "Tę kartę można", + "{C:attention}odwrócić{}, aby", + "odkryć inną kartę", + "{C:inactive}(Pusta strona może być połączona", + "{C:inactive}z inną kartą)", + }, + }, + e_cry_glass = { + name = "Krucha", + label = "Fragile", + text = { + "Mnoż. {C:white,X:mult} X#3# {}", + "{C:green}#1# na #2#{} szans", + "że ta karta {C:red}nie ulegnie zniszczeniu", + "kiedy zostanie aktywowana", + }, + }, + e_cry_glitched = { + name = "Zglitchowana", + text = { + "Wszystkie wartości tej karty", + "są {C:dark_edition}losowe{}", + "pomiędzy {C:attention}X0.1{} a {C:attention}X10{}", + "{C:inactive}(Jeśli to możliwe){}", + }, + }, + e_cry_gold = { + name = "Złota", + label = "Golden", + text = { + "Zarabiasz {C:money}$#1#{} kiedy zostaje użyta", + "lub uruchomiona", + }, + }, + e_cry_m = { + name = "Wesoła", + text = { + "Mnoż. {C:mult}+#1#{}", + "Ta karta jest", + "raczej {C:attention}wesoła{}", + }, + }, + e_cry_mosaic = { + name = "Mozaiczna", + text = { + "{X:chips,C:white} X#1# {} żet.", + }, + }, + e_cry_noisy = { + name = "Hałaśliwa", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Przesycona", + text = { + "Wszystkie wartości", + "na tej karcie", + "są {C:attention}podwojone{}", + "{C:inactive}(Jeśli to możliwe)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Karta Echa", + text = { + "{C:green}#2# na #3#{} szans", + "na {C:attention}ponowną aktywacje{} dodatkowe #1#", + "razy kiedy zdobywa punkty", + }, + }, + }, + Joker = { + j_cry_adroit = { + name = "Zręczny Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_altgoogol = { + name = "Nostalgiczna karta Googol Play", + text = { + "Sprzedaj tę kartę, aby stworzyć", + "{C:attention}2{} kopie {C:attention}jokera najbardziej wysuniętego na lewo{}", + "{C:inactive,s:0.8}Nie kopiuje Nostalgicznej Karty Googol Play{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Niczym anteny do nieba", + text = { + "Ten joker zdobywa", + "{X:chips,C:white} X#1# {} żet., kiedy każda", + "zagrana {C:attention}7{} lub {C:attention}4{} zdobywa punkty", + "{C:inactive}(obecny mnoż. żetonów: {X:chips,C:white}X#2# {C:inactive})", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "Mnoż. {X:mult,C:white} X#1# {} przeciwko {C:attention}Przeszkadzajkom Bossa{}" }, + }, + j_cry_astral_bottle = { + name = "Astral w butelce", + text = { + "Kiedy ten joker jest sprzedany, {C:attention}losowy joker{}", + "staje się {C:dark_edition}astralny{} i", + "{C:attention}nietrwały{}", + } + }, + j_cry_big_cube = { + name = "Duża kostka", + text = { + "{X:chips,C:white} X#1# {} żet.", + }, + }, + j_cry_biggestm = { + name = "ogroMny", + text = { + "Mnoż. {X:mult,C:white} X#1# {} do końca rundy", + "jeśli {C:attention}zagrana ręka{}", + "to {C:attention}#2#{}", + "{C:inactive}(obecnie: {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}nie jestem gruby, jestem grubokościsty!", + }, + }, + j_cry_blacklist = { + name = "Czarna lista", + text = { + "Jeśli karta o randze {C:attention}#1#{} jest w dłoni lub zdobywa punkty,", + "ustawia {C:chips}żetony{} i {C:mult}mnożnik{} na 0", + "{C:red,E:2}Ulega samozniszczeniu{} jeśli kart o randze {C:attention}#1#{} nie będzie w talii ", + "{C:inactive,s:0.8}Ranga się nie zmienia" + } + }, + j_cry_blender = { + name = "Blender", + text = { + "Tworzy {C:attention}losowy{}", + "przedmiot zużywalny, kiedy", + "{C:cry_code}Karta Kodowa{} jest użyta", + "{C:inactive}(wymaga miejsca){}", + }, + }, + j_cry_blurred = { + name = "Niewyraźny Joker", + text = { + "Zyskujesz {C:blue}+#1#{} do ręki,", + "kiedy {C:attention}przeszkadzajka{} jest wybrana", + }, + }, + j_cry_bonk = { + name = "Bęc!", + text = { + "Każdy {C:attention}joker{} daje {C:chips}+#1#{} żet.", + "Zwiększa ilość o {C:chips}+#2#{}, jeśli", + "zagrana {C:attention}ręka{} to {C:attention}#3#{}", + "{C:inactive,s:0.8}Wesołe Jokery{} dostają {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}żet.{}", + }, + }, + j_cry_bonkers = { + name = "Porąbany Joker", + text = { + "Mnoż. {C:red}+#1#{} jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_bonusjoker = { + name = "Bonusowy Joker", + text = { + "{C:green}#1# na #2#{} szans dla każdej", + "zagranej {C:attention}Karty Bonusowej{} na zwiększenie", + "slotów na {C:attention}jokera{} lub {C:attention}przedmioty zużywalne", + "o {C:dark_edition}1{} po zdobyciu punktów", + "{C:red}Działa dwa razy na rundę", + "{C:inactive,s:0.8}(Równa szansa dla każdej){}", + }, + }, + j_cry_booster = { + name = "Wzmacniający Joker", + text = { + "{C:attention}+#1#{} slot na paczkę wzmacniającą", + "w sklepie", + }, + }, + j_cry_boredom = { + name = "Znudzenie", + text = { + "{C:green}#1# na #2#{} szans na", + "{C:attention}ponowną aktywację{} każdego {C:attention}jokera{}", + "lub {C:attention}karty rozgrywającej{}", + "{C:inactive,s:0.8}nie dotyczy Znudzenia{}", + }, + }, + j_cry_brittle = { + name = "Krokantowy cukierek", + text = { + "Przez następne {C:attention}#1#{} rąk,", + "aplikuje Kartę {C:attention}Kamienną{}, {C:attention}Złotą{} lub {C:attention}Stalową{} do", + "najbardziej wysuniętej na prawo karty, która zdobyła punkty" + } + }, + j_cry_bubblem = { + name = "Bubble M", + text = { + "Tworzy {C:attention}Wesołego Jokera{} {C:dark_edition}w folii,", + "jeśli zagrana", + "ręka zawiera {C:attention}#1#{}", + "{C:red,E:2}Ulega samozniszczeniu{}", + }, + }, + j_cry_busdriver = { + name = "Kierowca busa", + text = { + "{C:green}#1# na #3#{} szans", + "na mnoż. {C:mult}+#2#{}", + "{C:green}1 na 4{} szans", + "na mnoż. {C:mult}-#2#{}", + }, + }, + j_cry_candy_basket = { + name = "Kosz słodyczy", + text = { + "Sprzedaj tę kartę, aby stworzyć {C:attention}#1#{} {C:cry_candy}Cukierków", + "{C:attention}+#2#{} {C:cry_candy}Cukierek{} co {C:attention}2{} pokonane przeszkadzajki", + "{C:attention}+#3#{} {C:cry_candy}Cukierki{}, kiedy {C:attention}Przeszkadzajka Bossa{} jest pokonana" + } + }, + j_cry_candy_buttons = { + name = "Guziki cukierkowe", + text = { + "Następne {C:attention}#1#{} ponownych rzutów", + "kosztuje {C:money}$1{}", + } + }, + j_cry_candy_cane = { + name = "Laska cukrowa", + text = { + "Przez następne {C:attention}#1#{} rund,", + "zagrane karty dają {C:money}$#2#,", + "kiedy są {C:attention}aktywowane ponownie" + } + }, + j_cry_candy_dagger = { + name = "Cukierkowy sztylet", + text = { + "Kiedy {C:attention}Przeszkadzajka{} jest wybrana,", + "niszczy jokera po prawej", + "i tworzy {C:cry_candy}Cukierka{}", + } + }, + j_cry_candy_sticks = { + name = "Cukrowe Pałeczki", + text = { + "Efekt przeszkadzajki następnego bossa nie jest aktywny,", + "dopóki nie zagrasz {C:attention}#1#{} ręki", + } + }, + j_cry_canvas = { + name = "Płótno", + text = { + "{C:attention}Aktywuj ponownie{} wszystkie {C:attention}jokery{} z lewej strony", + "za {C:attention}każdego{} {C:blue}niepospolitego{C:attention} jokera{}", + "po prawej stronie", + }, + }, + j_cry_caramel = { + name = "Karmel", + text = { + "Każda zagrana karta", + "daje mnoż. {X:mult,C:white}X#1#{} przy zdobyciu punktów", + "przez następne {C:attention}#2#{} rund", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Aktywuje {C:attention}jokera najbardziej wysuniętego na lewo{}", + "jeszcze {C:attention}#1#{} razy", + }, + }, + j_cry_chili_pepper = { + name = "Papryczka chilli", + text = { + "Ten joker zdobywa {X:mult,C:white} X#2# {} do mnożnika", + "na końcu rundy,", + "{C:red,E:2}Ulega samozniszczeniu{} po {C:attention}#3#{} rundach", + "{C:inactive}(obecny mnoż.:{} {X:mult,C:white} X#1# {}", + }, + }, + j_cry_chocolate_dice = { + name = "Czekoladowa kostka", + text = { + "Rzuca {C:green}kostką d10{}, kiedy", + "{C:attention}Przeszkadzajka Bossa{} jest pokonana,", + "aby zacząć {C:cry_ascendant,E:1}zdarzenie", + "{C:inactive}(obecne zdarzenie: #1#)" + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} żet. i mnoż. {X:dark_edition,C:white}^#1#{}, jeśli", + "zostały {C:attention}dokładnie{} #2#", + "ręce", + }, + }, + j_cry_circus = { + name = "Cyrk", + text = { + "{C:red}Rzadkie{} jokery dają mnoż. {X:mult,C:white} X#1# {}", + "{C:cry_epic}Epickie{} jokery dają mnoż. {X:mult,C:white} X#2# {}", + "{C:legendary}Legendarne{} jokery dają mnoż. {X:mult,C:white} X#3# {}", + "{C:cry_exotic}Egzotyczne{} jokery dają mnoż. {X:mult,C:white} X#4# {}", + }, + }, + j_cry_clash = { + name = "Starcie", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_CodeJoker = { + name = "Kodowy Joker", + text = { + "Tworzy {C:dark_edition}Negatyw{}", + "{C:cry_code}Karty Kodowej{}, kiedy", + "{C:attention}przeszkadzajka{} jest wybrana", + }, + }, + j_cry_coin = { + name = "Kryptomoneta", + text = { + "Zyskujesz pomiędzy", + "{C:money}$#1#{} a {C:money}$#2#{} za", + "każdego {C:attention}sprzedanego{} jokera", + }, + }, + j_cry_compound_interest = { + name = "Odsetkowy szał", + text = { + "Zdobywasz {C:money}#1#%{} całkowitej ilości pieniędzy", + "na koniec rundy,", + "zwiększa się o {C:money}#2#%{} przy", + "każdej następnej wypłacie", + }, + }, + j_cry_copypaste = { + name = "Kopiuj/Wklej", + text = { + "Kiedy {C:cry_code}Karta Kodowa{} jest użyta,", + "{C:green}#1# na #2#{} szans na dodanie jej kopii", + "do twojego obszaru przedmiotów zużywalnych", + "{C:inactive}(wymaga miejsce)", + }, + }, + j_cry_cotton_candy = { + name = "Wata cukrowa", + text = { + "Kiedy ta karta zostaje sprzedana, sąsiadujące", + "{C:attention}jokery{} dostają {C:dark_edition}Negatywy{}" + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "Ten joker zdobywa {C:chips}+#2#{} do żet.", + "za {C:attention}ponowny rzut{} w sklepie", + "{C:green}Wszystkie ponowne rzuty są darmowe{}", + "{C:inactive}(obecnie: {C:chips}+#1#{C:inactive} żet.)", + }, + }, + j_cry_cryptidmoment = { + name = "M Łańcuch", + text = { + "Sprzedaj tę kartę,", + "aby dodać {C:money}$#1#{} {C:attention}wartości sprzedaży{}", + "do każdego {C:attention}jokera{}", + }, + }, + j_cry_cube = { + name = "Sześcian", + text = { + "{C:chips}+#1#{} żet.", + }, + }, + j_cry_curse_sob = { + name = "Szloch", + text = { + "{C:edition,E:1}nie możesz{} {C:cry_ascendant,E:1}biec...{}", + "{C:edition,E:1}nie możesz{} {C:cry_ascendant,E:1}się ukryć...{}", + "{C:dark_edition,E:1}nie możesz uciec...{}", + "{C:inactive}(wymaga miejsca){}", + }, + }, + j_cry_cursor = { + name = "Kursor", + text = { + "Ten joker zdobywa {C:chips}+#2#{} żet.", + "za każdą {C:attention}kupioną{} kartę", + "{C:inactive}(obecnie {C:chips}+#1#{C:inactive} żet.)", + }, + }, + j_cry_cut = { + name = "Cięcie", + text = { + "Ten joker niszczy", + "losową {C:cry_code}Kartę Kodową{}", + "i zyskuje {X:mult,C:white} X#1# {} do mnożnika", + "na koniec {C:attention}sklepu{}", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#2# {C:inactive})", + }, + }, + j_cry_delirious = { + name = "Majaczący Joker", + text = { + "Mnoż. {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Dyskretny Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "gryzMoły", + text = { + "Tworzy 2 {C:dark_edition}Negatywy{} {C:attention}przedmiotów zużywalnych,{}", + "kiedy {C:attention}Przeszkadzajka{} jest wybrana", + "Tworzy 1 więcej {C:attention}przedmiot zużywalny", + "za każdego {C:attention}Wesołego Jokera{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Podwójna skala", + text = { + "Skalujące {C:attention}jokery{}", + "skalują się {C:attention}kwadratowo", + "{C:inactive,s:0.8}(np. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(rośną o +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Zrzut z góry", + text = { + "Ten joker zdobywa {X:mult,C:white} X#1# {} do mnożnika", + "za każdą kartę o kolorze {V:1}#2#{}, która {C:attention}nie zdobyła{} punktów", + "Kolor zmienia się co rundę", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#3# {C:inactive})", + }, + }, + j_cry_dubious = { + name = "Wątpliwy Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_duos = { + name = "Duety", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Ten joker zdobywa {X:mult,C:white} X#2# {} do mnożnika,", + "kiedy {C:attention}joker{} lub", + "karta rozgrwająca zdobywa punkty", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#1# {C:inactive})", + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Dobiera {C:green}pełną talię{} do ręki,", + "kiedy {C:attention}Przeszkadzajka{} jest wybrana", + "{C:inactive,s:0.8}\"Jeśli nie potrafisz mnie znieść przy 1x,", + "{C:inactive,s:0.8}to nie zasługujesz na mnie przy 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "Kiedy {C:attention}znacznik{} jest pozyskany,", + "tworzy jego {C:attention}#1#{} kopie", + "i {C:attention}zwiększa{} ilość", + "kopii o {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "As równowagi", + text = { + "Jokery pojawiają się", + "według kolejności z {C:attention}kolekcji{}", + "Tworzy {C:attention}#1#{} {C:dark_edition}Negatywy{} jokerów,", + "kiedy ręka jest zagrana", + "{C:cry_exotic,s:0.8}Egzotyczne {C:inactive,s:0.8}lub lepsze jokery nie mogą się pojawić", + "{s:0.8}Ostatni utworzony joker: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Wieczny płomień", + text = { + "Ten joker zdobywa {X:mult,C:white} X#1# {} do mnożnika", + "za każdą {C:attention}sprzedaną{} kartę", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#2# {C:inactive}", + }, + }, + j_cry_exoplanet = { + name = "Egzoplaneta", + text = { + "Wszystkie karty {C:dark_edition}holograficzne{}", + "dają mnoż. {C:mult}+#1#{}", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "Ten joker zdobywa {X:dark_edition,C:white} ^#1# {} do mnożnika,", + "kiedy {X:red,C:white} XMnożnik {} jest aktywowany", + "{C:inactive}(obecny mnoż.: {X:dark_edition,C:white} ^#2# {C:inactive})", + }, + }, + j_cry_exposed = { + name = "Zdemaskowany", + text = { + "Aktywuj ponownie wszystkie {C:attention}karty numerowane{}", + "jeszcze {C:attention}#1#{} razy", + "Wszystkie {C:attention}figury karciane{} zostają osłabione", + }, + }, + j_cry_facile = { + name = "Łatwizna", + text = { + "Mnoż. {X:dark_edition,C:white}^#1#{}, jeśli", + "zagrane karty zdobyły punkty", + "{C:attention}#2#{} lub więcej razy", + }, + }, + j_cry_filler = { + name = "Wypełniacz", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fraktalne palce", + text = { + "{C:attention}+#1#{} do limitu wyboru kart", + }, + }, + j_cry_flip_side = { + name = "Z drugiej strony", + text = { + "{C:dark_edition}Dwustronne{} jokery używają", + "swojej tylnej strony do efektów", + "zamiast przedniej strony", + "{C:attention}Atywuj ponownie{} wszystkie {C:dark_edition}dwustronne{} jokery" + }, + }, + j_cry_foodm = { + name = "Fast Foodowe M", + text = { + "Mnoż. {C:mult}+#1#{}", + "{C:red,E:2}Ulega samozniszczeniu{} po {C:attention}#2#{} rundach", + "Wzrasta o {C:attention}#3#{} rund, jeśli", + "{C:attention}Wesoły Joker{} jest {C:attention}sprzedany{}", + "{C:inactive,s:0.8}2 McDouble, 2 McChickeny{}", + "{C:inactive,s:0.8}Duże Frytki, 20 Nuggetsów & Duża Cola{}", + }, + }, + j_cry_foolhardy = { + name = "Lekkomyślny Joker", + text = { + "Mnoż. {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_formidiulosus = { + name = "Formidiulosus", + text = { + "Kiedy {X:cry_cursed,C:white}przeklęty{} joker jest pozyskany, niszczy go", + "i tworzy {C:attention}#1#{} {C:dark_edition}Negatywy {C:cry_candy}Cukierków{} na koniec sklepu", + "Każdy posiadany {C:cry_candy}Cukierek{} daje {X:dark_edition,C:white}^#2#{} do mnożnika", + "{C:inactive}(obecny mnoż.: {X:dark_edition,C:white}^#3#{C:inactive})", + }, + }, + j_cry_foxy = { + name = "Sprytny Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget spinner", + text = { + "Ten joker zdobywa {C:chips}+#2#{} do żetonów,", + "jeśli zagrana ręka {C:attention}nie jest{}", + "najczęściej granym {C:attention}układem pokerowym{}", + "{C:inactive}(obecnie: {C:chips}+#1#{C:inactive} żet.)", + }, + }, + j_cry_fuckedup = { + name = "Pierdolnięty Joker", + text = { + "Mnoż. {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_gardenfork = { + name = "Ogród rozgałęziających się ścieżek", + text = { + "Zyskujesz {C:money}$#1#{}, jeśli {C:attention}zagrana ręka{}", + "zawiera {C:attention}Asa{} i {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Bliźnięta", + text = { + "{C:attention}Podwaja{} wszystkie wartości", + "{C:attention}jokera najbardziej wysuniętego na lewo{}", + "na koniec rundy", + }, + }, + j_cry_ghost = { + name = "Duch", + text = { + "Na koniec rundy:", + "{C:green}#1# na #2#{} szans", + "na {C:attention}opętanie{} losowego {C:attention}jokera", + "{C:green}#1# na #3#{} szans na", + "{E:2,C:red}samozniszczenie" + } + }, + j_cry_giggly = { + name = "Absurdalny Joker", + text = { + "Mnoż. {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Złoty Joker", + text = { + "Zyskujesz {C:money}#1#%{} całkowitej", + "ilości pieniędzy na koniec rundy", + "Wypłata rośnie o {C:money}#2#%{} za każdym razem,", + "kiedy Karta {C:attention}Złota{}", + "zdobywa punkty", + }, + }, + j_cry_googol_play = { + name = "Karta Googol Play", + text = { + "{C:green}#1# na #2#{} szans", + "na mnoż. {X:red,C:white} X#3# {}", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Tworzy losowego {C:attention}jokera{}", + "na końcu rundy", + "Sprzedaj tę kartę,", + "aby stworzyć losowego {C:attention}jokera{}", + "{C:inactive}(wymaga miejsca){}", + }, + }, + j_cry_happyhouse = { + name = "Szczęśliwy dom", + text = { + "Mnożnik {X:dark_edition,C:white}^#1#{} po zagraniu", + "{C:attention}114{} rąk{}", + "{C:inactive}(Obecnie #2#/114){}", + "{C:inactive,s:0.8}Nie ma jak w domu!{}", + }, + }, + j_cry_home = { + name = "Dom", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Do schrupania", + text = { + "Zyskujesz {C:money}$#1#{}, kiedy", + "używasz {C:attention}przedmiotu zużywalnego{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Aktywuj ponownie wszystkie zagrane karty", + "{C:attention}#2#{} razy,", + "Każda zagrana karta zdobywa", + "mnoż. {X:mult,C:white} X#1# {} kiedy zdobywa punkty", + }, + }, + j_cry_jawbreaker = { + name = "Łamiszczęka", + text = { + "Kiedy {C:attention}Przeszkadzajka{} jest pokonana,", + "{C:attention}podwaja{} wartości sąsiadujących jokerów", + "{E:2,C:red}Ulega samozniszczeniu{}", + } + }, + j_cry_jimball = { + name = "Jimball", + text = { + "Ten joker zdobywa {X:mult,C:white} X#1# {} do mnożnika", + "za {C:kattention}kolejne{} zagrywane ręce", + "z twoim", + "najczęściej zagrywanym {C:attention}układem pokerowym", + "{C:inactive}(obecny mnoż. {X:mult,C:white} X#2# {C:inactive}", + }, + }, + j_cry_jollysus = { + name = "Wesoły Joker?", + text = { + "Tworzy {C:dark_edition}Wesołego{} Jokera,", + "kiedy joker jest {C:attention}sprzedany{}", + "{C:red}Działa raz na rundę{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Wygląda legitnie...{}", + }, + }, + j_cry_kidnap = { + name = "Porwanie", + text = { + "Zyskujesz {C:money}$#2#{} na końcu rundy", + "Wypłata rośnie o {C:money}$#1#{}", + "kiedy Joker {C:attention}mnożnikowy{} lub", + "{C:attention}żetonowy{} zostaje sprzedany", + }, + }, + j_cry_kooky = { + name = "Szurnięty Joker", + text = { + "Mnożnik {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Klaun Krusty", + text = { + "Ten joker zdobywa", + "{X:mult,C:white} X#1# {} do mnożnika,", + "kiedy każda zagrana {C:attention}karta{} zdobywa punkty", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#2# {C:inactive}", + }, + }, + j_cry_kscope = { + name = "Kalejdoskop", + text = { + "Dodaje {C:dark_edition}polichromowanie{} do", + "losowego {C:attention}jokera{}, kiedy", + "pokonana zostanie {C:attention}Przeszkadzajka Bossa{}", + }, + }, + j_cry_lightupthenight = { + name = "Rozświetlenie nocy", + text = { + "Każda zagrana {C:attention}7{} lub {C:attention}2{}", + "daje mnoż. {X:mult,C:white}X#1#{}, kiedy zdobywa punkty", + }, + }, + j_cry_longboi = { + name = "Potwór", + text = { + "Daje przyszłym kopiom", + "tego jokera mnoż. {X:mult,C:white}X#1#{}", + "na koniec rundy", + "{C:inactive}(obecny mnoż.: {X:mult,C:white}X#2#{C:inactive}){}", + }, + }, + j_cry_loopy = { + name = "Zawijas", + text = { + "{C:attention}Aktywuje ponownie{} wszystkie jokery", + "za każdego {C:attention}Wesołego{}", + "{C:attention}Jokera{} sprzedanego w tym podejściu", + "{C:inactive}(obecnie:{}{C:attention:} #1#{}{C:inactive} ponownych aktywacji){}", + "{C:inactive,s:0.8}nie było wystarczająco miejsca...{}", + }, + }, + j_cry_lucky_joker = { + name = "Szczęśliwy Joker", + text = { + "Zyskujesz {C:money}$#1#{} za każdym razem,", + "kiedy {C:attention}Karta Szczęścia{} zostaje", + "{C:green}skutecznie aktywowana{}", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "Wszystkie jokery dają", + "{X:chips,C:white} X#1# {} żet.", + }, + }, + j_cry_m = { + name = "m", + text = { + "Ten joker zdobywa {X:mult,C:white} X#1# {} do mnożnika,", + "kiedy {C:attention}Wesoły Joker{} zostaje sprzedany", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#2# {C:inactive})", + }, + }, + j_cry_M = { + name = "M", + text = { + "Tworzy {C:dark_edition}Negatyw{}", + "{C:attention}Wesołego Jokera{}, kiedy", + "{C:attention}przeszkadzajka{} jest wybrana", + }, + }, + j_cry_macabre = { + name = "Makabryczny Joker", + text = { + "Kiedy {C:attention}przeszkadzajka{} jest wybrana,", + "niszczy każdego {C:attention}jokera{} oprócz", + "{C:legendary}M Jokerów{} i {C:attention}Wesołych Jokerów{}", + "i tworzy 1 {C:attention}Wesołego Jokera{}", + "za każdego zniszczonego jokera", + }, + }, + j_cry_magnet = { + name = "Magnes na lodówkę", + text = { + "Zyskujesz {C:money}$#1#{} na końcu rundy", + "Zyskujesz {X:money,C:white} X#2# {} więcej, jeśli posiadasz", + "{C:attention}#3#{} lub mniej {C:attention}jokerów{}", + }, + }, + j_cry_manic = { + name = "Maniakalny Joker", + text = { + "Mnoż. {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Aktywuje ponownie wszystkie jokery", + "jeszcze {C:attention}#1#{} razy", + }, + }, + j_cry_mask = { + name = "Maska", + text = { + "Uruchom ponownie wszystkie {C:attention}figury karciane{}", + "jeszcze {C:attention}#1#{} razy", + "Wszystkie {C:attention}numerowane karty{} zostają osłabione", + }, + }, + j_cry_maximized = { + name = "Na maksa", + text = { + "Wszystkie {C:attention}figury karciane{}", + "są traktowane jako {C:attention}Królowie{},", + "wszystkie {C:attention}numerowane karty{}", + "są traktowane jako {C:attention}10-tki{}", + }, + }, + j_cry_maze = { + name = "Labirynt", + text = { + "Wszystkie ręce są traktowane jako", + "{C:attention}pierwsza ręka{} rundy,", + "wszystkie zrzutki są traktowane jako", + "{C:attention}pierwsza zrzutka{} rundy", + }, + }, + j_cry_Megg = { + name = "M-Jajko", + text = { + "Sprzedaj tę kartę, aby stworzyć", + "{C:attention}#2#{} {C:attention}Wesołych Jokerów{}, liczba rośnie", + "o {C:attention}#1#{} na koniec rundy", + }, + }, + j_cry_mellowcreme = { + name = "Cukierkowa dynia", + text = { + "Sprzedaj tę kartę, aby {C:attention}pomnożyć", + "wartość sprzedaży wszystkich", + "{C:attention}przedmiotów zużywalnych{} o {C:attention}X#1#" + } + }, + j_cry_membershipcard = { + name = "Karta członkowska", + text = { + "Mnoż. {X:mult,C:white}X#1#{} za każdego członka", + "discordowego serwera {C:attention}Cryptid{}", + "{C:inactive}(obecny mnoż.: {X:mult,C:white}X#2#{C:inactive}", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Dawna karta członkowska", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} za każdego członka", + "discordowego serwera {C:attention}Cryptid{}", + "{C:inactive}(obecnie: {C:chips}+#2#{C:inactive} żet.)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Rój meteorów", + text = { + "Wszystkie karty {C:dark_edition}w folii{}", + "dają {C:chips}+#1#{} żet.", + }, + }, + j_cry_mneon = { + name = "Neonowe M", + text = { + "Na koniec rundy zyskujesz {C:money}$#2#{}", + "Wypłata zwiększa się", + "o {C:money}$#1#{} za każdego {C:attention}Wesołego Jokera{}", + "lub {C:legendary}M Jokera{} na", + "koniec rundy", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "Ten joker zdobywa {X:mult,C:white} X#1# {} do mnożnika,", + "jeśli nie skorzystano ze {C:attention}zrzutek{}", + "do końca tej rundy", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#2# {C:inactive}", + }, + }, + j_cry_monkey_dagger = { + name = "Małpi sztylet", + text = { + "Po wybraniu {C:attention}przeszkadzajki{},", + "niszczy jokera po lewej", + "i na stałe dodaje {C:attention}10-krototność{}", + "jego wartości sprzedaży do {C:chips}żetonów{}", + "{C:inactive}(obecnie: {C:chips}+#1#{C:inactive} żet.)", + }, + }, + j_cry_monopoly_money = { + name = "Monopolowe pieniądze", + text = { + "{C:green}#1# na #2#{} szans na", + "{C:attention}zniszczenie{} zakupionych przedmiotów", + "Obniża pieniądze o połowę po {C:attention}sprzedaży{} tej karty", + } + }, + j_cry_morse = { + name = "Kod Morse’a", + text = { + "Zyskujesz {C:money}$#2#{} na końcu rundy", + "Wypłata rośnie o {C:money}$#1#{}, kiedy", + "karta z {C:attention}edycją{} zostaje sprzedana", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Tworzy {C:legendary}M Jokera{} na koniec rundy", + "Każdy {C:attention}Wesoły Joker{} lub {C:legendary}M Joker", + "daje {X:dark_edition,C:white}^#1#{} do mnożnika", + "Zwiększa ilość o {X:dark_edition,C:white}^#2#{},", + "kiedy {C:attention}Wesoły Joker{} jest {C:attention}sprzedany", + "{C:inactive,s:0.8}(Tredecim nie może być stworzony)", + }, + }, + j_cry_mstack = { + name = "Stos M", + text = { + "Aktywuj ponownie wszystkie zagrane karty", + "za każde", + "{C:attention}#2#{} {C:inactive}[#3#]{} sprzedane {C:attention}Wesołe Jokery{}", + "{C:inactive}(obecnie{}{C:attention:} #1#{}{C:inactive} ponownych aktywacji){}", + }, + }, + j_cry_multjoker = { + name = "Mnożnikowy Joker", + text = { + "{C:green}#1# na #2#{} szans dla każdej", + "zagranej karty {C:attention}Karty Mnożnikowej{} na stworzenie", + "{C:spectral}Kryptydy{} po zdobyciu punktów", + "{C:inactive}(wymaga miejsca)", + }, + }, + j_cry_necromancer = { + name = "Nekromanta", + text = { + "Kiedy joker jest {C:attention}sprzedany{} za więcej niż {C:attention}$0{},", + "otrzymujesz {C:attention}losowego{} jokera {C:attention}sprzedanego{} w tym podejściu", + "o wartości {C:money}$0{}", + }, + }, + j_cry_negative = { + name = "Negatywny Joker", + text = { + "{C:dark_edition}+#1# sloty na{C:attention} jokery{}", + }, + }, + j_cry_nice = { + name = "Najs", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana ręka", + "zawiera {C:attention}6{} i {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Noc", + text = { + "Mnoż. {X:dark_edition,C:white}^#1#{}", + "w ostatniej ręce rundy", + "{E:2,C:red}Ulega samozniszczeniu{}", + "w ostatniej ręce rundy", + }, + }, + j_cry_nosound = { + name = "Bez dźwięku, bez wspomnienia", + text = { + "Aktywuj ponownie każdą zagraną {C:attention}7{}", + "jeszcze {C:attention:}#1#{} razy", + }, + }, + j_cry_notebook = { + name = "Zeszyt", + text = { + "{C:green} #1# na #2#{} szans na {C:dark_edition}+1{} slot na jokera", + "za {C:attention}ponowny rzut{} w sklepie", + "{C:green}Zawsze aktywuje się,{} jeśli", + "posiadasz {C:attention}#5#{} lub więcej {C:attention}Wesołych Jokerów{}", + "{C:red}Działa raz na rundę{}", + "{C:inactive}(obecnie: {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Klocki liczbowe", + text = { + "Zdobywasz {C:money}$#1#{} na koniec rundy", + "Wypłata zwiększa się o {C:money}$#2#{}", + "za każdą kartę {C:attention}#3#{} trzymaną w ręcę,", + "ranga zmienia się co rundę", + }, + }, + j_cry_nuts = { + name = "Orzeszki", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Bzikowaty Joker", + text = { + "Mnoż. {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_oil_lamp = { + name = "Lampa naftowa", + text = { + "Zwiększa wartości {C:attention}jokera{} po prawej stronie", + "o {C:attention}X#1#{} na koniec rundy", + }, + }, + j_cry_oldblueprint = { + name = "Dawna światłokopia", + text = { + "Powiela zdolność", + "{C:attention}jokera{} na prawo", + "{C:green}#1# na #2#{} szans, że ta", + "karta zostanie zniszczona", + "na końcu rundy", + }, + }, + j_cry_oldcandy = { + name = "Nostalgiczny cukierek", + text = { + "Sprzedaj tę kartę,", + "aby na stałe zyskać", + "{C:attention}+#1#{} do rozmiaru ręki", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgiczny Niewidzialny Joker", + text = { + "{C:attention}Kopiuje{} losowego", + "{C:attention}joker{} co {C:attention}4", + "sprzedane jokery", + "{s:0.8}oprócz Nostalgicznego Niewidzialnego Jokera{}", + "{C:inactive}(obecnie: #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panoptykon", + text = { + "Wszystkie ręce są traktowane", + "jako {C:attention}ostatnia ręka{}", -- +$4 + }, + }, + j_cry_penetrating = { + name = "Penetrujący Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_pickle = { + name = "Ogór", + text = { + "Kiedy {C:attention}przeszkadzajka{} jest pominięta, tworzy", + "{C:attention}#1#{} znaczniki, zmniejsza się", + "o {C:red}#2#{} kiedy {C:attention}przeszkadzajka{} zostaje wybrana", + }, + }, + j_cry_pirate_dagger = { + name = "Piracki sztylet", + text = { + "Po wybraniu {C:attention}przeszkadzajki{},", + "niszczy jokera po prawej", + "oraz na stałe dodaje {C:attention}0,25{} jego", + "wartości sprzedaży do {X:chips,C:white} mnoż. żetonów {}", + "{C:inactive}(obecny mnoż. żetonów: {X:chips,C:white} X#1# {C:inactive})", + }, + }, + j_cry_pity_prize = { + name = "Nagroda pocieszenia", + text = { + "Kiedy pomijasz {C:attention}paczkę wzmacniającą{}, zyskujesz losowy {C:attention}znacznik{}" + }, + }, + j_cry_pot_of_jokes = { + name = "Garnek żartów", + text = { + "{C:attention}#1#{} do rozmiaru ręki,", + "rośnie o ", + "{C:blue}#2#{} w każdej rundzie", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "Ten joker zdobywa {X:dark_edition,C:white} ^#1# {} do mnożnika,", + "jeśli wszystkie punktujące karty w zagranej ręce to", + "{C:attention}As{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, lub {C:attention}7{}", + "{C:inactive}(obecny mnoż.: {X:dark_edition,C:white} ^#2# {C:inactive})", + }, + }, + j_cry_python = { + name = "Python", + text = { + "Ten joker zdobywa", + "{X:mult,C:white} X#1# {} do mnożnika, kiedy", + "{C:cry_code}Karta Kodowa{} jest użyta", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#2# {C:inactive})", + }, + }, + j_cry_queens_gambit = { + name = "Gambit królowej", + text = { + "Jeśli {C:attention}zagrana ręka{} to", + "{C:attention}Poker Królewski{}, niszczy", + "{C:attention}Królową{} i tworzy", + "{C:dark_edition}Negatyw {}{C:red}rzadkiego{}{C:attention} jokera{}", + }, + }, + j_cry_quintet = { + name = "Kwintet", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Czerwony balonik", + text = { + "Przez {C:attention}#2#{} rundy zyskujesz {C:money}$#1#{}", + "{C:red,E:2}Ulega samozniszczeniu{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} do wejścia, po", + "wydanych {C:money}$#2#{} {C:inactive}($#3#){}", + "{s:0.8}Wymagania rosną", + "{C:attention,s:0.8}wykładniczo{s:0.8} co użycie", + "{C:money,s:0.8}Następna podwyżka: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "Kiedy {C:attention}Joker{} jest sprzedany,", + "aplikuje jego efekty", + "do pozostałych jokerów", + "{C:inactive,s:0.8}nie dotyczy pozostałych Rescribere{}" + } + }, + j_cry_reverse = { + name = "Odwrócona karta", + text = { + "Wypełnia wszystkie puste sloty na jokery {C:inactive}(maks. 100){}", + "{C:dark_edition}holograficznymi{} {C:attention}Wesołymi Jokerami{}, jeśli", + "{C:attention}odrzucony układ pokerowy{} to {C:attention}#1#{}", + "{C:red,E:2}Ulega samozniszczeniu{}", + "{C:inactive,s:0.8}OSTATECZNY powrót{}", + }, + }, + j_cry_rnjoker = { + name = "RN Joker", + text = { + "Losuje umiejętności podczas każdego {C:attention}wejścia{}", + }, + }, + j_cry_sacrifice = { + name = "Poświęcenie", + text = { + "Tworzy {C:green}niepospolitego{} jokera", + "i 3 {C:attention}Wesołe Jokery{}, kiedy", + "karta {C:spectral}Ducha{} jest użyta", + "{C:red}Działa raz na rundę{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sadzonka", + text = { + "Po zagraniu {C:attention}#2#{} {C:inactive}[#1#]{} ulepszonych", + "kart, sprzedaj tę kartę", + "aby stworzyć {C:cry_epic}epickiego{} {C:attention}jokera{}", + "{C:inactive,s:0.8}stworzy {C:red,s:0.8}rzadkiego{} {C:attention,s:0.8}jokera{}", + "{C:inactive,s:0.8}jeśli {C:cry_epic,s:0.8}epickie{} {C:inactive,s:0.8}jokery są wyłączone{}", + }, + }, + j_cry_savvy = { + name = "Kumaty Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Skalujące {C:attention}jokery{} skalują się", + "jako wielomian stopnia {C:attention}#1#{}", + "Podnosi stopień o {C:attention}#2#{}", + "na koniec rundy", + "{C:inactive,s:0.8}({C:attention,s:0.8}Z wyjątkiem Scalae{C:inactive,s:0.8})", + }, + }, + j_cry_scrabble = { + name = "Płytka Scrabble", + text = { + "{C:green}#1# na #2#{} szans na stworzenie", + "{C:dark_edition}wesołego {C:green}niepospolitego{} jokera,", + "kiedy ręka jest zagrana", + }, + }, + j_cry_seal_the_deal = { + name = "Zapieczętowana sprawa", + text = { + "Dodaje {C:attention}losową pieczęć{} do wszystkich kart,", + "które zdobyły punkty w {C:attention}ostatniej ręce{} rundy", + }, + }, + j_cry_shrewd = { + name = "Przenikliwy Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_silly = { + name = "Głupiutki Joker", + text = { + "Mnoż. {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Malutki", + text = { + "Tworzy {C:cry_jolly}znacznik podwójnego M,", + "jeśli {C:attention}zagrana ręka{},", + "to {C:attention}#1#{}", + "{C:inactive,s:0.8}ok zasadniczo to jestem bardzo mały", + }, + }, + j_cry_soccer = { + name = "Jeden dla wszystkich", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} slot na jokera", + "{C:attention}+#1#{} paczka wzmacniająca", + "{C:attention}+#1#{} do rozmiaru ręki", + "{C:attention}+#1#{} slot na przedmioty zużywalne", + "{C:attention}+#1#{} karta w sklepie", + }, + }, + j_cry_fleshpanopticon = { + name = "Cielesny Panoptykon", + text = { + "{C:red}X#1#{} do rozmiaru {C:attention}Przeszkadzajki Bossa{}", + "Kiedy {C:attention}Przeszkadzajka Bossa{} jest pokonana,", + "{C:red}karta ulega samozniszczeniu{} i tworzy", + " {C:dark_edition}Negatyw{} karty {C:spectral}Wrota{}", + "{C:inactive,s:0.8}\"To więzienie... ma mnie powstrzymać?\"" + }, + }, + j_cry_spaceglobe = { + name = "Niebiański glob", + text = { + "Ten joker zdobywa {X:chips,C:white}X#2#{} żet.,", + "jeśli zagrany {C:attention}układ pokerowy{} to {C:attention}#3#{},", + "Układ zmienia się po wzroście wartości{}", + "{C:inactive}(obecnie:{} {X:chips,C:white}X#1#{} {C:inactive}żet.){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Tworzy {C:dark_edition}Negatyw{} kopii", + "losowego {C:attention}jokera{}", + "na koniec {C:attention}sklepu", + "{C:inactive,s:0.8}Nie kopiuje Speculo{}", + }, + }, + j_cry_spy = { + name = "Szpieg", + text = { + "Mnoż. {X:mult,C:white} X#2# {}, {C:dark_edition}+1{} slot na {C:attention}jokera{}", + "{C:inactive}#1# jest szpiegiem!", + }, + }, + j_cry_stardust = { + name = "Gwiezdny pył", + text = { + "Wszystkie karty {C:dark_edition}polichromowane{}", + "dają mnoż. {X:mult,C:white}X#1#{}", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "Ten joker niszczy", + "losową Kartę {C:planet}Planety{}", + "i zdobywa {X:dark_edition,C:white} ^#1# {} do mnożnika", + "na koniec {C:attention}sklepu{}", + "{C:inactive}(obecny mnoż.: {X:dark_edition,C:white} ^#2# {C:inactive})", + }, + }, + j_cry_stronghold = { + name = "Twierdza", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_subtle = { + name = "Subtelny Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Superkomórka", + text = { + "{C:chips}+#1#{} żet., mnoż. {C:mult}+#1#{},", + "{X:chips,C:white}X#2#{} żet., mnoż. {X:mult,C:white}X#2#{}", + "Zyskujesz {C:money}$#3#{}", + "na koniec rundy", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "Na końcu rundy, tworzy", + "{C:attention}kopię{} losowej", + "karty {C:attention}trzymanej w ręce{} i", + "niszczy pozostałe", + "{C:attention,s:0.8}Królowie{s:0.8} {C:hearts,s:0.8}Kier{s:0.8} mają priorytet", + }, + }, + j_cry_swarm = { + name = "Rój", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Katalizator synchronizacji", + text = { + "Równoważy {C:chips}żetony{} i {C:mult}mnożnik{}", + "{C:inactive,s:0.8}Ej! Gdzieś to widziałem!", + }, + }, + j_cry_tax_fraud = { + name = "Oszust podatkowy", + text = { + "Zyskujesz {C:attention}$#1#{} za każdego {C:attention}wypożyczonego", + "jokera na koniec rundy", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1# slotów na {C:attention}Jokery{}", + "Zyskujesz {C:money}$#2#{} na koniec rundy", + }, + }, + j_cry_translucent = { + name = "Przezroczysty Joker", + text = { + "Sprzedaj tę kartę, aby", + "stworzyć {C:attention}bananową, nietrwałą{} kopię", + "losowego {C:attention}jokera{}", + "{s:0.8,C:inactive}(kopia pomija niekompatybilność)", + }, + }, + j_cry_treacherous = { + name = "Podstępny Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_trick_or_treat = { + name = "Cukierek albo psikus", + text = { + "Kiedy ta karta jest {C:attention}sprzedana{}:", + "{C:green}#1# na #2#{} szans na stworzenie {C:attention}2{} {C:cry_candy}Cukierków", + "W innym razie, tworzy {X:cry_cursed,C:white}przeklętego{} jokera", + "{C:inactive}(może zignorować miejsce na jokery)" + } + }, + j_cry_tricksy = { + name = "Figlarny Joker", + text = { + "{C:chips}+#1#{} żet., jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Potrójny rytm", + text = { + "Mnoż. {X:mult,C:white} X#1# {},", + "jeśli zagrana ręka zawiera {C:attention}dokładnie{} trzy {C:attention}3", + }, + }, + j_cry_tropical_smoothie = { + name = "Tropikalne smoothie", + text = { + "Sprzedaj tę kartę, aby", + "{C:attention}zwiększyć{} wartości", + "posiadanych jokerów o {C:attention}X1.5{}", + }, + }, + j_cry_unity = { + name = "Jedność", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + j_cry_universe = { + name = "Wszechświat", + text = { + "Wszystkie karty {C:dark_edition}astralne{}", + "dają mnoż. {X:dark_edition,C:white}^#1#{}", + }, + }, + j_cry_universum = { + name = "Uniwersum", + text = { + "{C:attention}Układy pokerowe{} zdobywają", + "mnoż. {X:red,C:white} X#1# {} i mnoż. żet. {X:blue,C:white} X#1# {},", + "kiedy są ulepszane", + }, + }, + j_cry_unjust_dagger = { + name = "Niesprawiedliwy sztylet", + text = { + "Po wybraniu {C:attention}przeszkadzajki{},", + "niszczy jokera po lewej", + "i zdobywa {C:attention}0,2{} jego", + "sprzedaży do {X:mult,C:white} mnożnika {}", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#1# {C:inactive})", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Panna", + text = { + "Ten joker zyskuje {C:money}$#1#{} do {C:attention}wartości sprzedaży{},", + "jeśli {C:attention}zagrana ręka{} zawiera {C:attention}#2#{}", + "Sprzedaj tę kartę, aby stworzyć", + "{C:dark_edition}polichromowanego{} {C:attention}Wesołego Jokera{} za", + "każde {C:money}$4{} z jego {C:attention}wartości sprzedaży{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Ekscentryczny Joker", + text = { + "Mnoż. {C:red}+#1#{}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "Wszytkie jokery dają", + "mnoż. {X:mult,C:white} X#1#{}", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "Wszystkie jokery dają", + "{C:money}$#1#{} kiedy są aktywowane", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "Ten joker zdobywa", + "{C:mult}+#2#{} do mnożnika kiedy każdy", + "{C:attention}As{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "zdobywa punkty", + "{C:inactive}(obecny mnoż.: {C:mult}+#1#{C:inactive})", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Aktywuj ponownie wszystkie zagrane {C:attention}2{}", --wee gaming + "jeszcze {C:attention:}#2#{} razy", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Koło nadziei", + text = { + "Ten joker zdobywa", + "{X:mult,C:white} X#1# {} do mnożnika po nieudanej", + "aktywacji {C:attention}Koła Fortuny{}", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#2# {C:inactive})", + }, + }, + j_cry_whip = { + name = "Bicz", + text = { + "Ten joker zdobywa {X:mult,C:white} X#1# {} do mnożnika,", + "jeśli {C:attention}zagrana ręka{} zawiera", + "{C:attention}2{} i {C:attention}7{} w różnych kolorach", + "{C:inactive}(obecny mnoż.: {X:mult,C:white} X#2# {C:inactive}", + }, + }, + j_cry_wrapped = { + name = "Zawinięty cukierek", + text = { + "Tworzy losowego {C:attention}kulinarnego jokera{}", + "po {C:attention}#1#{} rundach", + "{C:red,E:2}Ulega samozniszczeniu{}", + }, + }, + j_cry_wtf = { + name = "Co kurwa!?", + text = { + "Mnoż. {X:mult,C:white} X#1# {}, jeśli zagrana", + "ręka zawiera", + "{C:attention}#2#", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}poz.#4#{})({V:2}poz.#5#{})({V:3}poz.#6#{})", + "Zwiększ poziom układów:", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "i {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}poz.#4#{})({V:2}poz.#5#{})({V:3}poz.#6#{})", + "Zwiększ poziom układów:", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "i {C:attention}#3#{}", + }, + }, + c_cry_Kaikki = { + name = "Kaikki", + text = { + "({V:1}poz.#4#{})({V:2}poz.#5#{})({V:3}poz.#6#{})", + "Zwiększ poziom układów:", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "i {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Gwiazda Neutronowa", + text = { + "Ulepsza losowy", + "układ pokerowy o", + "{C:attention}1{} poziom po każdym", + "użyciu {C:attention}Gwiazdy Neutronowej{}", + "w tym podejściu", + "{C:inactive}(Obecnie{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# na #2#{} szans na", + "ulepszenie każdego", + "{C:legendary,E:1}układu pokerowego{}", + "o {C:attention}1{} poziom", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}poz.#4#{})({V:2}poz.#5#{})({V:3}poz.#6#{})", + "Zwiększ poziom układów:", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "i {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}poz.#4#{})({V:2}poz.#5#{})({V:3}poz.#6#{})", + "Zwiększ poziom układów:", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "i {C:attention}#3#{}", + }, + }, + c_cry_marsmoons = { + name = 'Fobos & Deimos', + text = { + "{S:0.8}({S:0.8,V:1}poz.#1#{S:0.8}){} Zwiększ poziom układu:", + "{C:attention}#2#", + "mnoż. {C:mult}+#3#{} oraz", + "{C:chips}+#4#{} żet." + } + }, + c_cry_void = { + name = 'Otchłań', + text = { + "{S:0.8}({S:0.8,V:1}poz.#1#{S:0.8}){} Zwiększ poziom układu:", + "{C:attention}#2#", + "mnoż. {C:mult}+#3#{} oraz", + "{C:chips}+#4#{} żet." + } + }, + c_cry_asteroidbelt = { + name = 'Pas Asteroid', + text = { + "{S:0.8}({S:0.8,V:1}poz.#1#{S:0.8}){} Zwiększ poziom układu:", + "{C:attention}#2#", + "mnoż. {C:mult}+#3#{} oraz", + "{C:chips}+#4#{} żet." + } + }, + c_cry_universe = { + name = 'Wszechświat W Swojej Całej Pierdolonej Okazałości', + text = { + "{S:0.8}({S:0.8,V:1}poz.#1#{S:0.8}){} Zwiększ poziom układu:", + "{C:attention}#2#", + "mnoż. {C:mult}+#3#{} oraz", + "{C:chips}+#4#{} żet." + } + }, + }, + Sleeve = { + sleeve_cry_bountiful_sleeve = { + name = "Obfity Rękaw", + text = { + "Po {C:blue}zagraniu{} lub {C:red}zrzutce{},", + "zawsze dobierasz {C:attention}5{} kart" + }, + }, + sleeve_cry_ccd_sleeve = { + name = "Rękaw CCD", + text = { + "Każda karta jest również", + "{C:attention}losowym{} przedmiotem zużywalnym", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Taśmociągowy Rękaw", + text = { + "Jokery {C:attention}nie mogą{} zmieniać pozycji", + "Na początku rundy,", + "{C:attention}duplikuje{} jokera najbardziej wysuniętego na prawo", + "i {C:attention}niszczy{} najbardziej wysuniętego na lewo", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Krytyczny Rękaw", + text = { + "Po każdej zagranej ręce,", + "{C:green}#1# na 4{} szans na mnożnik {X:dark_edition,C:white} ^2 {}", + "{C:green}#1# na 8{} szans na mnożnik {X:dark_edition,C:white} ^0.5 {}", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Zakodowany Rękaw", + text = { + "Rozpoczynasz podejście z", + "{C:cry_code,T:j_cry_CodeJoker}Kodowym Jokerem{} i {C:cry_code,T:j_cry_copypaste}Kopiuj/Wklej{}", + "Tylko {C:cry_code}Karty kodowe{} pojawiają się w sklepie", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Zbalansowany Rękaw", + text = { + "Wszystkie karty mają", + "{C:attention}równą{} szansę", + "pojawienia się w sklepie,", + "rozpoczynasz podejście z", + "dodatkowymi {C:attention,T:v_overstock_plus}+2 slotami{} w sklepie", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Nieskończony Rękaw", + text = { + "Możesz wybrać {C:attention}dowolną", + "liczbę kart", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Błędny Rękaw", + text = { + "Wartości kart", + "są {C:attention}losowe", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Odkupiony Rękaw", + text = { + "Przy zakupie {C:attention}kuponu{} ,", + "otrzymujesz jego {C:attention}dodatkowe poziomy", + }, + }, + sleeve_cry_spooky_sleeve = { + name = "Upiorny Rękaw", + text = { + "Rozpoczynasz podejście z {C:eternal}Wieczną{} {C:attention,T:j_cry_chocolate_dice}Czekoladową kostką", + "Po każdym {C:attention}wejściu{}, tworzy", + "{C:cry_candy}Cukierka{} lub {X:cry_cursed,C:white}Przeklętego{} jokera", + } + }, + sleeve_cry_wormhole_sleeve = { + name = "Tuneloczasowy Rękaw", + text = { + "Rozpoczynasz podejście z {C:cry_exotic}egzotycznym{C:attention} jokerem", + "{C:attention}X20{} prawdopodobieństwa", + "na {C:dark_edition}Negatywy{} jokerów", + "{C:attention}-2{} sloty na jokera", + }, + }, + sleeve_cry_legendary_sleeve = { + name = "Legendarny Rękaw", + text = { + "Rozpoczynasz podejście z {C:legendary}legendarnym{C:legendary} jokerem", + "{C:green}1 in 5{} szans na dodanie kolejnego", + "po pokonaniu przeszkadzajki bossa {C:inactive}(wymaga miejsca){}", + }, + }, + }, + Spectral = { + c_cry_adversary = { + name = "Adwersarz", + text = { + "{C:red}Wszystkie{} twoje {C:attention}jokery{} dostają {C:dark_edition}Negatyw{},", + "{C:red}wszystkie{} {C:attention}jokery{} w sklepie są", + "{C:red}dwa razy{} droższe do końca podejścia", + }, + }, + c_cry_analog = { + name = "Analog", + text = { + "Tworzy {C:attention}#1#{} kopie", + "losowego {C:attention}jokera{}, niszczy", + "wszystkie inne jokery, {C:attention}+#2#{} do wejścia", + }, + }, + c_cry_chambered = { + name = "Komora", + text = { + "Tworzy {C:attention}#1#{} {C:dark_edition}negatywne{}", + "kopie", + "{C:attention}dowolnego{} przedmiotu zużywalnego", + "{C:inactive,s:0.8}Nie kopiuje Komory{}" + }, + }, + c_cry_conduit = { + name = "Przewód", + text = { + "Zamienia {C:attention}edycje{}", + "{C:attention}2{} wybranych kart lub {C:attention}jokerów{}", + }, + }, + c_cry_gateway = { + name = "Brama", + text = { + "Tworzy losowego", + "{C:cry_exotic,E:1}egzotycznego{C:attention} jokera{}, niszczy", + "wszystkie inne jokery", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Dodaje losowe {C:attention}przedmioty zużywalne{}", + "jako {C:dark_edition}ulepszenia{}", + "do kart trzymanych w ręce", + }, + }, + c_cry_lock = { + name = "Blokada", + text = { + "Usuwa {C:red}wszystkie{} naklejki", + "ze {C:red}wszystkich{} jokerów,", + "a następnie dodaje {C:purple,E:1}Wieczną{}", + "naklejkę na losowego {C:attention}jokera{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Tworzy {C:cry_code}wybranego", + "jokera lub kartę", + "{C:inactive,s:0.8}(Z wyjątkiem egzotycznych jokerów)", + }, + }, + c_cry_replica = { + name = "Replika", + text = { + "Zamienia wszystkie", + "karty w ręcę", + "na {C:attention}losową{}", + "kartę trzymaną w ręcę", + }, + }, + c_cry_ritual = { + name = "Rytuał", + text = { + "Dodaje {C:dark_edition}Negatyw{}, {C:dark_edition}mozaiczną{},", + "lub {C:dark_edition}astralną{} edycję do {C:attention}#1#{}", + "wybranej karty w ręcę", + }, + }, + c_cry_source = { + name = "Źródło", + text = { + "Dodaj {C:cry_code}Zieloną Pieczęć{}", + "do {C:attention}#1#{} wybranej", + "karty w ręcę", + }, + }, + c_cry_summoning = { + name = "Przywołanie", + text = { + "Tworzy losowego", + "{C:cry_epic}epickiego{} {C:joker}jokera{}, niszczy", + "jednego losowego {C:joker}jokera{}", + }, + }, + c_cry_trade = { + name = "Handel", + text = { + "{C:attention}Tracisz{} losowy kupon,", + "zyskujesz {C:attention}2{} losowe kupony", + }, + }, + c_cry_typhoon = { + name = "Tajfun", + text = { + "Dodaj {C:cry_azure}Azurową Pieczęć{}", + "do {C:attention}#1#{} wybranej", + "karty w ręcę", + }, + }, + c_cry_vacuum = { + name = "Próżnia", + text = { + "Usuwa {C:red}wszystkie {C:green}modyfikacje{}", + "ze {C:red}wszystkich{} kart trzymanych w ręcę,", + "Zarabiasz {C:money}$#1#{} za każdą usuniętą {C:green}modyfikację{}", + "{C:inactive,s:0.7}(na przykład ulepszenia, pieczęcie, edycje)", + }, + }, + c_cry_white_hole = { + name = "Biała Dziura", + text = { + "{C:attention}Usuwa{} wszystkie poziomy układów pokerowych,", + "ulepsza {C:legendary,E:1}najczęściej grany{} układ pokerowy", + "o {C:attention}3{} za każdy usunięty poziom", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Różowa Stawka", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Wymagany wynik skaluje się", + "szybciej dla każdego {C:attention}wejścia", + }, + }, + stake_cry_brown = { + name = "Brązowa Stawka", + colour = "Brown", + text = { + "Wszystkie {C:attention}naklejki{} są", + "ze sobą kompatybilne", + }, + }, + stake_cry_yellow = { + name = "Żółta Stawka", + colour = "Yellow", + text = { + "{C:attention}Naklejki{} mogą pojawić się", + "na wszystkich przedmiotach do kupienia", + }, + }, + stake_cry_jade = { + name = "Nefrytowa Stawka", + colour = "Jade", + text = { + "Karty mogą być {C:attention}dobierane w dół{}", + }, + }, + stake_cry_cyan = { + name = "Cyjanowa Stawka", + colour = "Cyan", + text = { + "{C:green}Niepospolite{} i {C:red}rzadkie{} jokery mają", + "mniejsze prawdopodobieństwo występowania", + }, + }, + stake_cry_gray = { + name = "Szara Stawka", + colour = "Gray", + text = { + "Koszt ponownych rzutów wzrasta o {C:attention}$2{} przy każdym użyciu", + }, + }, + stake_cry_crimson = { + name = "Karmazynowa Stawka", + colour = "Crimson", + text = { + "Kupony są odnawiane przy {C:attention}parzystych{} wejściach", + }, + }, + stake_cry_diamond = { + name = "Diamentowa Stawka", + colour = "Diamond", + text = { + "{C:attention}+2{} do wejścia", + }, + }, + stake_cry_amber = { + name = "Bursztynowa Stawka", + colour = "Amber", + text = { + "{C:attention}-1{} slot na paczki wzmacniające", + }, + }, + stake_cry_bronze = { + name = "Miedziowa Stawka", + colour = "Bronze", + text = { + "Kupony są {C:attention}50%{} droższe", + }, + }, + stake_cry_quartz = { + name = "Kwarcowa Stawka", + colour = "Quartz", + text = { + "Jokery mogą być {C:attention}przyszpilone{}", + "{s:0.8,C:inactive}(pozostają na pozycji najbardziej wysuniętej na lewo){}", + }, + }, + stake_cry_ruby = { + name = "Rubinowa Stawka", + colour = "Ruby", + text = { + "{C:attention}Duże w ciemno{} Blinds mogą stać się", + "{C:attention}Przeszkadzajkami Bossa{}", + }, + }, + stake_cry_glass = { + name = "Szklana Stawka", + colour = "Glass", + text = { + "Karty mogą się {C:attention}roztrzaskać{} przy punktacji", + }, + }, + stake_cry_sapphire = { + name = "Szafirowa Stawka", + colour = "Sapphire", + text = { + "Tracisz {C:attention}25%{} obecnych pieniędzy", + "na koniec rundy", + "{s:0.8,C:inactive}(maksymalnie $10){}", + }, + }, + stake_cry_emerald = { + name = "Szmaragdowa Stawka", + colour = "Emerald", + text = { + "Karty, paczki i kupony", + "mogą być {C:attention}dobierane w dół{}", + "{s:0.8,C:inactive}(Nie można ich sprawdzić przed zakupem){}", + }, + }, + stake_cry_platinum = { + name = "Platynowa Stawka", + colour = "Platinum", + text = { + "Małe w ciemno zostają {C:attention}usunięte{}", + }, + }, + stake_cry_twilight = { + name = "Zmierzchowa Stawka", + colour = "Twilight", + text = { + "Karty mogą być {C:attention}bananowe{}", + "{s:0.8,C:inactive}(1 na 10 szans, że zostaną zniszczone w każdej rundzie){}", + }, + }, + stake_cry_verdant = { + name = "Zarośnięta Stawka", + colour = "Verdant", + text = { + "Wymagany wynik skaluje się", + "szybciej dla każdego {C:attention}wejścia", + }, + }, + stake_cry_ember = { + name = "Rozżarzona Stawka", + colour = "Ember", + text = { + "Wszystkie przedmioty mają zerową wartość sprzedaży", + }, + }, + stake_cry_dawn = { + name = "Świtowa Stawka", + colour = "Dawn", + text = { + "Efekty kart Tarota i Ducha są", + "zredukowane do {C:attention}1{} karty", + "{s:0.8,C:inactive}(minimum 1){}", + }, + }, + stake_cry_horizon = { + name = "Nieboskłonna Stawka", + colour = "Horizon", + text = { + "Po wybraniu przeszkadzajki, dodaje", + "{C:attention}losową kartę{} do talii", + }, + }, + stake_cry_blossom = { + name = "Kwitnąca Stawka", + colour = "Blossom", + text = { + "{C:attention}Finałowe{} Przeszkadzajki Bossa mogą", + "pojawić się w {C:attention}dowolnym{} wejściu", + }, + }, + stake_cry_azure = { + name = "Azurowa Stawka", + colour = "Azure", + text = { + "Wartości Jokerów są", + "zmniejszone o {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Wschodząca Stawka", + colour = "Ascendant", + text = { + "{C:attention}-1{} slot w sklepie", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Znacznik astralny", + text = { + "Następny joker podstawowy", + "sklepu jest darmowy", + "i staje się {C:dark_edition}astralny{}", + }, + }, + tag_cry_banana = { + name = "Znacznik bananowy", + text = { + "Tworzy {C:attention}#1#", + "{C:inactive}(wymaga miejsca){}", + }, + }, + tag_cry_bettertop_up = { + name = "Znacznik superulepszenia", + text = { + "Otrzymujesz maks. {C:attention}#1#", + "{C:green}Niepospolitych{} Jokerów", + "{C:inactive}(wymaga miejsca){}", + }, + }, + tag_cry_better_voucher = { + name = "Złoty znacznik kuponu", + text = { + "Podczas następnej wizyty w sklepie", + "pojawia się kupon {C:attention}#1#{} poziomu", + }, + }, + tag_cry_blur = { + name = "Znacznik niewyraźny", + text = { + "Następny joker podstawowy", + "sklepu jest darmowy", + "i staje się {C:dark_edition}niewyraźny{}", + }, + }, + tag_cry_booster = { + name = "Znacznik wzmacniający", + text = { + "Następna {C:cry_code}paczka wzmacniająca{} ma", + "{C:attention}podwójną{} ilość kart i", + "{C:attention}podwójny{} wybór", + }, + }, + tag_cry_bundle = { + name = "Znacznik pakietowy", + text = { + "Daje {C:attention}znacznik standardowy{}, {C:tarot}znacznik czaru{},", + "{C:attention}znacznik pajaca{} i {C:planet}znacznik meteorytu", + }, + }, + tag_cry_cat = { + name = "Znacznik koci", + text = { "Miau.", "{C:inactive}Poziom {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Znacznik konsoli", + text = { + "Daje darmową", + "{C:cry_code}paczkę programową", + }, + }, + tag_cry_double_m = { + name = "Znacznik podwójnego M", + text = { + "W sklepie jest", + "{C:dark_edition}wesoły {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Znacznik władny", + text = { + "Daje darmową {C:spectral}paczkę Ducha", + "z {C:legendary,E:1}Duszą{} i {C:cry_exotic,E:1}Wrotami{}", + }, + }, + tag_cry_epic = { + name = "Znacznik epicki", + text = { + "W sklepie jest {C:cry_epic}epicki joker", + "za pół ceny", + }, + }, + tag_cry_gambler = { + name = "Znacznik hazardzisty", + text = { + "{C:green}#1# na #2#{} szans na", + "utworzenie {C:cry_exotic,E:1}znacznika władczego", + }, + }, + tag_cry_glass = { + name = "Znacznik kruchy", + text = { + "Następny joker podstawowy", + "sklepu jest darmowy", + "i staje się {C:dark_edition}kruchy{}", + }, + }, + tag_cry_glitched = { + name = "Znacznik zglitchowany", + text = { + "Następny joker podstawowy", + "sklepu jest darmowy", + "i staje się {C:dark_edition}zglitchowany{}", + }, + }, + tag_cry_gold = { + name = "Znacznik złoty", + text = { + "Następny joker podstawowy", + "sklepu jest darmowy", + "i staje się {C:dark_edition}złoty{}", + }, + }, + tag_cry_gourmand = { + name = "Znacznik smakosza", + text = { + "W sklepie jest darmowy", + "{C:attention}kulinarny joker", + }, + }, + tag_cry_loss = { + name = "Strata", + text = { + "Daje darmową", + "{C:cry_ascendant}paczkę memiczną", + }, + }, + tag_cry_m = { + name = "Znacznik wesoły", + text = { + "Następny joker podstawowy", + "sklepu jest darmowy", + "i staje się {C:dark_edition}wesoły{}", + }, + }, + tag_cry_memory = { + name = "Znacznik pamięciowy", + text = { + "Daje {C:attention}#1#{} kopie", + "poprzedniego użytego {C:attention}znacznika{}", + "w tym podejściu", + "{s:0.8,C:inactive}Z wyjątkiem znaczników kopiujących", + "{s:0.8,C:inactive}Obecnie: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Znacznik mozaiczny", + text = { + "Następny joker podstawowy", + "sklepu jest darmowy", + "i staje się {C:dark_edition}mozaiczny{}", + }, + }, + tag_cry_oversat = { + name = "Znacznik przesycony", + text = { + "Następny joker podstawowy", + "sklepu jest darmowy", + "i staje się {C:dark_edition}przesycony{}", + }, + }, + tag_cry_quadruple = { + name = "Znacznik poczwórny", + text = { + "Daje {C:attention}#1#{} kopie", + "następnego wybranego {C:attention}znacznika", + "{s:0.8,C:inactive}Z wyjątkiem znaczników kopiujących", + }, + }, + tag_cry_quintuple = { + name = "Znacznik popiątny", + text = { + "Daje {C:attention}#1#{} kopie", + "następnego wybranego {C:attention}znacznika", + "{s:0.8,C:inactive}Z wyjątkiem znaczników kopiujących", + }, + }, + tag_cry_rework = { + name = "Znacznik przeróbki", + text = { + "W sklepie jest", + "{C:cry_code}przerobiony joker", + }, + }, + tag_cry_schematic = { + name = "Znacznik schematyczny", + text = { + "W sklepie jest", + "{C:attention}Burza Mózgów", + }, + }, + tag_cry_scope = { + name = "Znacznik zakresowy", + text = { + "{C:attention}+#1# {C:blue}do ręki{} i", + "{C:red}zrzutek{} w następnej rundzie", + }, + }, + tag_cry_triple = { + name = "Znacznik potrójny", + text = { + "Daje {C:attention}#1#{} kopie", + "następnego wybranego {C:attention}znacznika", + "{s:0.8,C:inactive}Z wyjątkiem znaczników kopiujących", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "Automatyzacja", + text = { + "Otrzymujesz {C:attention}#1#", + "losową kartę {C:cry_code}Kodową{}", + "{C:inactive}(wymaga miejsca)", + }, + }, + c_cry_eclipse = { + name = "Zaćmienie", + text = { + "Ulepsza {C:attention}#1#{} wybraną kartę", + "do: {C:attention}Karta Echa", + }, + }, + c_cry_meld = { + name = "Wtopienie", + text = { + "Wybierz {C:attention}jokera{} lub", + "{C:attention}kartę rozgrywającą{}, aby", + "stała się {C:dark_edition}Dwustronna", + }, + }, + c_cry_theblessing = { + name = "Błogosławieństwo", + text = { + "Otrzymujesz {C:attention}1{}", + "losowy {C:attention}przedmiot zużywalny{}", + "{C:inactive}(wymaga miejsca){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglif", + text = { + "Ustawia wejście na {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Puste płótno", + text = { + "{C:attention}+#1#{} do rozmiaru ręki", + }, + }, + v_cry_clone_machine = { + name = "Maszyna klonująca", + text = { + "Znaczniki podwójne stają się", + "{C:attention}Znacznikami popiątnymi{} i", + "pojawiają się {C:attention}4X{} częściej", + }, + }, + v_cry_command_prompt = { + name = "Wiersz poleceń", + text = { + "{C:cry_code}Karty Kodowe{}", + "można kupić", + "w {C:attention}sklepie{}", + }, + }, + v_cry_copies = { + name = "Kopie", + text = { + "Znaczniki podwójne stają się", + "{C:attention}Znacznikami potrójnymi{} i", + "pojawiają się {C:attention}2X{} częściej", + }, + }, + v_cry_curate = { + name = "Kunszt", + text = { + "Wszystkie karty", + "pojawiają się z", + "{C:dark_edition}edycją{}", + }, + }, + v_cry_dexterity = { + name = "Zręczność", + text = { + "Zyskujesz na stałe", + "{C:blue}+#1#{} do ręki", + "na rundę", + }, + }, + v_cry_double_down = { + name = "Podwojenie stawki", + text = { + "Po każdej rundzie,", + "{X:dark_edition,C:white} X1.5 {} do wszystkich wartości", + "tylnej części", + "{C:dark_edition}Kart Dwustronnych{}" + }, + }, + v_cry_double_slit = { + name = "Podwójna szczelina", + text = { + "{C:attention}Wtopienie{} mogą", + "pojawić się w sklepie i", + "{C:tarot}paczkach wiedzy tajemnej{}", + }, + }, + v_cry_double_vision = { + name = "Podwójne widzenie", + text = { + "{C:dark_edition}Karty Dwustronne{} pojawiają się", + "{C:attention}4X{} razy częściej", + }, + }, + v_cry_fabric = { + name = "Uniwersalna tkanina", + text = { + "{C:dark_edition}+#1#{} sloty na jokera", + }, + }, + v_cry_massproduct = { + name = "Masowa produkcja", + text = { + "Wszystkie karty i paczki", + "w sklepie kosztują {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Pieniężna Fasola", + text = { + "Zwiększa limit", + "zdobywanych odstetek", + "w każdej rundzie do {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Nadmiar zapasów++", + text = { + "{C:attention}+#1#{} slot na kartę", + "{C:attention}+#1#{} slot na paczkę wzmacniającą", + "w sklepie", + }, + }, + v_cry_pacclimator = { + name = "Planetarny aklimator", + text = { + "Karty {C:planet}Planet{} pojawiają się", + "{C:attention}X#1#{} częściej", + "w sklepie", + "Wszystkie następne Karty {C:planet}Planet{}", + "stają się {C:green}darmowe{}", + }, + }, + v_cry_pairamount_plus = { + name = "Paramount Plus", + text = { + "{C:attention}Aktywuje ponownie{} wszystkie M Jokery", + "za każdą Parę", + "{C:attention}zawartą{} w zagranej ręce", + }, + }, + v_cry_pairing = { + name = "Parowanie", + text = { + "{C:attention}Aktywuj ponownie{} wszystkie M Jokery", + "jeśli zagrana ręka ma {C:attention}Parę", + }, + }, + v_cry_quantum_computing = { + name = "Obliczenia kwantowe", + text = { + "{C:cry_code}Karty Kodowe{} mogą pojawić się w sklepie", + "z {C:dark_edition}negatywną{} edycją", + }, + }, + v_cry_repair_man = { + name = "Pan od par", + text = { + "{C:attention}Aktywuj ponownie{} wszystkie M Jokery", + "jeśli zagrana ręka zawiera {C:attention}Parę", + }, + }, + v_cry_rerollexchange = { + name = "Zmiana losu", + text = { + "Ponowne rzuty", + "kosztują {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satelitarny łącznik", + text = { + "{C:cry_code}Karty Kodowe{} mogą", + "pojawić się w każdej", + "{C:attention}paczce niebiańskiej{}", + }, + }, + v_cry_scope = { + name = "Zakres galaktyczny", + text = { + "Tworzy kartę {C:planet}Planety", + "za zagrany", + "{C:attention}układ pokerowy{}", + "{C:inactive}(wymaga miejsca){}", + }, + }, + v_cry_tacclimator = { + name = "Tarotowy aklimator", + text = { + "Karty {C:tarot}Tarota{} pojawiają się", + "{C:attention}X#1#{} razy częściej", + "w sklepie", + "Wszystkie następne Karty {C:tarot}Tarota{}", + "stają się {C:green}darmowe{}", + }, + }, + v_cry_tag_printer = { + name = "Drukarka znaczników", + text = { + "Znaczniki podwójne stają się", + "{C:attention}Znacznikami poczwórnymi{} i", + "pojawiają się {C:attention}3X{} częściej", + }, + }, + v_cry_threers = { + name = "3 x R", + text = { + "Na stałe", + "zyskujesz {C:red}+#1#{} do zrzutek", + "w każdej rundzie", + }, + }, + v_cry_stickyhand = { + name = "Lepka dłoń", + text = { + "{C:attention}+#1#{} do limitu wyboru kart", + }, + }, + v_cry_grapplinghook = { + name = "Hak chwytający", + text = { + "{C:attention}+#1#{} do limitu wyboru kart", + "{C:inactive,s:0.7}Możesz zrobić o wiele więcej, niż myślisz.{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hiperprzestrzenny łańcuch", + text = { + "{C:attention}+#1#{} do limitu wyboru kart", + "{C:inactive,s:0.7}UWAGA: W przyszłości będzie mieć dodatkowe funkcje{}", + }, + }, + }, + Other = { + banana = { + name = "Bananowy", + text = { + "{C:green}#1# na #2#{} szans, że ta karta", + "zostanie zniszczona na końcu rundy", + }, + }, + cry_rigged = { + name = "Gwarantowany", + text = { + "Wszystkie {C:cry_code}wymienione{} prawdopodobieństwa", + "są {C:cry_code}zagwarantowane", + }, + }, + cry_hooked = { + name = "Zahaczony", + text = { + "Kiedy ten joker jest {C:cry_code}aktywowany{},", + "aktywuje {C:cry_code}#1#", + }, + }, + cry_flickering = { + name = "Migoczący", + text = { + "Ulega samozniszczeniu", + "po {C:attention}#1#{} aktywacjach", + "{C:inactive}({C:attention}#2#{C:inactive} pozostało)" + }, + }, + cry_flickering_desc = { --used by choco dice + name = "Migoczący", + text = { + "Ulega samozniszczeniu po", + "{C:attention}#1#{} aktywacjach", + }, + }, + cry_possessed = { + name = "Opętany", + text = { + "{C:attention}Wyłącza{} i {C:attention}odwraca{}", + "efekty, jeśli to możliwe", + "Ulega zniszczeniu razem z {C:attention}Duchem" + }, + }, + --todo? add candy jokers to list + food_jokers = { + name = "Kulinarne jokery", + text = { + "{s:0.8}Gros Michel, Jajo, Lody, Cavendish,", + "{s:0.8}Czarna Fasola, Dietetyczna Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Ogór, Papryczka Chilli, Karmel,", + "{s:0.8}Nostalgiczny Cukierek, Fast Foodowe M, itd.", + }, + }, + ev_cry_choco0 = { + name = "", + text = { + "Szczegóły aktywnego", + "{C:cry_ascendant,E:1}wydarzenia{} pojawią się tutaj" + } + }, + ev_cry_choco1 = { + name = "1: Opętanie", + text = { + "{C:attention}Jokery{} i karty rozgrywające mają", + "{C:green}1 na 3{} szans na Migotanie", + "Tworzy {C:attention}Ducha", + "{C:inactive,s:0.7}Zostałeś opętany przez Ducha, a twoja", + "{C:inactive,s:0.7}świadomość migocze w tę i z powrotem." + } + }, + ev_cry_choco2 = { + name = "2: Nawiedzony Dom", + text = { + "Uniemożliwia pomijanie {C:attention}Przeszkadzajki{}", + "Jeden {C:attention}ponowny rzut{} na sklep", + "Ceny {C:attention}kuponów{} są podwojone", + "{C:inactive,s:0.7}Upiorne duchy przejęły kontrolę! Niczego nie dotykaj", + "{C:inactive,s:0.7}i uciekaj stąd jak najszybciej!", + } + }, + ev_cry_choco3 = { + name = "3: Napary Czarownnic", + text = { + "Tworzy 3 {C:attention}mikstury", + "Wypij jedną z nich podczas {C:attention}małej w ciemno{},", + "albo {C:attention}wszystkie{} negatywne efekty zostaną zastosowane w tym {C:attention}wejściu", + "{C:inactive,s:0.7}Zostałeś porwany przez wiedźmę!", + "{C:inactive,s:0.7}Oferuje ci trzy mikstury, obserwując cię bardzo uważnie.", + "{C:inactive,s:0.7}Wybierz jedną, zanim ona podejmie decyzję za ciebie", + } + }, + ev_cry_choco4 = { + name = "4: Księżycowa Otchłań", + text = { + "Zagrane karty mają {C:green}1 na 4{} szans", + "na zmianę w losową figurę karcianą o randze {C:club}Trefl{}", + "{C:attention}Mnożnik{} dzieli się przez ilość zagranych figur karcianych", + "{C:inactive,s:0.7}Nawet człowiek o czystym sercu,", + "{C:inactive,s:0.7}modlący się co noc..." + } + }, + ev_cry_choco5 = { + name = "5: Krwiopijca", + text = { + "Usuwa {C:attention}ulepszenia{} ze wszystkich zagranych kart", + "{C:green}1 na 3{} szans na zniszczenie", + "{C:heart}Kierów{} i {C:diamond}Pików{}", + "{C:inactive,s:0.7}Bądź czujny w środku nocy wobec", + "{C:inactive,s:0.7,E:1}tych w cieniu{C:inactive,s:0.7}, czekających na zaspokojenie ich pragnień..." + } + }, + ev_cry_choco6 = { + name = "6: Proszę, weź jednego", + text = { + "{C:attention}Na koniec rundy{}, otwierasz", + "losową {C:attention}paczkę wzmacniającą{}", + "{C:inactive,s:0.7}Spacerując po ulicach, twoim oczom rzuca się", + "{C:inactive,s:0.7}skrzynia pełna paczek wzmacniających. Masz, poczęstuj się!" + } + }, + ev_cry_choco7 = { + name = "7: Świąteczna Atmosfera", + text = { + "Tworzy 3 kopie karty {C:attention}Cukierek albo Psikus{} 1 {C:attention}Kosz Słodyczy", + "W sklepie co rundę pojawia się {C:attention}Cukierek albo Psikus{}", + "{C:cry_candy}Słodycze{} dają {C:money}$3{} po nabyciu", + "{C:inactive,s:0.7}Okolica jest przystrojona wszelkimi upiornościami,", + "{C:inactive,s:0.7}przyjdź i poczuj świąteczną atmosferę!" + } + }, + ev_cry_choco8 = { + name = "8: Cukierkowy Deszcz", + text = { + "Kiedy {C:attention}Przeszkadzajka{} jest pokonana, zyskujesz 1 {C:cry_candy}Cukierek{}", + "za każdą pozostałą rękę. Dostajesz {C:attention}kulinarnego jokera,{}", + "kiedy {C:cry_candy}Cukierek{} zostaje wytworzony", + "{C:inactive,s:0.7}Z nieba spada deszcz cukierków!", + "{C:inactive,s:0.7,E:1}Szybko, weź ile potrafisz!" + } + }, + ev_cry_choco9 = { + name = "9: Upiorne Bogactwa", + text = { + "Zyskujesz {C:money}$20", + "Wszystkie zdobyte {C:money}pieniądze{} są {C:attention}podwojone", + "{C:inactive,s:0.7}Widmo dawno zmarłego krewnego", + "{C:inactive,s:0.7}odwiedza cię w środku nocy!", + "{C:inactive,s:0.7}Bez słowa, kładzie worek pieniędzy w twe dłonie,", + "{C:inactive,s:0.7}uśmiecha się ciepło i rozpływa się w powietrzu.", + } + }, + ev_cry_choco10 = { + name = "10: Szanowany Antyk", + text = { + "{C:legendary}Legendary{} {C:attention}joker{} pojawia się", + "w slocie na {C:attention}Kupony{} za {C:money}$50", + "Da się go kupić tylko jako {C:attention}ostatni{} przedmiot sklepu", + "{C:inactive,s:0.7}Przyciągnąłeś uwagę ducha relikwii,", + "{C:inactive,s:0.7}lecz stłumienie go nie będzie łatwe.", + } + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Wieczny", + text = { + "Wszystkie karty w paczce", + "są {C:attention}Wieczne{}", + }, + }, + cry_perishable_booster = { + name = "Nietrwały", + text = { + "Wszystkie karty w paczce", + "są {C:attention}Nietrwałe{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "Wszystkie karty w paczce", + "są {C:attention}Wypożyczone{}", + }, + }, + cry_pinned_booster = { + name = "Przyszpilony", + text = { + "Wszystkie karty w paczce", + "są {C:attention}Przyszpilone{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "Wszystkie karty w paczce", + "są {C:attention}Bananowe{}", + }, + }, + cry_eternal_voucher = { + name = "Wieczny", + text = { + "Nie może być wymieniony", + }, + }, + cry_perishable_voucher = { + name = "Nietrwały", + text = { + "Osłabienie po", + "{C:attention}#1#{} rund", + "{C:inactive}(pozostało: {C:attention}#2#{C:inactive})", + }, + }, + cry_rental_voucher = { + name = "Wypożyczony", + text = { + "Tracisz {C:money}#1#${} na", + "końcu rundy", + }, + }, + cry_pinned_voucher = { + name = "Przyszpilony", + text = { + "Zostaje w sklepie", + "do momentu wykupienia", + }, + }, + cry_banana_voucher = { + name = "Bananowy", + text = { + "{C:green}#1# na #2#{} szans", + "na zniknięcie pod koniec rundy", + }, + }, + cry_perishable_consumeable = { + name = "Nietrwały", + text = { + "Osłabiony na", + "końcu rundy", + }, + }, + cry_rental_consumeable = { + name = "Wypożyczony", + text = { + "Tracisz {C:money}$#1#{} na koniec", + "rundy i po użyciu", + }, + }, + cry_pinned_consumeable = { + name = "Przyszpilony", + text = { + "Nie można użyć innych", + "nie-{C:attention}Przyszpilonych{} przedmiotów zużywalnych", + }, + }, + cry_banana_consumeable = { + name = "Bananowy", + text = { + "{C:green}#1# na #2#{} na zrobienie", + "niczego", + }, + }, + p_cry_code_normal_1 = { + name = "Paczka programowa", + text = { + "Wybierz {C:attention}#1#{} z", + "{C:attention}#2#{} kart {C:cry_code}Kodowych{}", + }, + }, + p_cry_code_normal_2 = { + name = "Paczka programowa", + text = { + "Wybierz {C:attention}#1#{} z", + "{C:attention}#2#{} kart {C:cry_code}Kodowych{}", + }, + }, + p_cry_code_jumbo_1 = { + name = "Olbrzymia paczka programowa", + text = { + "Wybierz {C:attention}#1#{} z", + "{C:attention}#2#{} kart {C:cry_code}Kodowych{}", + }, + }, + p_cry_code_mega_1 = { + name = "Megapaczka programowa", + text = { + "Wybierz {C:attention}#1#{} z", + "{C:attention}#2#{} kart {C:cry_code}Kodowych{}", + }, + }, + p_cry_empowered = { + name = "Paczka kart Ducha [Znacznik władny]", + text = { + "Wybierz {C:attention}#1#{} z", + "{C:attention}#2#{} kart {C:spectral}Ducha{} ", + "{s:0.8,C:inactive}(stworzona przez znacznik władczy)", + }, + }, + p_cry_meme_1 = { + name = "Paczka memiczna", + text = { + "Wybierz {C:attention}#1#{} z", + "{C:attention}#2# memicznych jokerów{}", + }, + }, + p_cry_meme_two = { + name = "Paczka memiczna", + text = { + "Wybierz {C:attention}#1#{} z", + "{C:attention}#2# memicznych jokerów{}", + }, + }, + p_cry_meme_three = { + name = "Paczka memiczna", + text = { + "Wybierz {C:attention}#1#{} z", + "{C:attention}#2# memicznych jokerów{}", + }, + }, + undiscovered_code = { + name = "Nie odkryto", + text = { + "Kup albo użyj", + "tej karty w", + "nierozstawionym podejściu,", + "by dowiedzieć się, jaki jest jej efekt" + } + }, + undiscovered_unique = { + name = "Nie odkryto", + text = { + "Kup albo użyj", + "tej karty w", + "nierozstawionym podejściu,", + "by dowiedzieć się, jaki jest jej efekt" + } + }, + cry_green_seal = { + name = "Zielona Pieczęć", + text = { + "Tworzy {C:cry_code}Kartę Kodową{}", + "kiedy zagrana karta nie zdobywa punktów", + "{C:inactive}(wymaga miejsca)", + }, + }, + cry_azure_seal = { + name = "Azurowa Pieczęć", + text = { + "Tworzy {C:attention}#1#{} {C:dark_edition}Negatywy{}", + "{C:planet}Kart Planet{} za zagrany", + "{C:attention}układ pokerowy{}, a następnie", + "{C:red}niszczy{} kartę", + }, + }, + blurred_sdm0 = { + name = "a", + text = { + "{C:inactive,s:0.8}\"Nienawidzę tej karty\" - SDM0, 2024{}", + }, + }, + }, + Unique = { + c_cry_potion = { + name = "Mikstura", + text = { + "Aplikuje nieznany", + "{C:attention}negatywny efekt{} kiedy spożyta", + "{C:inactive,s:0.7}Zdobyta poprzez Czekoladową Kostkę" + } + } + } + }, + misc = { + poker_hands = { + ['cry_Bulwark'] = "Bastion", + ['cry_Clusterfuck'] = "Totalny Rozpierdol", + ['cry_UltPair'] = "Para Ostateczna", + ['cry_WholeDeck'] = "Cała Pierdolona Talia", + }, + poker_hand_descriptions = { + ['cry_Bulwark'] = { + '5 kart bez rangi i koloru', + }, + ['cry_Clusterfuck'] = { + 'Co najmniej 8 kart, które', + 'nie zawierają Pary, Koloru, lub Strita', + }, + ['cry_UltPair'] = { + 'Two Two Pairs, where each', + 'Two Pair is a single suit, for a', + 'total of two suits between them', + }, + ['cry_WholeDeck'] = { + 'Ręka, która zawiera każdą', + 'kartę w 52-kartowej talii.', + 'Zwariowałeś?', + }, + }, + achievement_names = { + ach_cry_ace_in_crash = "Kieszonkowy As", + ach_cry_blurred_blurred_joker = "Prawie Niewidomy", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Przełam Nieskończoność", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Dom Wariatów", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Agent Nieruchomości", + ach_cry_jokes_on_you = "Wystrychnięty na Dudka", + ach_cry_niw_uoy = "!owtsęicywZ", + ach_cry_now_the_fun_begins = "Czas na Prawdziwą Zabawę", + ach_cry_patience_virtue = "Cierpliwość Jest Cnotą", + ach_cry_perfectly_balanced = "Idealnie Zbalansowane", + ach_cry_pull_request = "Prośba o Połączenie", + ach_cry_traffic_jam = "Korek Uliczny", + ach_cry_ult_full_skip = "Absolutny Przeskok", + ach_cry_used_crash = "Ostrzegaliśmy Cię", + ach_cry_what_have_you_done = "CO NAROBIŁEŚ!?", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Zdobądź niewyraźnego Niewyraźnego Jokera", + ach_cry_bullet_hell = "Posiadaj 15 AP Jokerów", + ach_cry_break_infinity = "Zdobądź 1.79e308 żetonów z pojedyńczej ręki", + ach_cry_cryptid_the_cryptid = "Użyj Kryptydy na Kryptydzie", + ach_cry_exodia = "Posiadaj 5 egzotycznych jokerów", + ach_cry_freak_house = "Zagraj Garnitur Koloru z 6 i 9 karo posiadając Najs", + ach_cry_googol_play_pass = "Zhakuj Kartę Googol Play", + ach_cry_haxxor = "Wpisz kod do gry", + ach_cry_home_realtor = "Aktywuj Szczęśliwy Dom przed 8 wejściem (bez Talii Równowagi/Antymateryjnej)", + ach_cry_jokes_on_you = "Aktywuj efekt Dowcipu w pierwszym wejściu i wygraj podejście", + ach_cry_niw_uoy = "Osiągnij stawkę -8", + ach_cry_now_the_fun_begins = "Zdobądź Płótno", + ach_cry_patience_virtue = "Podczas Lawendowej Pętli, przeczekaj 2 minuty zanim zagrasz pierwszą rękę i pokonaj przeszkadzajkę", + ach_cry_perfectly_balanced = "Pokonaj Bardzo Uczciwą Talię na poziomie trudności Dominująca stawka", + ach_cry_pull_request = "Spraw, aby ://COMMIT stworzył tego samego jokera, którego zniszczył", + ach_cry_traffic_jam = "Pokonaj wszystkie wyzwania Godzin Szczytu", + ach_cry_ult_full_skip = "Wygraj w 1 rundzie", + ach_cry_used_crash = "Użyj ://CRASH", + ach_cry_what_have_you_done = "Sprzedaj lub poświęć egzotycznego jokera", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Wojna na Sztylety", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Pojedyńcza Karta", + c_cry_rng = "RNG", + c_cry_rush_hour = "Godzina Szczytu I", + c_cry_rush_hour_ii = "Godzina Szczytu II", + c_cry_rush_hour_iii = "Godzina Szczytu III", + c_cry_sticker_sheet = "Arkusz z Naklejkami", + c_cry_sticker_sheet_plus = "Arkusz z Naklejkami+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Funkcje", + cry_set_music = "Muzyka", + cry_set_enable_features = "Wybierz funkcje do włączenia (zastosują się po ponownym uruchomieniu gry):", + cry_feat_achievements = "Wyzwania", + ["cry_feat_antimatter deck"] = "Talia Antymateryjna", + cry_feat_blinds = "Przeszkadzajki", + cry_feat_challenges = "Wyzwania", + ["cry_feat_code cards"] = "Karty Kodowe", + ["cry_feat_misc. decks"] = "Różnorodne Talie", + ["cry_feat_https module"] = "Moduł HTTPS", + ["cry_feat_timer mechanics"] = "Mechanizm czasomierza", + ["cry_feat_enhanced decks"] = "Ulepszone Talie", + ["cry_feat_epic jokers"] = "Epickie Jokery", + ["cry_feat_exotic jokers"] = "Egzotyczne Jokery", + ["cry_feat_m jokers"] = "M Jokery", + cry_feat_menu = "Niestandardowe Menu Główne", + ["cry_feat_misc."] = "Różnorodne Jokery", + ["cry_feat_misc. jokers"] = "Inne Jokery", + cry_feat_planets = "Karty Planet", + cry_feat_jokerdisplay = "JokerDisplay (Nie robi nic)", + cry_feat_tags = "Znaczniki", + cry_feat_sleeves = "Pokrowce", + cry_feat_spectrals = "Karty Ducha", + cry_feat_spooky = "Upiorna Aktualizacja", + ["cry_feat_more stakes"] = "Stawki", + cry_feat_vouchers = "Kupony", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Chroniony prawem autorskim)", + cry_mus_code = "Karty Kodowe (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Egzotyczne Jokery (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "Wysoki Wynik (Finałowy Boss [Dla Twojego Komputera] by AlexZGreat)", + + k_cry_program_pack = "Paczka Programowa", + k_cry_meme_pack = "Paczka Memiczna", + + cry_critical_hit_ex = "Trafienie Krytyczne!", + cry_critical_miss_ex = "Krytyczne chybienie!", + + cry_potion1 = "-1 do wszystkich układów pokerowych", + cry_potion2 = "X1.15 rozmiaru Przeszkadzajki", + cry_potion3 = "-1 Ręka i zrzutka", + + cry_debuff_oldhouse = "Full House nie może być zagrany", + cry_debuff_oldarm = "Należy zagrać 4 albo mniej kart", + cry_debuff_oldpillar = " Strit nie może być zagrany", + cry_debuff_oldflint = "Kolor nie może być zagrany", + cry_debuff_oldmark = "Zagranie nie może posiadać Pary", + cry_debuff_obsidian_orb = "Aplikuje zdolności wszystkich pokonanych bossów", + + k_code = "Kod", + k_unique = "Unikalny", + b_code_cards = "Karty Kodowe", + b_unique_cards = "Unikalne Karty", + b_pull = "PULL", + cry_hooked_ex = "Zahaczone!", + k_end_blind = "Koniec przeszkadzajki", + + cry_code_rank = "WPROWADŹ RANGĘ", + cry_code_enh = "WPROWADŹ ULEPSZENIE", + cry_code_hand = "WPROWADŹ UKŁAD POKEROWY", + cry_code_enter_card = "WPROWADŹ KARTĘ", + cry_code_apply = "ZASTOSUJ", + cry_code_apply_previous = "ZASTOSUJ POPRZEDNI", + cry_code_exploit = "WYKORZYSTAJ", + cry_code_exploit_previous = "WYKORZYSTAJ POPRZEDNI", + cry_code_create = "STWÓRZ", + cry_code_create_previous = "STWÓRZ POPRZEDNI", + cry_code_execute = "WYKONAJ", + cry_code_cancel = "ANULUJ", + + b_flip = "ODWRÓĆ", + b_merge = "SCAL", + + cry_hand_bulwark = "Bastion", + cry_hand_clusterfuck = "Totalny Rozpierdol", + cry_hand_ultpair = "Para Ostateczna", + + cry_again_q = "Znowu?", + cry_curse = "Klątwa", + cry_curse_ex = "Klątwa!", + cry_sobbing = "Pomóż mi...", + cry_gaming = "Granie", + cry_gaming_ex = "Granie!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Uśmiechnij się!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 runda", + cry_plus_cryptid = "+1 Kryptyda", + cry_no_triggers = "Koniec aktywacji!", + cry_unredeemed = "Nieodkupiony...", + cry_active = "Aktywny", + cry_inactive = "Nieaktywny", + + k_disable_music = "Wyłącz muzykę", + + k_cry_epic = "Epicki", + k_cry_exotic = "Egzotyczny", + k_cry_candy = "Cukierek", + k_cry_cursed = "Przeklęty", + k_planet_disc = "Dysk Protoplanetarny", + k_planet_satellite = "Naturalne Satelity", + k_planet_universe = "Cały Pierdolony Wszechświat", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Informacja o prawach autorskich", + cry_notif_jimball_d1 = "Jimball gra utwór \"Funkytown\",", + cry_notif_jimball_d2 = "który jest objęty prawami autorskimi i nie może być", + cry_notif_jimball_d3 = "używany do streamów i filmów.", + }, + labels = { + food_jokers = "Kulinarne jokery", + banana = "Bananowy", + code = "Kodowy", + unique = "Unikalny", + cry_rigged = "Gwarantowany", + cry_hooked = "Zahaczony", + cry_flickering = "Migoczący", + cry_possessed = "Opętany", + + cry_green_seal = "Zielona Pieczęć", + cry_azure_seal = "Azurowa Pieczęć", + + cry_astral = "Astralna", + cry_blur = "Niewyraźna", + cry_double_sided = "Dwustronna", + cry_glass = "Krucha", + cry_glitched = "Zgliczowana", + cry_gold = "Złota", + cry_m = "Wesoła", + cry_mosaic = "Mozaiczna", + cry_noisy = "Hałaśliwa", + cry_oversat = "Przesycona", + + k_cry_epic = "Epicki", + k_cry_exotic = "Egzotyczny", + k_cry_candy = "Cukierek", + k_cry_cursed = "Przeklęty", + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_candy = {"+#1# Candy"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + + cry_art = {"Art: #1#"}, + cry_code = {"Code: #1#"}, + cry_idea = {"Idea: #1#"} + }, + v_text = { + ch_c_cry_all_perishable = {"Wszystkie jokery są {C:eternal}nietrwałe{}"}, + ch_c_cry_all_rental = {"Wszystkie jokery są {C:eternal}wypożyczone{}"}, + ch_c_cry_all_pinned = {"Wszystkie jokery są {C:eternal}przyszpilone{}"}, + ch_c_cry_all_banana = {"Wszystkie jokery są {C:eternal}bananowe{}"}, + ch_c_all_rnj = {"Wszystkie jokery to {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"Wszystkie karty dostępne w sklepie mają wszystkie naklejki"}, + ch_c_cry_rush_hour = {"Wszystkie bossy to {C:attention}Zegar{} lub {C:attention}Lawendowa Pętla"}, + ch_c_cry_rush_hour_ii = {"Wszystkie przeszkadzajki są {C:attention}przeszkadzajkami bossa{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}Zegar{} i {C:attention}Lawendowa Pętla{} skalują się {C:attention}dwa{} razy szybciej"}, + ch_c_cry_no_tags = {"Pomijanie jest {C:attention}zablokowane{}"}, + ch_c_cry_no_vouchers = {"{C:attention}Kupony{} nie pojawiają się w sklepie"}, + ch_c_cry_no_boosters = {"{C:attention}Paczki wzmacniające{} nie pojawiają się w sklepie"}, + ch_c_cry_no_rerolls = {"Ponowne rzuty są {C:attention}zablokowane{}"}, + ch_c_cry_no_consumables = {"{C:attention}Przedmioty zużywalne{} nie pojawiają się"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/pt_BR.lua b/Cryptid/localization/pt_BR.lua new file mode 100644 index 0000000..65b3b86 --- /dev/null +++ b/Cryptid/localization/pt_BR.lua @@ -0,0 +1,3314 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antimatter Deck", + text = { + "Applies the {C:legendary,E:1}upsides{}", + "of {C:attention}every{} deck", + }, + }, + b_cry_beta = { + name = "Nostalgic Deck", + text = { + "{C:attention}Joker{} and {C:attention}Consumable{}", + "slots are {C:attention}combined", + "{C:attention}Nostalgic{} Blinds replace", + "their updated Blind" + }, + }, + b_cry_blank = { + name = "Blank Deck", + text = { + "{C:inactive,E:1}Does nothing?", + }, + }, + b_cry_CCD = { + name = "CCD Deck", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + b_cry_conveyor = { + name = "Conveyor Deck", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + b_cry_critical = { + name = "Critical Deck", + text = { + "After each hand played,", + "{C:green}#1# in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}#1# in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + b_cry_encoded = { + name = "Encoded Deck", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + b_cry_equilibrium = { + name = "Deck of Equilibrium", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}Overstock Plus", + }, + }, + b_cry_glowing = { + name = "Glowing Deck", + text = { + "Multiply the values of", + "all Jokers by {X:dark_edition,C:white} X1.25 {}", + "when Boss Blind is defeated", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Infinite Deck", + text = { + "You can select {C:attention}any", + "number of cards", + "{C:attention}+1{} hand size", + }, + }, + b_cry_misprint = { + name = "Misprint Deck", + text = { + "Values of cards", + "and poker hands", + "are {C:attention}randomized", + }, + }, + b_cry_redeemed = { + name = "Redeemed Deck", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + b_cry_very_fair = { + name = "Very Fair Deck", + text = { + "{C:blue}-2{} hands, {C:red}-2{} discards", + "every round", + "{C:attention}Vouchers{} no longer", + "appear in the shop", + }, + }, + b_cry_wormhole = { + name = "Wormhole Deck", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + b_cry_legendary = { + name = "Legendary Deck", + text = { + "Start with an {C:legendary}Legendary{C:legendary} Joker", + "{C:green}1 in 5{} chance to create another", + "when Boss Blind is defeated {C:inactive}(must have room){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "The Box", + text = { + "All Common Jokers", + "are debuffed", + }, + }, + bl_cry_clock = { + name = "The Clock", + text = { + "+0.1X blind requirements every", + "3 seconds spent this ante", + }, + }, + bl_cry_hammer = { + name = "The Hammer", + text = { + "All cards with odd", + "rank are debuffed", + }, + }, + bl_cry_joke = { + name = "The Joke", + text = { + "If score is >2X requirements,", + "set ante to multiple of #1#", + }, + }, + bl_cry_magic = { + name = "The Magic", + text = { + "All cards with even", + "rank are debuffed", + }, + }, + bl_cry_lavender_loop = { + name = "Lavender Loop", + text = { + "1.25X blind requirements every", + "1.5 seconds spent this round", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidian Orb", + text = { + "Applies abilities of", + "all defeated bosses", + }, + }, + bl_cry_oldarm = { + name = "Nostalgic Arm", + text = { + "Must play 4", + "or fewer cards", + }, + }, + bl_cry_oldfish = { + name = "Nostalgic Fish", + text = { + "All hands start", + "with 1 Mult", + }, + }, + bl_cry_oldflint = { + name = "Nostalgic Flint", + text = { + "No Flushes", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgic House", + text = { + "No Full Houses", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgic Manacle", + text = { + "Divide Mult by discards", + }, + }, + bl_cry_oldmark = { + name = "Nostalgic Mark", + text = { + "No hands that", + "contain a Pair", + }, + }, + bl_cry_oldox = { + name = "Nostalgic Ox", + text = { + "All hands start", + "with 0 Chips", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgic Pillar", + text = { + "No Straights", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgic Serpent", + text = { + "Divide Mult by level", + "of played poker hand", + }, + }, + bl_cry_pin = { + name = "The Pin", + text = { + "Jokers with Epic or higher", + "rarity are debuffed", + }, + }, + bl_cry_pinkbow = { + name = "Pink Bow", + text = { + "Randomize rank of cards", + "held in hand on play", + }, + }, + bl_cry_sapphire_stamp = { + name = "Sapphire Stamp", + text = { + "Select an extra card, deselect", + "random card before scoring", + }, + }, + bl_cry_shackle = { + name = "The Shackle", + text = { + "All Negative Jokers", + "are debuffed", + }, + }, + bl_cry_striker = { + name = "The Striker", + text = { + "All Rare Jokers", + "are debuffed", + }, + }, + bl_cry_tax = { + name = "The Tax", + text = { + "Score per hand capped at", + "0.4X blind requirements", + }, + }, + bl_cry_tornado = { + name = "Turquoise Tornado", + text = { + "#1# in #2# chance for", + "played hand to not score", + }, + }, + bl_cry_trick = { + name = "The Trick", + text = { + "After each hand, flip all", + "face-up cards held in hand", + }, + }, + bl_cry_vermillion_virus = { + name = "Vermillion Virus", + text = { + "One random Joker", + "replaced every hand", + }, + }, + bl_cry_windmill = { + name = "The Windmill", + text = { + "All Uncommon Jokers", + "are debuffed", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Convert {C:cry_code}#1#{} selected card", + "to a {C:cry_code}chosen{} enhancement", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}new{} Joker", + "of the {C:cry_code}same rarity", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Don't.", + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Permanently{} remove a", + "{C:cry_code}selected{} shop item", + "{C:inactive,s:0.8}Item cannot appear again this run", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "{C:cry_code}Halve{} all listed prices", + "in current shop", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "The {C:cry_code}next{} hand played", + "is calculated as a", + "{C:cry_code}chosen{} poker hand", + "{C:inactive,s:0.8}Secret hands must be", + "{C:inactive,s:0.8}discovered to be valid", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Select two Jokers", + "to become {C:cry_code}Hooked", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Add {C:dark_edition}Glitched{} to all", "cards {C:cry_code}held in hand" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Merge a selected {C:cry_code}consumable", + "with a selected {C:cry_code}playing card", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Double{} all values of", + "a selected {C:cry_code}Joker{} until", + "end of round", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Next defeated Blind", + "gives {C:cry_code}X#1#{} interest", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:cry_code}#1#{} extra card and", + "{C:cry_code}#1#{} extra choice", + "{C:inactive}(Currently {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Replenish {C:blue}Hands{} and {C:red}Discards{},", + "return {C:cry_code}all{} cards to deck", + "and draw a {C:cry_code}new{} hand", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Set {C:cry_code}game state{} to", + "start of {C:cry_code}this Ante{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}Rework Tag{} with", + "an {C:cry_code}upgraded{} edition", + "{C:inactive,s:0.8}Upgrades using order in the Collection", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Visit a {C:cry_code}shop", + "during a {C:cry_code}Blind", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Select a Joker", + "or playing card", + "to become {C:cry_code}Rigged", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Ends current non-Boss {C:cry_code}Blind{}", "{C:cry_code}without{} cashing out" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Create a {C:cry_code}Glitched", + "Food Joker", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convert {C:cry_code}#1#{} selected cards", + "to a {C:cry_code}chosen{} rank", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} Mult", + }, + }, + e_cry_blur = { + name = "Blurred", + text = { + "{C:attention}Retrigger{} this", + "card {C:attention}1{} time", + "{C:green}#1# in #2#{} chance", + "to retrigger {C:attention}#3#{}", + "additional time", + }, + }, + e_cry_double_sided = { + name = "Double-Sided", + text = { + "This card can be", + "{C:attention}flipped{} to reveal", + "a different card", + "{C:inactive}(Blank side can be merged", + "{C:inactive}with another card)", + }, + }, + e_cry_glass = { + name = "Fragile", + label = "Fragile", + text = { + "{C:white,X:mult} X#3# {} Mult", + "{C:green}#1# in #2#{} chance this", + "card isn't {C:red}destroyed", + "when triggered", + }, + }, + e_cry_glitched = { + name = "Glitched", + text = { + "All values on this card", + "are {C:dark_edition}randomized{}", + "between {C:attention}X0.1{} and {C:attention}X10{}", + "{C:inactive}(If possible){}", + }, + }, + e_cry_gold = { + name = "Golden", + label = "Golden", + text = { + "Earn {C:money}$#1#{} when used", + "or triggered", + }, + }, + e_cry_m = { + name = "Jolly", + text = { + "{C:mult}+#1#{} Mult", + "This card is feeling", + "rather {C:attention}jolly{}", + }, + }, + e_cry_mosaic = { + name = "Mosaic", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + e_cry_noisy = { + name = "Noisy", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Oversaturated", + text = { + "All values", + "on this card", + "are {C:attention}doubled{}", + "{C:inactive}(If possible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Echo Card", + text = { + "{C:green}#2# in #3#{} chance to", + "{C:attention}retrigger{} #1# additional", + "times when scored", + }, + }, + }, + Joker = { + j_cry_altgoogol = { + name = "Nostalgic Googol Play Card", + text = { + "Sell this card to create", + "{C:attention}2{} copies of the leftmost {C:attention}Joker{}", + "{C:inactive,s:0.8}Does not copy Nostalgic Googol Play Cards{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Like Antennas to Heaven", + text = { + "This Joker gains", + "{X:chips,C:white} X#1# {} Chips when each", + "played {C:attention}7{} or {C:attention}4{} is scored", + "{C:inactive}(Currently {X:chips,C:white}X#2# {C:inactive} Chips)", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "{X:mult,C:white} X#1# {} Mult against {C:attention}Boss Blinds{}" }, + }, + j_cry_big_cube = { + name = "Big Cube", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_biggestm = { + name = "Huge", + text = { + "{X:mult,C:white} X#1# {} Mult until end", + "of round if {C:attention}poker hand{}", + "is a {C:attention}#2#{}", + "{C:inactive}(Currently {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}not fat, just big boned.", + }, + }, + j_cry_blender = { + name = "Blender", + text = { + "Create a {C:attention}random{}", + "consumable when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_blurred = { + name = "Blurred Joker", + text = { + "Gain {C:blue}+#1#{} hand(s) when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Each {C:attention}Joker{} gives {C:chips}+#1#{} Chips", + "Increase amount by {C:chips}+#2#{} if", + "{C:attention} poker hand{} is a {C:attention}#3#{}", + "{C:inactive,s:0.8}Jolly Jokers give{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips instead{}", + }, + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Bonus{} card to increase", + "{C:attention}Joker{} or {C:attention}Consumable slots", + "by {C:dark_edition}1{} when scored", + "{C:red}Works twice per round", + "{C:inactive,s:0.8}(Equal chance for each){}", + }, + }, + j_cry_booster = { + name = "Booster Joker", + text = { + "{C:attention}+#1#{} Booster Pack slot", + "available in shop", + }, + }, + j_cry_boredom = { + name = "Boredom", + text = { + "{C:green}#1# in #2#{} chance to", + "{C:attention}retrigger{} each {C:attention}Joker{}", + "or {C:attention}played card{}", + "{C:inactive,s:0.8}Does not affect other Boredom{}", + }, + }, + j_cry_bubblem = { + name = "Bubble M", + text = { + "Create a {C:dark_edition}Foil {C:attention}Jolly Joker{}", + "if played hand contains", + "a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_busdriver = { + name = "Bus Driver", + text = { + "{C:green}#1# in #3#{} chance", + "for {C:mult}+#2#{} Mult", + "{C:green}1 in 4{} chance", + "for {C:mult}-#2#{} Mult", + }, + }, + j_cry_canvas = { + name = "Canvas", + text = { + "{C:attention}Retrigger{} all {C:attention}Jokers{} to the left", + "once for {C:attention}every{} non-{C:blue}Common{C:attention} Joker{}", + "to the right of this Joker", + }, + }, + j_cry_caramel = { + name = "Caramel", + text = { + "Each played card gives", + "{X:mult,C:white}X#1#{} Mult when scored", + "for the next {C:attention}#2#{} rounds", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Retrigger {C:attention}leftmost{} Joker", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_chili_pepper = { + name = "Chili Pepper", + text = { + "This Joker gains {X:mult,C:white} X#2# {} Mult", + "at end of round,", + "{C:red,E:2}self destructs{} after {C:attention}#3#{} rounds", + "{C:inactive}(Currently{} {X:mult,C:white} X#1# {} {C:inactive}Mult){}", + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Chips and {X:dark_edition,C:white}^#1#{} Mult", + "if {C:attention}exactly{} #2#", + "hands remaining", + }, + }, + j_cry_circus = { + name = "Circus", + text = { + "{C:red}Rare{} Jokers each give {X:mult,C:white} X#1# {} Mult", + "{C:cry_epic}Epic{} Jokers each give {X:mult,C:white} X#2# {} Mult", + "{C:legendary}Legendary{} Jokers each give {X:mult,C:white} X#3# {} Mult", + "{C:cry_exotic}Exotic{} Jokers each give {X:mult,C:white} X#4# {} Mult", + }, + }, + j_cry_CodeJoker = { + name = "Code Joker", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:cry_code}Code Card{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Earn between", + "{C:money}$#1#{} and {C:money}$#2#{} for", + "each Joker {C:attention}sold{}", + }, + }, + j_cry_compound_interest = { + name = "Compound Interest", + text = { + "Earn {C:money}#1#%{} of total money", + "at end of round,", + "increases by {C:money}#2#%{} per", + "consecutive payout", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "When a {C:cry_code}Code{} card is used,", + "{C:green}#1# in #2#{} chance to add a copy", + "to your consumable area", + "{C:inactive}(Must have room)", + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "per {C:attention}reroll{} in the shop", + "{C:green}All rerolls are free{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Chain", + text = { + "Sell this card to", + "add {C:money}$#1#{} of {C:attention}sell value{}", + "to every {C:attention}Joker{} card", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob = { + name = "Sob", + text = { + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}run...{}", + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}hide...{}", + "{C:dark_edition,E:1}you cannot escape...{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "for each card {C:attention}purchased{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Cut", + text = { + "This Joker destroys", + "a random {C:cry_code}Code{} card", + "and gains {X:mult,C:white} X#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Delirious Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "Doodle M", + text = { + "Create 2 {C:dark_edition}Negative{} {C:attention}consumables{}", + "when {C:attention}Blind{} is selected", + "Create 1 more {C:attention}consumable", + "for each {C:attention}Jolly Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Scale", + text = { + "Scaling {C:attention}Jokers{}", + "scale {C:attention}quadratically", + "{C:inactive,s:0.8}(ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grows by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult for", + "each played, {C:attention}nonscoring{} {V:1}#2#{} card,", + "suit changes every round", + "{C:inactive}(Currently {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubious Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_duos = { + name = "The Duos", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Every {C:attention}Joker{} gives", + "{X:dark_edition,C:white}^#1#{} Mult" + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Draw {C:green}full deck{} to hand", + "when {C:attention}Blind{} is selected", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "When a {C:attention}Tag{} is acquired,", + "create {C:attention}#1#{} copies of it", + "and {C:attention}increase{} the number of", + "copies by {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Jokers appear using the", + "order from the {C:attention}Collection{}", + "Create {C:attention}#1#{} {C:dark_edition}Negative{} Joker(s)", + "when hand is played", + "{C:cry_exotic,s:0.8}Exotic {C:inactive,s:0.8}or better Jokers cannot appear", + "{s:0.8}Last Joker Generated: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Eternal Flame", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "for each card {C:attention}sold{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographic{} cards", + "each give {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "when {X:red,C:white} XMult {} is triggered", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult if", + "played cards are scored", + "{C:attention}#2#{} or fewer times", + }, + }, + j_cry_filler = { + name = "The Filler", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fractal Fingers", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + j_cry_flip_side = { + name = "On the Flip Side", + text = { + "{C:dark_edition}Double-Sided{} Jokers use", + "their back side for effects", + "instead of the front side", + "{C:attention}Retrigger{} all {C:dark_edition}Double-Sided{} Jokers" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}self destructs{} in {C:attention}#2#{} round(s)", + "Increases by {C:attention}#3#{} round when", + "{C:attention}Jolly Joker{} is {C:attention}sold{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foxy = { + name = "Foxy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "if hand played is {C:attention}not{}", + "most played {C:attention}poker hand{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Earn {C:money}$#1#{} if {C:attention}played hand{}", + "contains an {C:attention}Ace{} and a {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} all values", + "of leftmost {C:attention}Joker", + "at end of round", + }, + }, + j_cry_giggly = { + name = "Absurd Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Earn {C:money}#1#%{} of total", + "money at end of round", + "Payout increases by {C:money}#2#%{}", + "when each played {C:attention}Gold{}", + "card is scored", + }, + }, + j_cry_googol_play = { + name = "Googol Play Card", + text = { + "{C:green}#1# in #2#{} chance for", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Create a random {C:attention}Joker{}", + "at end of round", + "Sell this card to", + "create a random {C:attention}Joker{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_happyhouse = { + name = "Happy House", + text = { + "{X:dark_edition,C:white}^#1#{} Mult only after", + "playing {C:attention}114{} hands{}", + "{C:inactive}(Currently #2#/114){}", + "{C:inactive,s:0.8}There is no place like home!{}", + }, + }, + j_cry_home = { + name = "The Home", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Earn {C:money}$#1#{} when", + "using a {C:attention}consumable{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Retrigger all cards played", + "{C:attention}#2#{} time(s),", + "each played card gives", + "{X:mult,C:white} X#1# {} Mult when scored", + }, + }, + j_cry_jimball = { + name = "Jimball", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "per {C:attention}consecutive{} hand played", + "while playing your", + "most played {C:attention}poker hand", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Jolly Joker?", + text = { + "Create a {C:dark_edition}Jolly{} Joker", + "when a Joker is {C:attention}sold{}", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{}", + "when a {C:attention}Type Mult{} or", + "{C:attention}Type Chips{} Joker is sold", + }, + }, + j_cry_kooky = { + name = "Kooky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty the Clown", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when", + "each played {C:attention}card{} is scored", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoscope", + text = { + "Add {C:dark_edition}Polychrome{} to", + "a random {C:attention}Joker{} when", + "{C:attention}Boss Blind{} is defeated", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Each played {C:attention}7{} or {C:attention}2{}", + "gives {X:mult,C:white}X#1#{} Mult when scored", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Give future copies of", + "this Joker {X:mult,C:white}X#1#{} Mult", + "at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Retrigger{} all Jokers", + "once for each {C:attention}Jolly{}", + "{C:attention}Joker{} sold this round", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Retrigger(s)){}", + "{C:inactive,s:0.8}There wasn't enough room...{}", + }, + }, + j_cry_lucky_joker = { + name = "Lucky Joker", + text = { + "Earn {C:money}$#1#{} every time a", + "{C:attention}Lucky{} card {C:green}successfully{}", + "triggers", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "All Jokers give", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "when {C:attention}Jolly Joker{} is sold", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:attention}Jolly Joker{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_macabre = { + name = "Macabre Joker", + text = { + "When {C:attention}Blind{} is selected,", + "destroys each {C:attention}Joker{} except", + "{C:legendary}M Jokers{} and {C:attention}Jolly Jokers{}", + "and create 1 {C:attention}Jolly Joker{}", + "for each destroyed card", + }, + }, + j_cry_magnet = { + name = "Fridge Magnet", + text = { + "Earn {C:money}$#1#{} at end of round", + "This earns {X:money,C:white} X#2# {} if there are", + "{C:attention}#3#{} or fewer {C:attention}Joker{} cards", + }, + }, + j_cry_manic = { + name = "Manic Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Retrigger all Jokers", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_maximized = { + name = "Maximized", + text = { + "All {C:attention}face{} cards", + "are considered {C:attention}Kings{},", + "all {C:attention}numbered{} cards", + "are considered {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Labyrinth", + text = { + "All hands are considered the", + "{C:attention}first hand{} of each round,", + "all discards are considered the", + "{C:attention}first discard{} of each round", + }, + }, + j_cry_Megg = { + name = "Megg", + text = { + "Sell this card to create", + "{C:attention}#2#{} Jolly #3#, increase", + "by {C:attention}#1#{} at end of round", + }, + }, + j_cry_membershipcard = { + name = "Membership Card", + text = { + "{X:mult,C:white}X#1#{} Mult for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Old Membership Card", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteor Shower", + text = { + "{C:dark_edition}Foil{} cards each", + "give {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by", + "{C:money}$#1#{} for each {C:attention}Jolly Joker{}", + "or {C:legendary}M Joker{} at", + "end of round", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if no {C:attention}discards{} were", + "used this round", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Monkey Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and permanently add {C:attention}ten times{}", + "its sell value to this {C:chips}Chips{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{} when", + "a card with an {C:attention}Edition{} is sold", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Create an {C:legendary}M Joker{} at end of round", + "Each {C:attention}Jolly Joker{} or {C:legendary}M Joker", + "gives {X:dark_edition,C:white}^#1#{} Mult", + "Increase amount by {X:dark_edition,C:white}^#2#{}", + "when a {C:attention}Jolly Joker{} is {C:attention}sold", + "{C:inactive,s:0.8}(Tredecim excluded)", + }, + }, + j_cry_mstack = { + name = "M Stack", + text = { + "Retrigger all cards played", + "once for every", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jolly Jokers{} sold", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} retriggers){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Mult{} card to create", + "a {C:spectral}Cryptid{} card when scored", + "{C:inactive}(Must have room)", + }, + }, + j_cry_negative = { + name = "Negative Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips if played hand", + "contains a {C:attention}6{} and a {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Night", + text = { + "{X:dark_edition,C:white}^#1#{} Mult on final", + "hand of round", + "{E:2,C:red}self destructs{} on", + "final hand of round", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Retrigger each played {C:attention}7{}", + "{C:attention:}#1#{} additional time(s)", + }, + }, + j_cry_notebook = { + name = "Notebook", + text = { + "{C:green} #1# in #2#{} chance to gain {C:dark_edition}+1{} Joker", + "slot per {C:attention}reroll{} in the shop", + "{C:green}Always triggers{} if there are", + "{C:attention}#5#{} or more {C:attention}Jolly Jokers{}", + "{C:red}Works once per round{}", + "{C:inactive}(Currently {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Number Blocks", + text = { + "Earn {C:money}$#1#{} at end of round", + "Increase payout by {C:money}$#2#{}", + "for each {C:attention}#3#{} held in hand,", + "rank changes every round", + }, + }, + j_cry_nuts = { + name = "The Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_oldblueprint = { + name = "Old Blueprint", + text = { + "Copies ability of", + "{C:attention}Joker{} to the right", + "{C:green}#1# in #2#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + j_cry_oldcandy = { + name = "Nostalgic Candy", + text = { + "Sell this card to", + "permanently gain", + "{C:attention}+#1#{} hand size", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgic Invisible Joker", + text = { + "{C:attention}Duplicate{} a random", + "{C:attention}Joker{} every {C:attention}4", + "Joker cards sold", + "{s:0.8}Nostalgic Invisible Joker Excluded{}", + "{C:inactive}(Currently #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panopticon", + text = { + "All hands are considered the", + "{C:attention}last hand{} of each round", -- +$4 + }, + }, + j_cry_pickle = { + name = "Pickle", + text = { + "When {C:attention}Blind{} is skipped, create", + "{C:attention}#1#{} Tags, reduced by", + "{C:red}#2#{} when {C:attention}Blind{} is selected", + }, + }, + j_cry_pirate_dagger = { + name = "Pirate Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "and gain {C:attention}one-fourth{} of", + "its sell value as {X:chips,C:white} XChips {}", + "{C:inactive}(Currently {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + j_cry_pot_of_jokes = { + name = "Pot of Jokes", + text = { + "{C:attention}#1#{} hand size,", + "increases by", + "{C:blue}#2#{} every round", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "if all cards in played hand are", + "{C:attention}Aces{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, or {C:attention}7s{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "If {C:attention}poker hand{} is a", + "{C:attention}Royal Flush{}, destroy scored", + "{C:attention}Queen{} and create a", + "{C:dark_edition}Negative {}{C:red}Rare{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "The Quintet", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Red Bloon", + text = { + "Earn {C:money}$#1#{} in {C:attention}#2#{} round(s)", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante when", + "{C:money}$#2#{} {C:inactive}($#3#){} spent", + "{s:0.8}Requirements increase", + "{C:attention,s:0.8}exponentially{s:0.8} per use", + "{C:money,s:0.8}Next increase: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "When a {C:attention}Joker{} is sold,", + "add its effects to", + "every other Joker", + "{C:inactive,s:0.8}Does not affect other Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fill all empty Joker slots {C:inactive}(Max 100){}", + "with {C:dark_edition}Holographic{} {C:attention}Jolly Jokers{} if", + "{C:attention}discarded poker hand{} is a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Randomize abilities each {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Create an {C:green}Uncommon{} Joker", + "and 3 {C:attention}Jolly Jokers{} when", + "a {C:spectral}Spectral{} card is used", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sapling", + text = { + "After scoring {C:attention}#2#{} {C:inactive}[#1#]{} Enhanced", + "cards, sell this card to", + "create an {C:cry_epic}Epic{} {C:attention}Joker{}", + "{C:inactive,s:0.8}Will create a {C:red,s:0.8}Rare{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}if {C:cry_epic,s:0.8}Epic{} {C:inactive,s:0.8}Jokers are disabled{}", + }, + }, + j_cry_savvy = { + name = "Savvy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Scaling {C:attention}Jokers{} scale", + "as a degree-{C:attention}#1#{} polynomial", + "raise degree by {C:attention}#2#{}", + "at end of round", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluded)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Tile", + text = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Jolly {C:green}Uncommon{} Joker", + "when hand is played", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Add a {C:attention}random seal{} to each card", + "scored on {C:attention}final hand{} of round", + }, + }, + j_cry_shrewd = { + name = "Shrewd Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Silly Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Tiny", + text = { + "Create a {C:cry_jolly}Double M", + "tag if {C:attention}poker hand{}", + "is a {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker slot", + "{C:attention}+#1#{} Booster Pack slot", + "{C:attention}+#1#{} hand size", + "{C:attention}+#1#{} consumable slot", + "{C:attention}+#1#{} card in shop", + }, + }, + j_cry_spaceglobe = { + name = "Celestial Globe", + text = { + "This Joker gains {X:chips,C:white}X#2#{} Chips", + "if {C:attention}poker hand{} is a {C:attention}#3#{},", + "Hand changes after increase{}", + "{C:inactive}(Currently{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Creates a {C:dark_edition}Negative{} copy", + "of a random {C:attention}Joker{}", + "at the end of the {C:attention}shop", + "{C:inactive,s:0.8}Does not copy other Speculo{}", + }, + }, + j_cry_stardust = { + name = "Stardust", + text = { + "{C:dark_edition}Polychrome{} cards", + "each give {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "This Joker destroys a", + "random {C:planet}Planet{} card", + "and gains {X:dark_edition,C:white} ^#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_subtle = { + name = "Subtle Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Earn {C:money}$#3#{} at", + "end of round", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "At end of round, create", + "a {C:attention}copy{} of a random", + "card {C:attention}held in hand{},", + "destroy all others", + "{C:attention,s:0.8}Kings{s:0.8} of {C:hearts,s:0.8}Hearts{s:0.8} are prioritized", + }, + }, + j_cry_swarm = { + name = "The Swarm", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Catalyst", + text = { + "Balances {C:chips}Chips{} and {C:mult}Mult{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + "Earn {C:money}$#2#{} at end of round", + }, + }, + j_cry_translucent = { + name = "Translucent Joker", + text = { + "Sell this card to create", + "a {C:attention}Banana Perishable{} copy", + "of a random {C:attention}Joker{}", + "{s:0.8,C:inactive}(Copy bypasses perish compat)", + }, + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Triplet Rhythm", + text = { + "{X:mult,C:white} X#1# {} Mult if scoring hand", + "contains {C:attention}exactly{} three {C:attention}3s", + }, + }, + j_cry_unity = { + name = "The Unity", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Poker hands{} gain", + "{X:red,C:white} X#1# {} Mult and {X:blue,C:white} X#1# {} Chips", + "when leveled up", + }, + }, + j_cry_unjust_dagger = { + name = "Unjust Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and gain {C:attention}one-fifth{} of", + "its sell value as {X:mult,C:white} XMult {}", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "This Joker gains {C:money}$#1#{} of {C:attention}sell value{}", + "if {C:attention}poker hand{} contains a {C:attention}#2#{}", + "Sell this card to create a", + "{C:dark_edition}Polychrome{} {C:attention}Jolly Joker{} for", + "every {C:money}$4{} of {C:attention}sell value{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Wacky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "All Jokers give", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "All Jokers give", + "{C:money}$#1#{} when triggered", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "This Joker gains", + "{C:mult}+#2#{} Mult when each played", + "{C:attention}Ace{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "is scored", + "{C:inactive}(Currently {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Retrigger each played {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} additional time(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Wheel of Hope", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when failing", + "a {C:attention}Wheel of Fortune{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if {C:attention}played hand{} contains a", + "{C:attention}2{} and {C:attention}7{} of different suits", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutron Star", + text = { + "Upgrade a random", + "poker hand by", + "{C:attention}1{} level for each", + "{C:attention}Neutron Star{} used", + "in this run", + "{C:inactive}(Currently{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# in #2#{} chance to", + "upgrade every", + "{C:legendary,E:1}poker hand{}", + "by {C:attention}1{} level", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "CCD Sleeve", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Conveyor Sleeve", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Critical Sleeve", + text = { + "After each hand played,", + "{C:green}1 in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}1 in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Encoded Sleeve", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balanced Sleeve", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}+2 Shop Slots", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unlimited Sleeve", + text = { + "You can select {C:attention}any", + "number of cards", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Misprinted Sleeve", + text = { + "Values of cards", + "are {C:attention}randomized", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Redeemed Sleeve", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Wormhole Sleeve", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + }, + Spectral = { + c_cry_analog = { + name = "Analog", + text = { + "Create {C:attention}#1#{} copies of a", + "random {C:attention}Joker{}, destroy", + "all other Jokers, {C:attention}+#2#{} Ante", + }, + }, + c_cry_gateway = { + name = "Gateway", + text = { + "Create a random", + "{C:cry_exotic,E:1}Exotic{C:attention} Joker{}, destroy", + "all other Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Lock", + text = { + "Remove {C:red}all{} stickers", + "from {C:red}all{} Jokers,", + "then apply {C:purple,E:1}Eternal{}", + "to a random {C:attention}Joker{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Create a card", + "of {C:cry_code}your choice", + "{C:inactive,s:0.8}(Exotic Jokers #1#excluded)", + }, + }, + c_cry_replica = { + name = "Replica", + text = { + "Convert all cards", + "held in hand", + "to a {C:attention}random{}", + "card held in hand", + }, + }, + c_cry_source = { + name = "Source", + text = { + "Add a {C:cry_code}Green Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_summoning = { + name = "Summoning", + text = { + "Create a random", + "{C:cry_epic}Epic{} {C:joker}Joker{}, destroy", + "one random {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Trade", + text = { + "{C:attention}Lose{} a random Voucher,", + "gain {C:attention}2{} random Vouchers", + }, + }, + c_cry_typhoon = { + name = "Typhoon", + text = { + "Add an {C:cry_azure}Azure Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_vacuum = { + name = "Vacuum", + text = { + "Removes {C:red}all {C:green}modifications{}", + "from {C:red}all{} cards held in hand,", + "Earn {C:money}$#1#{} per {C:green}modification{} removed", + "{C:inactive,s:0.7}(ex. Enhancements, Seals, Editions)", + }, + }, + c_cry_white_hole = { + name = "White Hole", + text = { + "{C:attention}Remove{} all hand levels,", + "upgrade {C:legendary,E:1}most played{} poker hand", + "by {C:attention}3{} for each removed level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pink Stake", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brown Stake", + colour = "Brown", + text = { + "All {C:attention}stickers{} are compatible", + "with each other", + }, + }, + stake_cry_yellow = { + name = "Yellow Stake", + colour = "Yellow", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Jade Stake", + colour = "Jade", + text = { + "Cards can be drawn {C:attention}face down{}", + }, + }, + stake_cry_cyan = { + name = "Cyan Stake", + colour = "Cyan", + text = { + "{C:green}Uncommon{} and {C:red}Rare{} Jokers are", + "less likely to appear", + }, + }, + stake_cry_gray = { + name = "Gray Stake", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Crimson Stake", + colour = "Crimson", + text = { + "Vouchers restock on {C:attention}even{} Antes", + }, + }, + stake_cry_diamond = { + name = "Diamond Stake", + colour = "Diamond", + text = { + "Must beat Ante {C:attention}10{} to win", + }, + }, + stake_cry_amber = { + name = "Amber Stake", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Pack slot", + }, + }, + stake_cry_bronze = { + name = "Bronze Stake", + colour = "Bronze", + text = { + "Vouchers are {C:attention}50%{} more expensive", + }, + }, + stake_cry_quartz = { + name = "Quartz Stake", + colour = "Quartz", + text = { + "Jokers can be {C:attention}Pinned{}", + "{s:0.8,C:inactive}(Stays pinned to the leftmost position){}", + }, + }, + stake_cry_ruby = { + name = "Ruby Stake", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds can become", + "{C:attention}Boss{} Blinds", + }, + }, + stake_cry_glass = { + name = "Glass Stake", + colour = "Glass", + text = { + "Cards can {C:attention}shatter{} when scored", + }, + }, + stake_cry_sapphire = { + name = "Sapphire Stake", + colour = "Sapphire", + text = { + "Lose {C:attention}25%{} of current money", + "at end of Ante", + "{s:0.8,C:inactive}(Up to $10){}", + }, + }, + stake_cry_emerald = { + name = "Emerald Stake", + colour = "Emerald", + text = { + "Cards, packs, and vouchers", + "can be {C:attention}face down{}", + "{s:0.8,C:inactive}(Unable to be viewed until purchased){}", + }, + }, + stake_cry_platinum = { + name = "Platinum Stake", + colour = "Platinum", + text = { + "Small Blinds are {C:attention}removed{}", + }, + }, + stake_cry_twilight = { + name = "Twilight Stake", + colour = "Twilight", + text = { + "Cards can be {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 in 10 chance of being destroyed each round){}", + }, + }, + stake_cry_verdant = { + name = "Verdant Stake", + colour = "Verdant", + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Ember Stake", + colour = "Ember", + text = { + "All items give no money when sold", + }, + }, + stake_cry_dawn = { + name = "Dawn Stake", + colour = "Dawn", + text = { + "Tarots and Spectrals target {C:attention}1", + "fewer card", + "{s:0.8,C:inactive}(Minimum of 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizon Stake", + colour = "Horizon", + text = { + "When blind selected, add a", + "{C:attention}random card{} to deck", + }, + }, + stake_cry_blossom = { + name = "Blossom Stake", + colour = "Blossom", + text = { + "{C:attention}Final{} Boss Blinds can appear", + "in {C:attention}any{} Ante", + }, + }, + stake_cry_azure = { + name = "Azure Stake", + colour = "Azure", + text = { + "Values on Jokers are reduced", + "by {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Ascendant Stake", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop slot", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Banana Tag", + text = { + "Creates {C:attention}#1#", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_bettertop_up = { + name = "Better Top-up Tag", + text = { + "Creates up to {C:attention}#1#", + "{C:green}Uncommon{} Jokers", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_better_voucher = { + name = "Golden Voucher Tag", + text = { + "Adds one Tier {C:attention}#1#{} Voucher", + "to the next shop", + }, + }, + tag_cry_blur = { + name = "Blurred Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Blurred{}", + }, + }, + tag_cry_booster = { + name = "Booster Tag", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:attention}double{} cards and", + "{C:attention}double{} choices", + }, + }, + tag_cry_bundle = { + name = "Bundle Tag", + text = { + "Create a {C:attention}Standard Tag{}, {C:tarot}Charm Tag{},", + "{C:attention}Buffoon Tag{}, and {C:planet}Meteor Tag", + }, + }, + tag_cry_cat = { + name = "Cat Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Console Tag", + text = { + "Gives a free", + "{C:cry_code}Program Pack", + }, + }, + tag_cry_double_m = { + name = "Double M Tag", + text = { + "Shop has a", + "{C:dark_edition}Jolly {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Empowered Tag", + text = { + "Gives a free {C:spectral}Spectral Pack", + "with {C:legendary,E:1}The Soul{} and {C:cry_exotic,E:1}Gateway{}", + }, + }, + tag_cry_epic = { + name = "Epic Tag", + text = { + "Shop has a half-price", + "{C:cry_epic}Epic Joker", + }, + }, + tag_cry_gambler = { + name = "Gambler's Tag", + text = { + "{C:green}#1# in #2#{} chance to create", + "an {C:cry_exotic,E:1}Empowered Tag", + }, + }, + tag_cry_glass = { + name = "Fragile Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Fragile{}", + }, + }, + tag_cry_glitched = { + name = "Glitched Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Glitched{}", + }, + }, + tag_cry_gold = { + name = "Golden Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Golden{}", + }, + }, + tag_cry_gourmand = { + name = "Gourmand Tag", + text = { + "Shop has a free", + "{C:attention}Food Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gives a free", + "{C:cry_ascendant}Meme Pack", + }, + }, + tag_cry_m = { + name = "Jolly Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Jolly{}", + }, + }, + tag_cry_memory = { + name = "Memory Tag", + text = { + "Create {C:attention}#1#{} copies of", + "the last {C:attention}Tag{} used", + "during this run", + "{s:0.8,C:inactive}Copying Tags excluded", + "{s:0.8,C:inactive}Currently: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Mosaic{}", + }, + }, + tag_cry_oversat = { + name = "Oversaturated Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Oversaturated{}", + }, + }, + tag_cry_quadruple = { + name = "Quadruple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_quintuple = { + name = "Quintuple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_rework = { + name = "Rework Tag", + text = { + "Shop has a(n)", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schematic Tag", + text = { + "Shop has a", + "{C:attention}Brainstorm", + }, + }, + tag_cry_scope = { + name = "Scope Tag", + text = { + "{C:attention}+#1# {C:blue}hands{} and", + "{C:red}discards{} next round", + }, + }, + tag_cry_triple = { + name = "Triple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "The Automaton", + text = { + "Creates up to {C:attention}#1#", + "random {C:cry_code}Code{} card", + "{C:inactive}(Must have room)", + }, + }, + c_cry_eclipse = { + name = "The Eclipse", + text = { + "Enhances {C:attention}#1#{} selected card", + "into an {C:attention}Echo Card", + }, + }, + c_cry_meld = { + name = "Meld", + text = { + "Select a {C:attention}Joker{} or", + "{C:attention}playing card{} to", + "become {C:dark_edition}Double-Sided", + }, + }, + c_cry_theblessing = { + name = "The Blessing", + text = { + "Creates {C:attention}1{}", + "random {C:attention}consumable{}", + "{C:inactive}(Must have room){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglyph", + text = { + "Set Ante to {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Blank Canvas", + text = { + "{C:attention}+#1#{} hand size", + }, + }, + v_cry_clone_machine = { + name = "Clone Machine", + text = { + "Double Tags become", + "{C:attention}Quintuple Tags{} and", + "are {C:attention}4X{} as common", + }, + }, + v_cry_command_prompt = { + name = "Command Prompt", + text = { + "{C:cry_code}Code{} cards", + "can appear", + "in the {C:attention}shop{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Double Tags become", + "{C:attention}Triple Tags{} and are", + "{C:attention}2X{} as common", + }, + }, + v_cry_curate = { + name = "Curate", + text = { + "All cards", + "appear with", + "an {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Dexterity", + text = { + "Permanently", + "gain {C:blue}+#1#{} hand(s)", + "each round", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "After every round,", + "{X:dark_edition,C:white} X1.5 {} to all values", + "on the back of", + "{C:dark_edition}Double-Sided{} cards" + }, + }, + v_cry_double_slit = { + name = "Double Slit", + text = { + "{C:attention}Meld{} can appear", + "in the shop and", + "Arcana Packs", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "{C:dark_edition}Double-Sided{} cards appear", + "{C:attention}4X{} more frequently", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker slot(s)", + }, + }, + v_cry_massproduct = { + name = "Mass Production", + text = { + "All cards and packs", + "in shop cost {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Raise the cap on", + "interest earned in", + "each round to {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multistock", + text = { + "{C:attention}+#1#{} card slot(s) and", + "{C:attention}+#1#{} booster pack slot(s)", + "available in shop", + }, + }, + v_cry_pacclimator = { + name = "Planet Acclimator", + text = { + "{C:planet}Planet{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:planet}Planet{}", + "cards are {C:green}free{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Retrigger{} all M Jokers", + "once for every Pair", + "{C:attention}contained{} in played hand", + }, + }, + v_cry_pairing = { + name = "Pairing", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand is a {C:attention}Pair", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computing", + text = { + "{C:cry_code}Code{} cards can spawn", + "with {C:dark_edition}Negative{} edition", + }, + }, + v_cry_repair_man = { + name = "Repair Man", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand contains a {C:attention}Pair", + }, + }, + v_cry_rerollexchange = { + name = "Reroll Exchange", + text = { + "All rerolls", + "cost {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satellite Uplink", + text = { + "{C:cry_code}Code{} cards may", + "appear in any of", + "the {C:attention}Celestial Packs{}", + }, + }, + v_cry_scope = { + name = "Galactic Scope", + text = { + "Create the {C:planet}Planet", + "card for played", + "{C:attention}poker hand{}", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Tarot Acclimator", + text = { + "{C:tarot}Tarot{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:tarot}Tarot{}", + "cards are {C:green}free{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Printer", + text = { + "Double Tags become", + "{C:attention}Quadruple Tags{} and", + "are {C:attention}3X{} as common", + }, + }, + v_cry_threers = { + name = "The 3 Rs", + text = { + "Permanently", + "gain {C:red}+#1#{} discard(s)", + "each round", + }, + }, + v_cry_stickyhand = { + name = "Sticky Hand", + text = { + "{C:attention}+#1#{} card", + "selection limit", + }, + }, + v_cry_grapplinghook = { + name = "Grappling Hook", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperspace Tether", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "destroyed each round", + }, + }, + cry_rigged = { + name = "Rigged", + text = { + "All {C:cry_code}listed{} probabilities", + "are {C:cry_code}guaranteed", + }, + }, + cry_hooked = { + name = "Hooked", + text = { + "When this Joker is {C:cry_code}triggered{},", + "trigger {C:cry_code}#1#", + }, + }, + food_jokers = { + name = "Food Jokers", + text = { + "{s:0.8}Gros Michel, Egg, Ice Cream, Cavendish,", + "{s:0.8}Turtle Bean, Diet Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Pickle, Chili Pepper, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eternal", + text = { + "All cards in pack", + "are {C:attention}Eternal{}", + }, + }, + cry_perishable_booster = { + name = "Perishable", + text = { + "All cards in pack", + "are {C:attention}Perishable{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "All cards in pack", + "are {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Pinned", + text = { + "All cards in pack", + "are {C:attention}Pinned{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "All cards in pack", + "are {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eternal", + text = { + "Can't be traded", + }, + }, + cry_perishable_voucher = { + name = "Perishable", + text = { + "Debuffed after", + "{C:attention}#1#{} rounds", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)", + }, + }, + cry_rental_voucher = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at", + "end of round", + }, + }, + cry_pinned_voucher = { + name = "Pinned", + text = { + "Remains in shop", + "until redeemed", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "unredeemed each round", + }, + }, + cry_perishable_consumeable = { + name = "Perishable", + text = { + "Debuffed at", + "end of round", + }, + }, + cry_rental_consumeable = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at end of", + "round, and on use", + }, + }, + cry_pinned_consumeable = { + name = "Pinned", + text = { + "Can't use other", + "non-{C:attention}Pinned{} consumables", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance to do", + "nothing on use", + }, + }, + p_cry_code_normal_1 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_normal_2 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_two = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_three = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + undiscovered_code = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + cry_green_seal = { + name = "Green Seal", + text = { + "Creates a {C:cry_code}Code{} card", + "when played and unscoring", + "{C:inactive}(Must have room)", + }, + }, + cry_azure_seal = { + name = "Azure Seal", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planets{} for played", + "{C:attention}poker hand{}, then", + "{C:red}destroy{} this card", + }, + }, + }, + }, + misc = { + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Legally Blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Break Infinity", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Home Realtor", + ach_cry_jokes_on_you = "Joke's on You, Pal!", + ach_cry_niw_uoy = "!niW uoY", + ach_cry_now_the_fun_begins = "Now the Fun Begins", + ach_cry_patience_virtue = "Patience is a Virtue", + ach_cry_perfectly_balanced = "Perfectly Balanced", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Traffic Jam", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "We Told You Not To", + ach_cry_what_have_you_done = "WHAT HAVE YOU DONE?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtain Blurred Blurred Joker", + ach_cry_bullet_hell = "Have 15 AP Jokers", + ach_cry_break_infinity = "Score 1.79e308 Chips in a single hand", + ach_cry_cryptid_the_cryptid = "Use Cryptid on Cryptid", + ach_cry_exodia = "Have 5 Exotic Jokers", + ach_cry_freak_house = "Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice", + ach_cry_googol_play_pass = "Rig a Googol Play Card", + ach_cry_haxxor = "Use a cheat code", + ach_cry_home_realtor = "Activate Happy House before Ante 8 (without DoE/Antimatter)", + ach_cry_jokes_on_you = "Trigger The Joke's effect on Ante 1 and win the run", + ach_cry_niw_uoy = "Reach Ante -8", + ach_cry_now_the_fun_begins = "Obtain Canvas", + ach_cry_patience_virtue = "Wait out Lavender Loop for 2 minutes before playing first hand and beat the blind", + ach_cry_perfectly_balanced = "Beat Very Fair Deck on Ascendant Stake", + ach_cry_pull_request = "Have ://COMMIT spawn the same Joker that it destroyed", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Win in 1 round", + ach_cry_used_crash = "Use ://CRASH", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Music", + cry_set_enable_features = "Select features to enable (applies on game restart):", + cry_feat_achievements = "Achievements", + ["cry_feat_antimatter deck"] = "Antimatter Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Code Cards", + ["cry_feat_misc. decks"] = "Misc. Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechanics", + ["cry_feat_enhanced decks"] = "Enhanced Decks", + ["cry_feat_epic jokers"] = "Epic Jokers", + ["cry_feat_exotic jokers"] = "Exotic Jokers", + ["cry_feat_m jokers"] = "M Jokers", + cry_feat_menu = "Custom Main Menu", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Misc. Jokers", + cry_feat_planets = "Planets", + cry_feat_jokerdisplay = "JokerDisplay (Does Nothing)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Sleeves", + cry_feat_spectrals = "Spectrals", + ["cry_feat_more stakes"] = "Stakes", + cry_feat_vouchers = "Vouchers", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Pack", + k_cry_meme_pack = "Meme Pack", + + cry_critical_hit_ex = "Critical Hit!", + cry_critical_miss_ex = "Critical Miss!", + + cry_debuff_oldhouse = "No Full Houses", + cry_debuff_oldarm = "Must play 4 or fewer cards", + cry_debuff_oldpillar = "No Straights", + cry_debuff_oldflint = "No Flushes", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applies abilities of all defeated bosses", + + k_code = "Code", + b_code_cards = "Code Cards", + b_pull = "PULL", + cry_hooked_ex = "Hooked!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTER RANK", + cry_code_enh = "ENTER ENHANCEMENT", + cry_code_hand = "ENTER POKER HAND", + cry_code_enter_card = "ENTER A CARD", + cry_code_apply = "APPLY", + cry_code_apply_previous = "APPLY PREVIOUS", + cry_code_exploit = "EXPLOIT", + cry_code_exploit_previous = "EXPLOIT PREVIOUS", + cry_code_create = "CREATE", + cry_code_create_previous = "CREATE PREVIOUS", + cry_code_execute = "EXECUTE", + cry_code_cancel = "CANCEL", + + b_flip = "FLIP", + b_merge = "MERGE", + + cry_again_q = "Again?", + cry_curse = "Curse", + cry_curse_ex = "Curse!", + cry_sobbing = "Help me...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Round", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "No triggers left!", + cry_unredeemed = "Unredeemed...", + cry_active = "Active", + cry_inactive = "Inactive", + + k_disable_music = "Disable Music", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Copyright Notice", + cry_notif_jimball_d1 = "Jimball plays the song \"Funkytown\",", + cry_notif_jimball_d2 = "which is copyrighted and can't be", + cry_notif_jimball_d3 = "used for streams and videos.", + }, + labels = { + food_jokers = "Food Jokers", + banana = "Banana", + code = "Code", + cry_rigged = "Rigged", + cry_hooked = "Hooked", + + cry_green_seal = "Green Seal", + cry_azure_seal = "Azure Seal", + + cry_astral = "Astral", + cry_blur = "Blurred", + cry_double_sided = "Double-Sided", + cry_glass = "Fragile", + cry_glitched = "Glitched", + cry_gold = "Golden", + cry_m = "Jolly", + cry_mosaic = "Mosaic", + cry_noisy = "Noisy", + cry_oversat = "Oversaturated", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic" + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + }, + v_text = { + ch_c_cry_all_perishable = {"All Jokers are {C:eternal}Perishable{}"}, + ch_c_cry_all_rental = {"All Jokers are {C:eternal}Rental{}"}, + ch_c_cry_all_pinned = {"All Jokers are {C:eternal}Pinned{}"}, + ch_c_cry_all_banana = {"All Jokers are {C:eternal}Banana{}"}, + ch_c_all_rnj = {"All Jokers are {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"All purchasable items have all stickers"}, + ch_c_cry_rush_hour = {"All Boss Blinds are {C:attention}The Clock{} or {C:attention}Lavender Loop"}, + ch_c_cry_rush_hour_ii = {"All Blinds are {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}The Clock{} and {C:attention}Lavender Loop{} scale {C:attention}twice{} as fast"}, + ch_c_cry_no_tags = {"Skipping is {C:attention}disabled{}"}, + ch_c_cry_no_vouchers = {"{C:attention}Vouchers{} no longer appear in the shop"}, + ch_c_cry_no_boosters = {"{C:attention}Booster Packs{} no longer appear in the shop"}, + ch_c_cry_no_rerolls = {"Rerolling is {C:attention}disabled{}"}, + ch_c_cry_no_consumables = {"{C:attention}Consumables{} no longer appear"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/ru.lua b/Cryptid/localization/ru.lua new file mode 100644 index 0000000..e582d43 --- /dev/null +++ b/Cryptid/localization/ru.lua @@ -0,0 +1,3329 @@ +-- НАЙДИ РУССКИЙ ПЕРЕВО- +-- +-- ‹36ü¯ ‹¯¯¯` +-- ‹ü6üÇÅ6‹ ‹üGGü66l +-- `‡‡3ÇÇÞÅÇ*` `‹‹`` `¯ÇgÅ6Þü‡3¯ +-- ¯ll‡3Ç6gÅÞ‡l¯¯***‡l6gg6Ç33l** +-- `*ll33‡‡l*¯‹‹‹¯*‹¯¯*l*‡‡‡l**` +-- `ll‹‹‹‹‹¯ll*3l‡‡l*¯¯‹‹‹‹ll‹ +-- `¯¯¯‹‹¯*‡Ç6‡636ü‡6‡**¯*33¯ +-- ‹*¯¯¯l‡‡üÞ3ÇÞÇÞ363ü‡3*¯lü3` +-- ¯*l3l*ÞGÇG‡GÞÅGü‡ÞÇÇü3l*ü6* +-- `lü‡*¯‡ÞÆÆÅlÇÞ6ü3ÆÆÆÇülü6üÇ3 +-- `3l‹¯*3l3ü*ü‡‡33l*6üüü6ü6G63 +-- ‹33‡l ‹‡‡36‡33l36ÇüllüÞ‡‡6ülllü666 +-- ¯üÇÇÇü6Çü `l‡l*¯‹¯*‡3¯ÇÞÞÞ¯‡‡**lüü3üÇ3 +-- ¯3Ç6ü36ÇÇ666‡ ¯*¯‹*¯¯¯¯‹``l3``‹¯¯*‡**ll‡` +-- ‡66ÇÇ6ÇÇÇ6ÇÇüü‡ll‡l¯¯l‡ll**¯¯¯***l‡*¯¯¯*ll` +-- ‹ü6Ç6ÇÇÇÇÇ63‡l*¯33ül*¯***¯¯****¯*¯¯¯¯‹‹¯***ll` +-- `6ÇÇ66ü6üüÇl‡3336ÇÇ6ü*¯‹‹‹‹¯¯¯‹¯¯¯¯¯¯‹‹‹‹¯***l‡¯ +-- ‡ÇÇÇ66ÇÇ36ÇüÇÇÇÇÇÇÇÇ6ü‡*‹‹‹¯¯‹‹‹¯‹¯‹‹‹‹¯*llll**‡l` +-- lÇÇÇÇÇü33üü366ü66ÇÇÇ6ÇÇ66l¯‹‹‹‹‹‹¯l3ü6ÇÇÇÇ6666üÇÇ6‡¯¯ +-- üÇÇÇ66ü3l*l‡ü3üüÇÇÇ3üÇÇ66ül¯¯¯¯*lÇÇ6663******¯¯***‡üÇü‹ +-- `ÇÇÇü3‡3*‡l‡üü‡lll‡‡33‡36Ç63‡l**l*lüÇÇ6l‡ü3333ll‡3üü3*l` +-- *ÇÇÇ63l*¯*l33l‡‡lll*‡ll‡‡ÇÇüü‡l**ÇÇÇÇ6¯``````````lÇÇÇÇLJ +-- *ÇÇÇüll‡‡333lll33‡l*‡l*3lüÇÇ6ü‡l*ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇü‡ +-- `6Ç6ÇÇÇ3ÇÇ6ü663‡l‡‡‡3l*‡*‡‡6Ç6Çü3üÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇLJ¯* +-- üÇÇÇÇÇ6ÇÇüÇ6Ç33333‡36666üÇÇ6ÇÇÇ6üÇÇÇÇÇÇÇÇÇÇÇÇÇÇü63‡l‡ÞÇÞ3* +-- ¯ÇÇÇÇÇÇ33ÇÇÇ3‡‡l3‡lÇÇü63‡Ç6l6Ç6üÇÞÇÇÇÇÇÇÇÇÇÇÇÇÇ33l3üü66ÇÇ* +-- `ÇÇÇÇÇ6ü‡‡3ü66‡‡3l‡‡‡‡‡3ü6üüü66ÇÇÇÞÇÇÇÇÇÇÇÇÇÇ6üül*lll33ü6l +-- `‡‡66Ç3‡l33633ülüü‡ü‡33ll3üÇü3‡36ÞÇÇÇÇÇÇÇÇÇÇÇG33l**ll‡6l +-- ‡ü‡33ü3‡ü3‡l****lll‡‡‡*¯l‡üü‡l*l‡6ÇÇÇÇÇÇÇÇÇÇÇÇÇgÆ6633‡‹ +-- `6Ç63‡‡3üÞÇül******lll*‡ll*‡‡lll‡‡*3ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇ` +-- üüü‡‡‡‡llü366‡l**¯*l**l3l***lll‡‡üüü3‡6ÇÇÇÇÇÇÇÇÇÇÇÇÇ6 +-- ‡ü‡‡*ll*3ügÞ6ÇÇü‡*l***lll3363‡ü6ÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇǯ +-- lü‡l‡6G6ÇüÇÇ6Ç6‡*l‡3366ÇÇÇ6‡6üÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇÇ +-- `*36Ç6* *‡ÇÇÇÇü6Ç6ÇÇüüüülüÇÇÇÇÇÇÇÞÆÞ6ÇÇÇÇÇÇÇÇü‹` +-- `l66ÇÇÇ6ü33‡63üÇÇÇÞgggÞÞÇÞGÇ3l*‡üÇÇ3 +-- `6ÞÞÞÇÇÇÇÞÞGgÅgGÇ6ü33‡llll33333‡ll‹ +-- ‹3‡3ü6üü66666ü33‡‡lll*l‡l***l‡l*l‡* +-- ¯33‡l‡‡lll‡‡‡*‡‡‡‡lllll3llll*‡‡ll‡l +-- +-- АХАХАХ ЭТА ЧЁ???? ОГРОМНЫЙ КОТ С ЧЕБУРЕКОМ И ПИВОМ ЧЁЁЁЁЁЁЁЁЁЁЁЁЁЁЁЁЁЁЁЁ??? Котяря блин ты либо делись либо улепётывая из перевода +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Колода антиматерии", + text = { + "Применяет {C:legendary,E:1}плюсы{}", + "{C:attention}каждой{} колоды", + }, + }, + b_cry_beta = { + name = "Ностальгическая колода", + text = { + "Слоты под {C:attention}Джокеры{}", + "и под {C:attention}Расходники{} {C:attention}соединены", + "{C:attention}Ностальгические{} блайнды заменяют", + "их обновлённые блайнды" + }, + }, + b_cry_blank = { + name = "Пустая колода", + text = { + "{C:inactive,E:1}Ничего не делает?", + }, + }, + b_cry_CCD = { + name = "CCD Колода", + text = { + "Каждая карта это ещё и", + "{C:attention}случайный{} расходник", + }, + }, + b_cry_conveyor = { + name = "Conveyor Deck", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + b_cry_critical = { + name = "Критическая колода", + text = { + "После каждой сыгранной руки,", + "{C:green}#1# к 4{} шанс для {X:dark_edition,C:white} ^2 {} Множ", + "{C:green}#1# к 8{} шанс для {X:dark_edition,C:white} ^0.5 {} Множ", + }, + }, + b_cry_encoded = { + name = "Закодированная колода", + text = { + "Начинаете партию с {C:cry_code,T:j_cry_CodeJoker}Код Джокером{}", + "и {C:cry_code,T:j_cry_copypaste}Копировать/Вставить{}", + "Только {C:cry_code}Карты кода{} появляются в магазине", + }, + }, + b_cry_equilibrium = { + name = "Колода равновесия", + text = { + "Все карты имеют", + "{C:attention}одинаковый шанс{}", + "Появления в магазинах,", + "Начинаете партию с", + "{C:attention,T:v_overstock_plus}Изобилие перебросов", + }, + }, + b_cry_glowing = { + name = "Glowing Deck", + text = { + "Умножает значения всех", + "джокеров на {X:dark_edition,C:white} X1.25 {}", + "после победы над босс-блайндом", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Бесконечная колода", + text = { + "Вы можете выбрать{C:attention}любое", + "количество карт", + "{C:attention}+1{} размер руки", + }, + }, + b_cry_misprint = { + name = "Колода с опечатками", + text = { + "Числы карт", + "и покерных рук", + "{C:attention}случайны", + }, + }, + b_cry_redeemed = { + name = "Погашенная колода", + text = { + "Когда {C:attention}Ваучер{} куплен,", + "получаешь {C:attention}все улучш. версии", + }, + }, + b_cry_very_fair = { + name = "Очень честная колода", + text = { + "{C:blue}-2{} руки, {C:red}-2{} сброса", + "каждый раунд", + "{C:attention}Ваучеры{} не появляются", + "в магазине", + }, + }, + b_cry_wormhole = { + name = "Колода червоточины", + text = { + "Начинаете с {C:cry_exotic}Экзотическим{C:attention} Джокером", + "Джокеры в {C:attention}20X{} чаще", + "появляются {C:dark_edition}Негативными", + "{C:attention}-2{} Слота джокера", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "Коробка", + text = { + "Все обычные джокеры", + "ослабляются", + }, + }, + bl_cry_clock = { + name = "Часы", + text = { + "+0.1X минимум очков каждые", + "3 секунды в этом анте", + }, + }, + bl_cry_hammer = { + name = "Молоток", + text = { + "Все карты с нечётным", + "рангом ослабляются", + }, + }, + bl_cry_joke = { + name = "Шутка", + text = { + "Если очки в два раза больше минимума,", + "Умножить анте на #1#", + }, + }, + bl_cry_magic = { + name = "Магия", + text = { + "Все карты с чётным", + "рангом ослабляются", + }, + }, + bl_cry_lavender_loop = { + name = "Лавандовый круг", + text = { + "1.25X минимум очков каждые", + "1.5 секунды потраченные в этом раунде", + }, + }, + bl_cry_obsidian_orb = { + name = "Обсидиановая сфера", + text = { + "Применяет все способности", + "побеждённых босс-блайдов", + }, + }, + bl_cry_oldarm = { + name = "Рука, Ностальгия", + text = { + "Необходима сыграть 4", + "или меньше карт", + }, + }, + bl_cry_oldfish = { + name = "Рыба, Ностальгия", + text = { + "Все руки начинают", + "с 1 Множ.", + }, + }, + bl_cry_oldflint = { + name = "Кремень, Ностальгия", + text = { + "Без флешов", + }, + }, + bl_cry_oldhouse = { + name = "Дом, Ностальгия", + text = { + "Без фулл-хаусов", + }, + }, + bl_cry_oldmanacle = { + name = "Кандалы, Ностальгия", + text = { + "Разделить Множ. на Сбросы", + }, + }, + bl_cry_oldmark = { + name = "Знак, Ностальгия", + text = { + "Без рук,", + "которые содержат пару", + }, + }, + bl_cry_oldox = { + name = "Буйвол, Ностальгия", + text = { + "Все руки начинают", + "с нулём фишек", + }, + }, + bl_cry_oldpillar = { + name = "Столп, Ностальгия", + text = { + "Без стритов", + }, + }, + bl_cry_oldserpent = { + name = "Змей, Ностальгия", + text = { + "Разделить Множ.", + "на уровень разыгранной руки", + }, + }, + bl_cry_pin = { + name = "Булавка", + text = { + "Джокеры с эпической или выше", + "редкостью ослабляются", + }, + }, + bl_cry_pinkbow = { + name = "Бантик", + text = { + "Картам присваивается случайная", + "", + }, + }, + bl_cry_sapphire_stamp = { + name = "Сапфировый штамп", + text = { + "Выбор дополнительной карты,", + "случайная карта не учитывается", + }, + }, + bl_cry_shackle = { + name = "Кандалы 2.0", + text = { + "Все негативные джокеры", + "ослаблены", + }, + }, + bl_cry_striker = { + name = "Нападающий", + text = { + "Все редкие джокеры", + "ослаблены", + }, + }, + bl_cry_tax = { + name = "Налог", + text = { + "Максимальные очки за руку", + "0.4X требований блайнда", + }, + }, + bl_cry_tornado = { + name = "Бирюзовый торнадо", + text = { + "#1# в #2# шанс что", + "руку не посчитает", + }, + }, + bl_cry_trick = { + name = "Трюк", + text = { + "После каждой руки, переворачивает", + "все карты в руке рубашкой вверх", + }, + }, + bl_cry_vermillion_virus = { + name = "Вирус Вермиллион", + text = { + "Один случайный джокер", + "заменяется каждую руку", + }, + }, + bl_cry_windmill = { + name = "Мельница", + text = { + "Все необычные джокеры", + "ослаблены", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Конвертировать {C:cry_code}#1#{} выбранную карту", + "в {C:cry_code}выбранное{} улучшение", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Уничтожить {C:cry_code}выбранного{} Джокер,", + "создать {C:cry_code}нового{} Джокера", + "той же {C:cry_code}редкости", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Не думай.", + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Навсегда{} удалить", + "{C:cry_code}выбранный{} предмет из магазина", + "{C:inactive,s:0.8}Предмет больше не появится в этом забеге", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "Уменьшить цены {C:cry_code}Наполовину{}", + "в текущем магазине", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "{C:cry_code}Следущая{} рука", + "считается", + "{C:cry_code}выбранной{} покерной рукой", + "{C:inactive,s:0.8}Секретные покерные руки", + "{C:inactive,s:0.8}должны быть найдены, чтобы они считались", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Выбери двух джокеров", + "которые станут {C:cry_code}Соединены", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Добавляет {C:dark_edition}Глючный{} ко всем", "картам {C:cry_code}в руке" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Соединить {C:cry_code}Расходник", + "с выбранной {C:cry_code}игральной картой", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Удваивает{} все значения", + "выбранного {C:cry_code}Джокера{} до", + "конца раунда", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Следующий блайнд", + "даёт {C:cry_code}X#1#{} процентов ", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Следующий {C:cry_code}Набор{} имеет на", + "{C:cry_code}#1#{} дополнительную карту и", + "{C:cry_code}#1#{} дополнительный выбор", + "{C:inactive}(Сейчас: {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Даёт {C:blue}Рук{} и {C:red}Сбросов{},", + "возвращает {C:cry_code}все{} карты в карты", + "и вытянуть {C:cry_code}новую{} руку", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Ставит {C:cry_code}состояние игры{} на", + "начало {C:cry_code}Анте{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Уничтожить {C:cry_code}выбранного{} Джокера,", + "создаёт {C:cry_code}Тэг доработки{} с", + "{C:cry_code}улучшенной{} версии", + "{C:inactive,s:0.8}Следует порядку как в коллекции", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Посетить {C:cry_code}магазин", + "посреди {C:cry_code}Блайнда", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Выбери джокера", + "или игровую карту", + "чтобы те стали {C:cry_code}Подкрученными", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Закончить текущий не-босс {C:cry_code}Блайнд{}", "{C:cry_code}без{} подсчета денег" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Создать {C:cry_code}Глючного", + "Съедобного Джокера", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Конвертировать {C:cry_code}#1#{} выбранных карт", + "в {C:cry_code}выбранный{} ранг", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Астральный", + text = { + "{X:dark_edition,C:white}^#1#{} Множ.", + }, + }, + e_cry_blur = { + name = "Размытый", + text = { + "{C:attention}Перезапустить{} эту", + "карту {C:attention}1{} раз", + "{C:green}#1# к #2#{} шанс", + "перезапустить ещё{C:attention}#3#{}", + "раза", + }, + }, + e_cry_double_sided = { + name = "Двухсторонний", + text = { + "Эта карта может быть", + "{C:attention}перевёрнута{} чтобы", + "показать другую карту", + }, + }, + e_cry_glass = { + name = "Хрупкий", + label = "Хрупкий", + text = { + "{C:white,X:mult} X#3# {} Множ", + "{C:green}#1# к #2#{} шанс, чтобы эта", + "карта не {C:red}уничтожилась", + "при срабатывании", + }, + }, + e_cry_glitched = { + name = "Глючный", + text = { + "Все значения на карте", + "{C:dark_edition}случайны{}", + "между {C:attention}X0.1{} и {C:attention}X10{}", + "{C:inactive}(если возможно){}", + }, + }, + e_cry_gold = { + name = "Золотой", + label = "Золотой", + text = { + "Заработать {C:money}$#1#{} при использовании", + "или срабатывании", + }, + }, + e_cry_m = { + name = "Весёлый", + text = { + "{C:mult}+#1#{} Множ", + "Эта карта чуствует", + "скорее всего {C:attention}веселье{}", + }, + }, + e_cry_mosaic = { + name = "Мозаичный", + text = { + "{X:chips,C:white} X#1# {} Фишек", + }, + }, + e_cry_noisy = { + name = "Шумный", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Перенасыщенный", + text = { + "Все значения", + "на карте", + "{C:attention}удвоенны{}", + "{C:inactive}(Если возможно)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Эхо-Карта", + text = { + "{C:green}#2# к #3#{} шанс", + "{C:attention}перезапустить{} #1# дополнительный", + "раз когда сыграно", + }, + }, + }, + Joker = { + j_cry_altgoogol = { + name = "Ностальгическая Гугол Игровая Карта", + text = { + "Продайте эту карту, чтобы создать", + "{C:attention}2{} копии крайнего левого {C:attention}Джокера{}", + "{C:inactive,s:0.8}Не копирует Ностальгические Гугол Игровые Карты{}", + }, + }, + j_cry_antennastoheaven = { + name = " ...КАК АНТЕННЫ ДО НЕБЕС", + text = { + "Этот джокер даёт", + "{X:chips,C:white} X#1# {} Фишек при подсчете", + "разыгранных {C:attention}7{} или {C:attention}4{}", + "{C:inactive}(Сейчас: {X:chips,C:white}X#2# {C:inactive} Фишек)" + }, + }, + j_cry_apjoker = { + name = "ББ Джокер", + text = { "{X:mult,C:white} X#1# {} Множ. против {C:attention}Босс-Блайндов{}" }, + }, + j_cry_big_cube = { + name = "Большой Куб", + text = { + "{X:chips,C:white} X#1# {} Фишек", + }, + }, + j_cry_biggestm = { + name = "Огромный", + text = { + "{X:mult,C:white} X#1# {} Множ. до конца", + "раунда если {C:attention}покерная рука{}", + "это {C:attention}#2#{}", + "{C:inactive}(Сейчас: Множ {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}просто широкая кость.", + }, + }, + j_cry_blender = { + name = "Блендер", + text = { + "Создать {C:attention}случайный{}", + "Расходник когда", + "{C:cry_code}Код{} карта использована", + "{C:inactive}(Должно быть место){}", + }, + }, + j_cry_blurred = { + name = "Мыльный джокер", + text = { + "{C:blue}+#1#{} рука(и) при", + "выборе {C:attention}блайнда{}", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Each {C:attention}Joker{} gives {C:chips}+#1#{} Chips", + "Increase amount by {C:chips}+#2#{} if", + "{C:attention} poker hand{} is a {C:attention}#3#{}", + "{C:inactive,s:0.8}Jolly Jokers give{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips instead{}", + }, + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Bonus{} card to increase", + "{C:attention}Joker{} or {C:attention}Consumable slots", + "by {C:dark_edition}1{} when scored", + "{C:red}Works twice per round", + "{C:inactive,s:0.8}(Equal chance for each){}", + }, + }, + j_cry_booster = { + name = "Бустер Джокер", + text = { + "{C:attention}+#1#{}Слот под Бустерные джокеры", + "В магазине", + }, + }, + j_cry_boredom = { + name = "Скука", + text = { + "{C:green}#1# к #2#{} шанс", + "{C:attention}перезапустить{} каждого {C:attention}Джокера{}", + "или {C:attention}Игровую карту{}", + "{C:inactive,s:0.8}Не влияет на скуки{}", + }, + }, + j_cry_bubblem = { + name = "Пузырный М", + text = { + "Создаёт {C:dark_edition}Фольгового {C:attention}Весёлого Джокера{}", + "если сыгранная рука содержит {C:attention}#1#{}", + "{C:red,E:2}само уничтожается{}", + }, + }, + j_cry_busdriver = { + name = "Водитель автобуса", + text = { + "{C:green}#1# к #3#{} шанс", + "на {C:mult}+#2#{} Множ.", + "{C:green}1 к 4{} шанс", + "на {C:mult}-#2#{} Множ.", + }, + }, + j_cry_canvas = { + name = "Холст", + text = { + "{C:attention}Перезапускает{} ВСЕХ {C:attention}Джокеров{} слева", + "за {C:attention}каждого{} не-{C:blue}Обычного{C:attention} Джокера{}", + "справа этого джокера", + }, + }, + j_cry_caramel = { + name = "Карамель", + text = { + "Каждая сыгранная карта даёт", + "{X:mult,C:white}X#1#{} Множ. при подсчете", + "для следующих {C:attention}#2#{} раундов", + }, + }, + j_cry_chad = { + name = "Чад", + text = { + "Перезапуск {C:attention}крайнего левого{} Джокера", + "{C:attention}#1#{} дополнительный(ых) раз(а)", + }, + }, + j_cry_chili_pepper = { + name = "Перец чили", + text = { + "Этот джокер даёт {X:mult,C:white} X#2# {} Множ.", + "в конце раунда,", + "{C:red,E:2}самоуничтожается{} через {C:attention}#3#{} раунда", + "{C:inactive}(Сейчас:{} Множ.{X:mult,C:white} X#1# {} {C:inactive}){}", + }, + }, + j_cry_circulus_pistoris = { + name = "Циркуль Писторис", + text = { + "{X:dark_edition,C:white}^#1#{} Фишек, {X:dark_edition,C:white}^#1#{} Множ.", + "если {C:attention}ровно{} #2#", + "руки остаются", + }, + }, + j_cry_circus = { + name = "Цирк", + text = { + "Каждый {C:red}Редкий{} даёт {X:mult,C:white} X#1# {} Множ.", + "Каждый {C:cry_epic}Эпический{} даёт {X:mult,C:white} X#2# {} Множ.", + "Каждый {C:legendary}Легендарный{} даёт {X:mult,C:white} X#3# {} Множ.", + "Каждый {C:cry_exotic}Экзотический{} даёт {X:mult,C:white} X#4# {} Множ.", + }, + }, + j_cry_CodeJoker = { + name = "Код Джокер", + text = { + "Создаёт {C:dark_edition}Негативную{}", + "{C:cry_code}Код карту{} при", + "выборе {C:attention}Блайнда{}", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Earn between", + "{C:money}$#1#{} and {C:money}$#2#{} for", + "each Joker {C:attention}sold{}", + }, + }, + j_cry_compound_interest = { + name = "Compound Interest", + text = { + "Earn {C:money}#1#%{} of total money", + "at end of round,", + "increases by {C:money}#2#%{} per", + "consecutive payout", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "When a {C:cry_code}Code{} card is used,", + "{C:green}#1# in #2#{} chance to add a copy", + "to your consumable area", + "{C:inactive}(Must have room)", + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "per {C:attention}reroll{} in the shop", + "{C:green}All rerolls are free{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Chain", + text = { + "Sell this card to", + "add {C:money}$#1#{} of {C:attention}sell value{}", + "to every {C:attention}Joker{} card", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob = { + name = "Sob", + text = { + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}run...{}", + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}hide...{}", + "{C:dark_edition,E:1}you cannot escape...{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "for each card {C:attention}purchased{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Cut", + text = { + "This Joker destroys", + "a random {C:cry_code}Code{} card", + "and gains {X:mult,C:white} X#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Delirious Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "Doodle M", + text = { + "Create 2 {C:dark_edition}Negative{} {C:attention}consumables{}", + "when {C:attention}Blind{} is selected", + "Create 1 more {C:attention}consumable", + "for each {C:attention}Jolly Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Scale", + text = { + "Scaling {C:attention}Jokers{}", + "scale {C:attention}quadratically", + "{C:inactive,s:0.8}(ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grows by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult for", + "each played, {C:attention}nonscoring{} {V:1}#2#{} card,", + "suit changes every round", + "{C:inactive}(Currently {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubious Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_duos = { + name = "The Duos", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Every {C:attention}Joker{} gives", + "{X:dark_edition,C:white}^#1#{} Mult" + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Draw {C:green}full deck{} to hand", + "when {C:attention}Blind{} is selected", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "When a {C:attention}Tag{} is acquired,", + "create {C:attention}#1#{} copies of it", + "and {C:attention}increase{} the number of", + "copies by {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Jokers appear using the", + "order from the {C:attention}Collection{}", + "Create {C:attention}#1#{} {C:dark_edition}Negative{} Joker(s)", + "when hand is played", + "{C:cry_exotic,s:0.8}Exotic {C:inactive,s:0.8}or better Jokers cannot appear", + "{s:0.8}Last Joker Generated: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Eternal Flame", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "for each card {C:attention}sold{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographic{} cards", + "each give {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "when {X:red,C:white} XMult {} is triggered", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult if", + "played cards are scored", + "{C:attention}#2#{} or fewer times", + }, + }, + j_cry_filler = { + name = "The Filler", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fractal Fingers", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + j_cry_flip_side = { + name = "On the Flip Side", + text = { + "{C:dark_edition}Double-Sided{} Jokers use", + "their back side for effects", + "{C:attention}Retrigger{} all {C:dark_edition}Double-Sided{} Jokers" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}self destructs{} in {C:attention}#2#{} round(s)", + "Increases by {C:attention}#3#{} round when", + "{C:attention}Jolly Joker{} is {C:attention}sold{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foxy = { + name = "Foxy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "if hand played is {C:attention}not{}", + "most played {C:attention}poker hand{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Earn {C:money}$#1#{} if {C:attention}played hand{}", + "contains an {C:attention}Ace{} and a {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} all values", + "of leftmost {C:attention}Joker", + "at end of round", + }, + }, + j_cry_giggly = { + name = "Absurd Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Earn {C:money}#1#%{} of total", + "money at end of round", + "Payout increases by {C:money}#2#%{}", + "when each played {C:attention}Gold{}", + "card is scored", + }, + }, + j_cry_googol_play = { + name = "Googol Play Card", + text = { + "{C:green}#1# in #2#{} chance for", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Create a random {C:attention}Joker{}", + "at end of round", + "Sell this card to", + "create a random {C:attention}Joker{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_happyhouse = { + name = "Happy House", + text = { + "{X:dark_edition,C:white}^#1#{} Mult only after", + "playing {C:attention}114{} hands{}", + "{C:inactive}(Currently #2#/114){}", + "{C:inactive,s:0.8}There is no place like home!{}", + }, + }, + j_cry_home = { + name = "The Home", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Earn {C:money}$#1#{} when", + "using a {C:attention}consumable{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Retrigger all cards played", + "{C:attention}#2#{} time(s),", + "each played card gives", + "{X:mult,C:white} X#1# {} Mult when scored", + }, + }, + j_cry_jimball = { + name = "Jimball", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "per {C:attention}consecutive{} hand played", + "while playing your", + "most played {C:attention}poker hand", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Jolly Joker?", + text = { + "Create a {C:dark_edition}Jolly{} Joker", + "when a Joker is {C:attention}sold{}", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{}", + "when a {C:attention}Type Mult{} or", + "{C:attention}Type Chips{} Joker is sold", + }, + }, + j_cry_kooky = { + name = "Kooky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty the Clown", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when", + "each played {C:attention}card{} is scored", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoscope", + text = { + "Add {C:dark_edition}Polychrome{} to", + "a random {C:attention}Joker{} when", + "{C:attention}Boss Blind{} is defeated", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Each played {C:attention}7{} or {C:attention}2{}", + "gives {X:mult,C:white}X#1#{} Mult when scored", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Give future copies of", + "this Joker {X:mult,C:white}X#1#{} Mult", + "at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Retrigger{} all Jokers", + "once for each {C:attention}Jolly{}", + "{C:attention}Joker{} sold this round", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Retrigger(s)){}", + "{C:inactive,s:0.8}There wasn't enough room...{}", + }, + }, + j_cry_lucky_joker = { + name = "Lucky Joker", + text = { + "Earn {C:money}$#1#{} every time a", + "{C:attention}Lucky{} card {C:green}successfully{}", + "triggers", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "All Jokers give", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "when {C:attention}Jolly Joker{} is sold", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:attention}Jolly Joker{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_macabre = { + name = "Macabre Joker", + text = { + "When {C:attention}Blind{} is selected,", + "destroys each {C:attention}Joker{} except", + "{C:legendary}M-Jokers{} and {C:attention}Jolly Jokers{}", + "and create 1 {C:attention}Jolly Joker{}", + "for each destroyed card", + }, + }, + j_cry_magnet = { + name = "Fridge Magnet", + text = { + "Earn {C:money}$#1#{} at end of round", + "This earns {X:money,C:white} X#2# {} if there are", + "{C:attention}#3#{} or fewer {C:attention}Joker{} cards", + }, + }, + j_cry_manic = { + name = "Manic Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Retrigger all Jokers", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_maximized = { + name = "Maximized", + text = { + "All {C:attention}face{} cards", + "are considered {C:attention}Kings{},", + "all {C:attention}numbered{} cards", + "are considered {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Labyrinth", + text = { + "All hands are considered the", + "{C:attention}first hand{} of each round,", + "all discards are considered the", + "{C:attention}first discard{} of each round", + }, + }, + j_cry_Megg = { + name = "Megg", + text = { + "Sell this card to create", + "{C:attention}#2#{} Jolly #3#, increase", + "by {C:attention}#1#{} at end of round", + }, + }, + j_cry_membershipcard = { + name = "Membership Card", + text = { + "{X:mult,C:white}X#1#{} Mult for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Old Membership Card", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteor Shower", + text = { + "{C:dark_edition}Foil{} cards each", + "give {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by", + "{C:money}$#1#{} for each {C:attention}Jolly Joker{}", + "or {C:legendary}M Joker at", + "end of round", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if no {C:attention}discards{} were", + "used this round", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Monkey Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and permanently add {C:attention}ten times{}", + "its sell value to this {C:chips}Chips{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{} when", + "a card with an {C:attention}Edition{} is sold", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Create an {C:legendary}M Joker{} at end of round", + "Each {C:attention}Jolly Joker{} or {C:legendary}M Joker", + "gives {X:dark_edition,C:white}^#1#{} Mult", + "Increase amount by {X:dark_edition,C:white}^#2#{}", + "when a {C:attention}Jolly Joker{} is {C:attention}sold", + "{C:inactive,s:0.8}(Tredecim excluded)", + }, + }, + j_cry_mstack = { + name = "M Stack", + text = { + "Retrigger all cards played", + "once for every", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jolly Jokers{} sold", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} retriggers){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Mult{} card to create", + "a {C:spectral}Cryptid{} card when scored", + "{C:inactive}(Must have room)", + }, + }, + j_cry_negative = { + name = "Negative Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips if played hand", + "contains a {C:attention}6{} and a {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Night", + text = { + "{X:dark_edition,C:white}^#1#{} Mult on final", + "hand of round", + "{E:2,C:red}self destructs{} on", + "final hand of round", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Retrigger each played {C:attention}7{}", + "{C:attention:}#1#{} additional time(s)", + }, + }, + j_cry_notebook = { + name = "Notebook", + text = { + "{C:green} #1# in #2#{} chance to gain {C:dark_edition}+1{} Joker", + "slot per {C:attention}reroll{} in the shop", + "{C:green}Always triggers{} if there are", + "{C:attention}#5#{} or more {C:attention}Jolly Jokers{}", + "{C:red}Works once per round{}", + "{C:inactive}(Currently {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Number Blocks", + text = { + "Earn {C:money}$#1#{} at end of round", + "Increase payout by {C:money}$#2#{}", + "for each {C:attention}#3#{} held in hand,", + "rank changes every round", + }, + }, + j_cry_nuts = { + name = "The Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_oldblueprint = { + name = "Old Blueprint", + text = { + "Copies ability of", + "{C:attention}Joker{} to the right", + "{C:green}#1# in #2#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + j_cry_oldcandy = { + name = "Nostalgic Candy", + text = { + "Sell this card to", + "permanently gain", + "{C:attention}+#1#{} hand size", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgic Invisible Joker", + text = { + "{C:attention}Duplicate{} a random", + "{C:attention}Joker{} every {C:attention}4", + "Joker cards sold", + "{s:0.8}Nostalgic Invisible Joker Excluded{}", + "{C:inactive}(Currently #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panopticon", + text = { + "All hands are considered the", + "{C:attention}last hand{} of each round", -- +$4 + }, + }, + j_cry_pickle = { + name = "Pickle", + text = { + "When {C:attention}Blind{} is skipped, create", + "{C:attention}#1#{} Tags, reduced by", + "{C:red}#2#{} when {C:attention}Blind{} is selected", + }, + }, + j_cry_pirate_dagger = { + name = "Pirate Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "and gain {C:attention}one-fourth{} of", + "its sell value as {X:chips,C:white} XChips {}", + "{C:inactive}(Currently {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + j_cry_pot_of_jokes = { + name = "Pot of Jokes", + text = { + "{C:attention}#1#{} hand size,", + "increases by", + "{C:blue}#2#{} every round", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "if all cards in played hand are", + "{C:attention}Aces{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, or {C:attention}7s{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "If {C:attention}poker hand{} is a", + "{C:attention}Royal Flush{}, destroy scored", + "{C:attention}Queen{} and create a", + "{C:dark_edition}Negative {}{C:red}Rare{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "The Quintet", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Red Bloon", + text = { + "Earn {C:money}$#1#{} in {C:attention}#2#{} round#3#", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante when", + "{C:money}$#2#{} {C:inactive}($#3#){} spent", + "{s:0.8}Requirements increase", + "{C:attention,s:0.8}exponentially{s:0.8} per use", + "{C:money,s:0.8}Next increase: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "When a {C:attention}Joker{} is sold,", + "add its effects to", + "every other Joker", + "{C:inactive,s:0.8}Does not affect other Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fill all empty Joker slots {C:inactive}(Max 100){}", + "with {C:dark_edition}Holographic{} {C:attention}Jolly Jokers{} if", + "{C:attention}discarded poker hand{} is a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Randomize abilities each {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Create an {C:green}Uncommon{} Joker", + "and 3 {C:attention}Jolly Jokers{} when", + "a {C:spectral}Spectral{} card is used", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sapling", + text = { + "After scoring {C:attention}#2#{} {C:inactive}[#1#]{} Enhanced", + "cards, sell this card to", + "create an {C:cry_epic}Epic{} {C:attention}Joker{}", + "{C:inactive,s:0.8}Will create a {C:red,s:0.8}Rare{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}if {C:cry_epic,s:0.8}Epic{} {C:inactive,s:0.8}Jokers are disabled{}", + }, + }, + j_cry_savvy = { + name = "Savvy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Scaling {C:attention}Jokers{} scale", + "as a degree-{C:attention}#1#{} polynomial", + "raise degree by {C:attention}#2#{}", + "at end of round", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluded)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Tile", + text = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Jolly {C:green}Uncommon{} Joker", + "when hand is played", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Add a {C:attention}random seal{} to each card", + "scored on {C:attention}final hand{} of round", + }, + }, + j_cry_shrewd = { + name = "Shrewd Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Silly Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Tiny", + text = { + "Create a {C:cry_jolly}Double M", + "tag if {C:attention}poker hand{}", + "is a {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker slot", + "{C:attention}+#1#{} Booster Pack slot", + "{C:attention}+#1#{} hand size", + "{C:attention}+#1#{} consumable slot", + "{C:attention}+#1#{} card in shop", + }, + }, + j_cry_spaceglobe = { + name = "Celestial Globe", + text = { + "This Joker gains {X:chips,C:white}X#2#{} Chips", + "if {C:attention}poker hand{} is a {C:attention}#3#{},", + "Hand changes after increase{}", + "{C:inactive}(Currently{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Creates a {C:dark_edition}Negative{} copy", + "of a random {C:attention}Joker{}", + "at the end of the {C:attention}shop", + "{C:inactive,s:0.8}Does not copy other Speculo{}", + }, + }, + j_cry_stardust = { + name = "Stardust", + text = { + "{C:dark_edition}Polychrome{} cards", + "each give {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "This Joker destroys a", + "random {C:planet}Planet{} card", + "and gains {X:dark_edition,C:white} ^#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_subtle = { + name = "Subtle Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Earn {C:money}$#3#{} at", + "end of round", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "At end of round, create", + "a {C:attention}copy{} of a random", + "card {C:attention}held in hand{},", + "destroy all others", + "{C:attention,s:0.8}Kings{s:0.8} of {C:hearts,s:0.8}Hearts{s:0.8} are prioritized", + }, + }, + j_cry_swarm = { + name = "The Swarm", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Catalyst", + text = { + "Balances {C:chips}Chips{} and {C:mult}Mult{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + "Earn {C:money}$#2#{} at end of round", + }, + }, + j_cry_translucent = { + name = "Translucent Joker", + text = { + "Sell this card to create", + "a {C:attention}Banana Perishable{} copy", + "of a random {C:attention}Joker{}", + "{s:0.8,C:inactive}(Copy bypasses perish compat)", + }, + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Triplet Rhythm", + text = { + "{X:mult,C:white} X#1# {} Mult if scoring hand", + "contains {C:attention}exactly{} three {C:attention}3s", + }, + }, + j_cry_unity = { + name = "The Unity", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Poker hands{} gain", + "{X:red,C:white} X#1# {} Mult and {X:blue,C:white} X#1# {} Chips", + "when leveled up", + }, + }, + j_cry_unjust_dagger = { + name = "Unjust Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and gain {C:attention}one-fifth{} of", + "its sell value as {X:mult,C:white} XMult {}", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "This Joker gains {C:money}$#1#{} of {C:attention}sell value{}", + "if {C:attention}poker hand{} contains a {C:attention}#2#{}", + "Sell this card to create a", + "{C:dark_edition}Polychrome{} {C:attention}Jolly Joker{} for", + "every {C:money}$4{} of {C:attention}sell value{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Wacky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "All Jokers give", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "All Jokers give", + "{C:money}$#1#{} when triggered", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "This Joker gains", + "{C:mult}+#2#{} Mult when each played", + "{C:attention}Ace{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "is scored", + "{C:inactive}(Currently {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Retrigger each played {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} additional time(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Wheel of Hope", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when failing", + "a {C:attention}Wheel of Fortune{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if {C:attention}played hand{} contains a", + "{C:attention}2{} and {C:attention}7{} of different suits", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutron Star", + text = { + "Upgrade a random", + "poker hand by", + "{C:attention}1{} level for each", + "{C:attention}Neutron Star{} used", + "in this run", + "{C:inactive}(Currently{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# in #2#{} chance to", + "upgrade every", + "{C:legendary,E:1}poker hand{}", + "by {C:attention}1{} level", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "CCD Sleeve", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Conveyor Sleeve", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Critical Sleeve", + text = { + "After each hand played,", + "{C:green}1 in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}1 in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Encoded Sleeve", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balanced Sleeve", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}+2 Shop Slots", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unlimited Sleeve", + text = { + "You can select {C:attention}any", + "number of cards", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Misprinted Sleeve", + text = { + "Values of cards", + "are {C:attention}randomized", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Redeemed Sleeve", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Wormhole Sleeve", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + }, + Spectral = { + c_cry_analog = { + name = "Analog", + text = { + "Create {C:attention}#1#{} copies of a", + "random {C:attention}Joker{}, destroy", + "all other Jokers, {C:attention}+#2#{} Ante", + }, + }, + c_cry_gateway = { + name = "Gateway", + text = { + "Create a random", + "{C:cry_exotic,E:1}Exotic{C:attention} Joker{}, destroy", + "all other Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Lock", + text = { + "Remove {C:red}all{} stickers", + "from {C:red}all{} Jokers,", + "then apply {C:purple,E:1}Eternal{}", + "to a random {C:attention}Joker{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Create a card", + "of {C:cry_code}your choice", + "{C:inactive,s:0.8}(Exotic Jokers #1#excluded)", + }, + }, + c_cry_replica = { + name = "Replica", + text = { + "Convert all cards", + "held in hand", + "to a {C:attention}random{}", + "card held in hand", + }, + }, + c_cry_source = { + name = "Source", + text = { + "Add a {C:cry_code}Green Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_summoning = { + name = "Summoning", + text = { + "Create a random", + "{C:cry_epic}Epic{} {C:joker}Joker{}, destroy", + "one random {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Trade", + text = { + "{C:attention}Lose{} a random Voucher,", + "gain {C:attention}2{} random Vouchers", + }, + }, + c_cry_typhoon = { + name = "Typhoon", + text = { + "Add an {C:cry_azure}Azure Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_vacuum = { + name = "Vacuum", + text = { + "Removes {C:red}all {C:green}modifications{}", + "from {C:red}all{} cards held in hand,", + "Earn {C:money}$#1#{} per {C:green}modification{} removed", + "{C:inactive,s:0.7}(ex. Enhancements, Seals, Editions)", + }, + }, + c_cry_white_hole = { + name = "White Hole", + text = { + "{C:attention}Remove{} all hand levels,", + "upgrade {C:legendary,E:1}most played{} poker hand", + "by {C:attention}3{} for each removed level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pink Stake", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brown Stake", + colour = "Brown", + text = { + "All {C:attention}stickers{} are compatible", + "with each other", + }, + }, + stake_cry_yellow = { + name = "Yellow Stake", + colour = "Yellow", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Jade Stake", + colour = "Jade", + text = { + "Cards can be drawn {C:attention}face down{}", + }, + }, + stake_cry_cyan = { + name = "Cyan Stake", + colour = "Cyan", + text = { + "{C:green}Uncommon{} and {C:red}Rare{} Jokers are", + "less likely to appear", + }, + }, + stake_cry_gray = { + name = "Gray Stake", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Crimson Stake", + colour = "Crimson", + text = { + "Vouchers restock on {C:attention}even{} Antes", + }, + }, + stake_cry_diamond = { + name = "Diamond Stake", + colour = "Diamond", + text = { + "Must beat Ante {C:attention}10{} to win", + }, + }, + stake_cry_amber = { + name = "Amber Stake", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Pack slot", + }, + }, + stake_cry_bronze = { + name = "Bronze Stake", + colour = "Bronze", + text = { + "Vouchers are {C:attention}50%{} more expensive", + }, + }, + stake_cry_quartz = { + name = "Quartz Stake", + colour = "Quartz", + text = { + "Jokers can be {C:attention}Pinned{}", + "{s:0.8,C:inactive}(Stays pinned to the leftmost position){}", + }, + }, + stake_cry_ruby = { + name = "Ruby Stake", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds can become", + "{C:attention}Boss{} Blinds", + }, + }, + stake_cry_glass = { + name = "Glass Stake", + colour = "Glass", + text = { + "Cards can {C:attention}shatter{} when scored", + }, + }, + stake_cry_sapphire = { + name = "Sapphire Stake", + colour = "Sapphire", + text = { + "Lose {C:attention}25%{} of current money", + "at end of Ante", + "{s:0.8,C:inactive}(Up to $10){}", + }, + }, + stake_cry_emerald = { + name = "Emerald Stake", + colour = "Emerald", + text = { + "Cards, packs, and vouchers", + "can be {C:attention}face down{}", + "{s:0.8,C:inactive}(Unable to be viewed until purchased){}", + }, + }, + stake_cry_platinum = { + name = "Platinum Stake", + colour = "Platinum", + text = { + "Small Blinds are {C:attention}removed{}", + }, + }, + stake_cry_twilight = { + name = "Twilight Stake", + colour = "Twilight", + text = { + "Cards can be {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 in 10 chance of being destroyed each round){}", + }, + }, + stake_cry_verdant = { + name = "Verdant Stake", + colour = "Verdant", + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Ember Stake", + colour = "Ember", + text = { + "All items give no money when sold", + }, + }, + stake_cry_dawn = { + name = "Dawn Stake", + colour = "Dawn", + text = { + "Tarots and Spectrals target {C:attention}1", + "fewer card", + "{s:0.8,C:inactive}(Minimum of 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizon Stake", + colour = "Horizon", + text = { + "When blind selected, add a", + "{C:attention}random card{} to deck", + }, + }, + stake_cry_blossom = { + name = "Blossom Stake", + colour = "Blossom", + text = { + "{C:attention}Final{} Boss Blinds can appear", + "in {C:attention}any{} Ante", + }, + }, + stake_cry_azure = { + name = "Azure Stake", + colour = "Azure", + text = { + "Values on Jokers are reduced", + "by {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Ascendant Stake", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop slot", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Banana Tag", + text = { + "Creates {C:attention}#1#", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_bettertop_up = { + name = "Better Top-up Tag", + text = { + "Creates up to {C:attention}#1#", + "{C:green}Uncommon{} Jokers", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_better_voucher = { + name = "Golden Voucher Tag", + text = { + "Adds one Tier {C:attention}#1#{} Voucher", + "to the next shop", + }, + }, + tag_cry_blur = { + name = "Blurred Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Blurred{}", + }, + }, + tag_cry_booster = { + name = "Booster Tag", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:attention}double{} cards and", + "{C:attention}double{} choices", + }, + }, + tag_cry_bundle = { + name = "Bundle Tag", + text = { + "Create a {C:attention}Standard Tag{}, {C:tarot}Charm Tag{},", + "{C:attention}Buffoon Tag{}, and {C:planet}Meteor Tag", + }, + }, + tag_cry_cat = { + name = "Cat Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Console Tag", + text = { + "Gives a free", + "{C:cry_code}Program Pack", + }, + }, + tag_cry_double_m = { + name = "Double M Tag", + text = { + "Shop has a", + "{C:dark_edition}Jolly {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Empowered Tag", + text = { + "Gives a free {C:spectral}Spectral Pack", + "with {C:legendary,E:1}The Soul{} and {C:cry_exotic,E:1}Gateway{}", + }, + }, + tag_cry_epic = { + name = "Epic Tag", + text = { + "Shop has a half-price", + "{C:cry_epic}Epic Joker", + }, + }, + tag_cry_gambler = { + name = "Gambler's Tag", + text = { + "{C:green}#1# in #2#{} chance to create", + "an {C:cry_exotic,E:1}Empowered Tag", + }, + }, + tag_cry_glass = { + name = "Fragile Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Fragile{}", + }, + }, + tag_cry_glitched = { + name = "Glitched Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Glitched{}", + }, + }, + tag_cry_gold = { + name = "Golden Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Golden{}", + }, + }, + tag_cry_gourmand = { + name = "Gourmand Tag", + text = { + "Shop has a free", + "{C:attention}Food Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gives a free", + "{C:cry_ascendant}Meme Pack", + }, + }, + tag_cry_m = { + name = "Jolly Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Jolly{}", + }, + }, + tag_cry_memory = { + name = "Memory Tag", + text = { + "Create {C:attention}#1#{} copies of", + "the last {C:attention}Tag{} used", + "during this run", + "{s:0.8,C:inactive}Copying Tags excluded", + "{s:0.8,C:inactive}Currently: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Mosaic{}", + }, + }, + tag_cry_oversat = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Oversaturated{}", + }, + }, + tag_cry_quadruple = { + name = "Quadruple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_quintuple = { + name = "Quintuple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_rework = { + name = "Rework Tag", + text = { + "Shop has a(n)", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schematic Tag", + text = { + "Shop has a", + "{C:attention}Brainstorm", + }, + }, + tag_cry_scope = { + name = "Scope Tag", + text = { + "{C:attention}+#1# {C:blue}hands{} and", + "{C:red}discards{} next round", + }, + }, + tag_cry_triple = { + name = "Triple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "The Automaton", + text = { + "Creates up to {C:attention}#1#", + "random {C:cry_code}Code{} card", + "{C:inactive}(Must have room)", + }, + }, + c_cry_eclipse = { + name = "The Eclipse", + text = { + "Enhances {C:attention}#1#{} selected card", + "into an {C:attention}Echo Card", + }, + }, + c_cry_meld = { + name = "Meld", + text = { + "Select a {C:attention}Joker{} or", + "{C:attention}playing card{} to", + "become {C:dark_edition}Double-Sided", + }, + }, + c_cry_theblessing = { + name = "The Blessing", + text = { + "Creates {C:attention}1{}", + "random {C:attention}consumable{}", + "{C:inactive}(Must have room){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglyph", + text = { + "Set Ante to {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Blank Canvas", + text = { + "{C:attention}+#1#{} hand size", + }, + }, + v_cry_clone_machine = { + name = "Clone Machine", + text = { + "Double Tags become", + "{C:attention}Quintuple Tags{} and", + "are {C:attention}4X{} as common", + }, + }, + v_cry_command_prompt = { + name = "Command Prompt", + text = { + "{C:cry_code}Code{} cards", + "can appear", + "in the {C:attention}shop{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Double Tags become", + "{C:attention}Triple Tags{} and are", + "{C:attention}2X{} as common", + }, + }, + v_cry_curate = { + name = "Curate", + text = { + "All cards", + "appear with", + "an {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Dexterity", + text = { + "Permanently", + "gain {C:blue}+#1#{} hand(s)", + "each round", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "After every round,", + "{X:dark_edition,C:white} X1.5 {} to all values", + "on the back of", + "{C:dark_edition}Double-Sided{} cards" + }, + }, + v_cry_double_slit = { + name = "Double Slit", + text = { + "{C:attention}Meld{} can appear", + "in the shop and", + "Arcana Packs", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "{C:dark_edition}Double-Sided{} cards appear", + "{C:attention}4X{} more frequently", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker slot(s)", + }, + }, + v_cry_massproduct = { + name = "Mass Production", + text = { + "All cards and packs", + "in shop cost {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Raise the cap on", + "interest earned in", + "each round to {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multistock", + text = { + "{C:attention}+#1#{} card slot(s) and", + "{C:attention}+#1#{} booster pack slot(s)", + "available in shop", + }, + }, + v_cry_pacclimator = { + name = "Planet Acclimator", + text = { + "{C:planet}Planet{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:planet}Planet{}", + "cards are {C:green}free{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Retrigger{} all M Jokers", + "once for every Pair", + "{C:attention}contained{} in played hand", + }, + }, + v_cry_pairing = { + name = "Pairing", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand is a {C:attention}Pair", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computing", + text = { + "{C:cry_code}Code{} cards can spawn", + "with {C:dark_edition}Negative{} edition", + }, + }, + v_cry_repair_man = { + name = "Repair Man", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand contains a {C:attention}Pair", + }, + }, + v_cry_rerollexchange = { + name = "Reroll Exchange", + text = { + "All rerolls", + "cost {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satellite Uplink", + text = { + "{C:cry_code}Code{} cards may", + "appear in any of", + "the {C:attention}Celestial Packs{}", + }, + }, + v_cry_scope = { + name = "Galactic Scope", + text = { + "Create the {C:planet}Planet", + "card for played", + "{C:attention}poker hand{}", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Tarot Acclimator", + text = { + "{C:tarot}Tarot{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:tarot}Tarot{}", + "cards are {C:green}free{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Printer", + text = { + "Double Tags become", + "{C:attention}Quadruple Tags{} and", + "are {C:attention}3X{} as common", + }, + }, + v_cry_threers = { + name = "The 3 Rs", + text = { + "Permanently", + "gain {C:red}+#1#{} discard(s)", + "each round", + }, + }, + v_cry_stickyhand = { + name = "Sticky Hand", + text = { + "{C:attention}+#1#{} card", + "selection limit", + }, + }, + v_cry_grapplinghook = { + name = "Grappling Hook", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperspace Tether", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "destroyed each round", + }, + }, + cry_rigged = { + name = "Rigged", + text = { + "All {C:cry_code}listed{} probabilities", + "are {C:cry_code}guaranteed", + }, + }, + cry_hooked = { + name = "Hooked", + text = { + "When this Joker is {C:cry_code}triggered{},", + "trigger {C:cry_code}#1#", + }, + }, + food_jokers = { + name = "Food Jokers", + text = { + "{s:0.8}Gros Michel, Egg, Ice Cream, Cavendish,", + "{s:0.8}Turtle Bean, Diet Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Pickle, Chili Pepper, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eternal", + text = { + "All cards in pack", + "are {C:attention}Eternal{}", + }, + }, + cry_perishable_booster = { + name = "Perishable", + text = { + "All cards in pack", + "are {C:attention}Perishable{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "All cards in pack", + "are {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Pinned", + text = { + "All cards in pack", + "are {C:attention}Pinned{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "All cards in pack", + "are {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eternal", + text = { + "Can't be traded", + }, + }, + cry_perishable_voucher = { + name = "Perishable", + text = { + "Debuffed after", + "{C:attention}#1#{} rounds", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)", + }, + }, + cry_rental_voucher = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at", + "end of round", + }, + }, + cry_pinned_voucher = { + name = "Pinned", + text = { + "Remains in shop", + "until redeemed", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "unredeemed each round", + }, + }, + cry_perishable_consumeable = { + name = "Perishable", + text = { + "Debuffed at", + "end of round", + }, + }, + cry_rental_consumeable = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at end of", + "round, and on use", + }, + }, + cry_pinned_consumeable = { + name = "Pinned", + text = { + "Can't use other", + "non-{C:attention}Pinned{} consumables", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance to do", + "nothing on use", + }, + }, + p_cry_code_normal_1 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_normal_2 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_two = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_tbree = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + undiscovered_code = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + cry_green_seal = { + name = "Green Seal", + text = { + "Creates a {C:cry_code}Code{} card", + "when played and unscoring", + "{C:inactive}(Must have room)", + }, + }, + cry_azure_seal = { + name = "Azure Seal", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planets{} for played", + "{C:attention}poker hand{}, then", + "{C:red}destroy{} this card", + }, + }, + }, + }, + misc = { + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Legally Blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Break Infinity", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Home Realtor", + ach_cry_jokes_on_you = "Joke's on You, Pal!", + ach_cry_niw_uoy = "!niW uoY", + ach_cry_now_the_fun_begins = "Now the Fun Begins", + ach_cry_patience_virtue = "Patience is a Virtue", + ach_cry_perfectly_balanced = "Perfectly Balanced", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Traffic Jam", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "We Told You Not To", + ach_cry_what_have_you_done = "WHAT HAVE YOU DONE?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtain Blurred Blurred Joker", + ach_cry_bullet_hell = "Have 15 AP Jokers", + ach_cry_break_infinity = "Score 1.79e308 Chips in a single hand", + ach_cry_cryptid_the_cryptid = "Use Cryptid on Cryptid", + ach_cry_exodia = "Have 5 Exotic Jokers", + ach_cry_freak_house = "Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice", + ach_cry_googol_play_pass = "Rig a Googol Play Card", + ach_cry_haxxor = "Use a cheat code", + ach_cry_home_realtor = "Activate Happy House before Ante 8 (without DoE/Antimatter)", + ach_cry_jokes_on_you = "Trigger The Joke's effect on Ante 1 and win the run", + ach_cry_niw_uoy = "Reach Ante -8", + ach_cry_now_the_fun_begins = "Obtain Canvas", + ach_cry_patience_virtue = "Wait out Lavender Loop for 2 minutes before playing first hand and beat the blind", + ach_cry_perfectly_balanced = "Beat Very Fair Deck on Ascendant Stake", + ach_cry_pull_request = "Have ://COMMIT spawn the same Joker that it destroyed", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Win in 1 round", + ach_cry_used_crash = "Use ://CRASH", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Music", + cry_set_enable_features = "Select features to enable (applies on game restart):", + cry_feat_achievements = "Achievements", + ["cry_feat_antimatter deck"] = "Antimatter Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Code Cards", + ["cry_feat_misc. decks"] = "Misc. Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechanics", + ["cry_feat_enhanced decks"] = "Enhanced Decks", + ["cry_feat_epic jokers"] = "Эпические джокеры", + ["cry_feat_exotic jokers"] = "Экзотические джокеры", + ["cry_feat_m jokers"] = "М джокеры", + cry_feat_menu = "Custom Main Menu", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Misc. Jokers", + cry_feat_planets = "Planets", + cry_feat_jokerdisplay = "JokerDisplay (Does Nothing)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Sleeves", + cry_feat_spectrals = "Spectrals", + ["cry_feat_more stakes"] = "Stakes", + cry_feat_vouchers = "Vouchers", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Pack", + k_cry_meme_pack = "Meme Pack", + + cry_critical_hit_ex = "Critical Hit!", + cry_critical_miss_ex = "Critical Miss!", + + cry_debuff_oldhouse = "No Full Houses", + cry_debuff_oldarm = "Must play 4 or fewer cards", + cry_debuff_oldpillar = "No Straights", + cry_debuff_oldflint = "No Flushes", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applies abilities of all defeated bosses", + + k_code = "Code", + b_code_cards = "Code Cards", + b_pull = "PULL", + cry_hooked_ex = "Hooked!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTER RANK", + cry_code_enh = "ENTER ENHANCEMENT", + cry_code_hand = "ENTER POKER HAND", + cry_code_enter_card = "ENTER A CARD", + cry_code_apply = "APPLY", + cry_code_apply_previous = "APPLY PREVIOUS", + cry_code_exploit = "EXPLOIT", + cry_code_exploit_previous = "EXPLOIT PREVIOUS", + cry_code_create = "CREATE", + cry_code_create_previous = "CREATE PREVIOUS", + cry_code_execute = "EXECUTE", + cry_code_cancel = "CANCEL", + + b_flip = "FLIP", + b_merge = "MERGE", + + cry_again_q = "Again?", + cry_curse = "Curse", + cry_curse_ex = "Curse!", + cry_sobbing = "Help me...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Round", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "No triggers left!", + cry_unredeemed = "Unredeemed...", + cry_active = "Active", + cry_inactive = "Inactive", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic" + }, + labels = { + food_jokers = "Food Jokers", + banana = "Banana", + code = "Code", + cry_rigged = "Rigged", + cry_hooked = "Hooked", + + cry_green_seal = "Green Seal", + cry_azure_seal = "Azure Seal", + + cry_astral = "Astral", + cry_blur = "Blurred", + cry_double_sided = "Double-Sided", + cry_glass = "Fragile", + cry_glitched = "Glitched", + cry_gold = "Golden", + cry_m = "Jolly", + cry_mosaic = "Mosaic", + cry_noisy = "Noisy", + cry_oversat = "Oversaturated", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic" + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + }, + v_text = { + ch_c_cry_all_perishable = {"All Jokers are {C:eternal}Perishable{}"}, + ch_c_cry_all_rental = {"All Jokers are {C:eternal}Rental{}"}, + ch_c_cry_all_pinned = {"All Jokers are {C:eternal}Pinned{}"}, + ch_c_cry_all_banana = {"All Jokers are {C:eternal}Banana{}"}, + ch_c_all_rnj = {"All Jokers are {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"All purchasable items have all stickers"}, + ch_c_cry_rush_hour = {"All Boss Blinds are {C:attention}The Clock{} or {C:attention}Lavender Loop"}, + ch_c_cry_rush_hour_ii = {"All Blinds are {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}The Clock{} and {C:attention}Lavender Loop{} scale {C:attention}twice{} as fast"}, + ch_c_cry_no_tags = {"Skipping is {C:attention}disabled{}"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/localization/zh_CN.lua b/Cryptid/localization/zh_CN.lua new file mode 100644 index 0000000..98d8400 --- /dev/null +++ b/Cryptid/localization/zh_CN.lua @@ -0,0 +1,4549 @@ +return { + descriptions = { + Back = { + b_cry_CCD = { + name = "CCD牌组", + text = { + "每张牌也是一张{C:attention}随机{}消耗牌", + "{C:inactive,s:0.8}{C:attention,s:0.8}(Consume Card Deck){C:inactive,s:0.8}" + } + }, + b_cry_antimatter = { + name = "反物质牌组", + text = { + "拥有{C:legendary,E:1}所有牌组的{}增益效果" + } + }, + b_cry_beta = { + name = "怀旧牌组", + text = { + "{C:attention}小丑牌槽{} 和 {C:attention}消耗牌槽{}", + "{C:attention}合并", + "boss底注被替换为它们的怀旧版本" + } + }, + b_cry_blank = { + name = "空白牌组", + text = { + "{C:inactive,E:1}无作用?{}" + } + }, + b_cry_bountiful = { + name = "丰饶的露台", + text = { + "每次{C:attention}出牌{} 或 {C:attention}弃牌{}后", + "固定抽五张牌" + } + }, + b_cry_conveyor = { + name = "传送带牌组", + text = { + "小丑牌{C:attention}不可{}移动", + "回合开始时,", + "{C:attention}复制{}最右边的小丑牌", + "并且{C:attention}销毁{}最左边的小丑牌" + } + }, + b_cry_critical = { + name = "暴击牌组", + text = { + "每打出一手牌后,", + "{C:green}#1#/4{}几率获得{X:dark_edition,C:white}^2{}倍率", + "{C:green}#1#/8{}几率获得{X:dark_edition,C:white}^0.5{}倍率" + } + }, + ["b_cry_cry-Blue_deck"] = { + name = "入迷牌组", + text = { + "所有牌都带有一个{C:dark_edition}蓝色蜡封{}", + "卡牌不能更改蜡封类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-Gold_deck"] = { + name = "护身符牌组", + text = { + "所有牌都带有一个{C:dark_edition}金色蜡封{}", + "卡牌不能更改蜡封类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-Purple_deck"] = { + name = "灵媒牌组", + text = { + "所有牌都带有一个{C:dark_edition}紫色蜡封{}", + "卡牌不能更改蜡封类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-Red_deck"] = { + name = "既视感牌组", + text = { + "所有牌都带有一个{C:dark_edition}红色蜡封{}", + "卡牌不能更改蜡封类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-bonus_deck"] = { + name = "教皇牌组", + text = { + "所有的 {C:attention}手牌{}", + "都是 {C:attention,T:m_bonus}奖励牌{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-club_deck"] = { + name = "梅花卡组", + text = { + "所有的牌都是{C:dark_edition}黑桃{}", + "不能改变花色", + "boss不会出现{C:attention}梅花{}" + } + }, + ["b_cry_cry-eternal_deck"] = { + name = "永恒牌组", + text = { + "所有的牌都是{C:attention}永恒卡{}", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-foil_deck"] = { + name = "闪箔牌组", + text = { + "所有的牌都是{C:dark_edition,T:foil}闪箔牌{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-glass_deck"] = { + name = "正义牌组", + text = { + "所有的 {C:attention}手牌{}", + "都是 {C:attention,T:m_glass}玻璃牌{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-goad_deck"] = { + name = "世界牌组", + text = { + "所有的牌都是{C:dark_edition}黑桃{}", + "不能改变花色", + "boss不会出现{C:attention}挑衅{}" + } + }, + ["b_cry_cry-gold_deck"] = { + name = "恶魔牌组", + text = { + "所有的 {C:attention}手牌{}", + "都是 {C:attention,T:m_gold}黄金牌{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-head_deck"] = { + name = "太阳牌组", + text = { + "所有的牌都是{C:dark_edition}红桃{}", + "不能改变花色", + "", + "{C:attention}头部{}不会出现" + } + }, + ["b_cry_cry-holo_deck"] = { + name = "镭射牌组", + text = { + "所有的牌都是{C:dark_edition,T:holo}镭射牌{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-lucky_deck"] = { + name = "魔术师牌组", + text = { + "所有的 {C:attention}手牌{}", + "都是 {C:attention,T:m_lucky}幸运牌{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-mult_deck"] = { + name = "皇后牌组", + text = { + "所有的 {C:attention}手牌{}", + "都是 {C:attention,T:m_mult}倍率牌{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-negative_deck"] = { + name = "负片牌组", + text = { + "所有的牌都是{C:dark_edition,T:negative}负片牌{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-perishable_deck"] = { + name = "易腐牌组", + text = { + "所有的牌都带有{C:attention}易腐{}贴纸", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-pinned_deck"] = { + name = "固定牌组", + text = { + "所有的牌都带有{C:attention}固定{}贴纸", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-polychrome_deck"] = { + name = "多彩牌组", + text = { + "所有的牌都是{C:dark_edition,T:polychrome}多彩牌{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-rental_deck"] = { + name = "租赁牌组", + text = { + "所有的牌都带有{C:attention}租用{}贴纸", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-steel_deck"] = { + name = "战车牌组", + text = { + "所有的 {C:attention}手牌{}", + "都是 {C:attention,T:m_steel}钢铁牌{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-stone_deck"] = { + name = "石头牌组", + text = { + "所有的 {C:attention}手牌{}", + "都是 {C:attention,T:m_stone}石头牌{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-wild_deck"] = { + name = "爱人牌组", + text = { + "所有的 {C:attention}手牌{}", + "都是 {C:attention,T:m_wild}万能牌{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + ["b_cry_cry-window_deck"] = { + name = "星星牌组", + text = { + "所有的牌都是{C:dark_edition}方块{}", + "不能改变花色", + "boss不会出现{C:attention}窗口{}" + } + }, + b_cry_cryastral_deck = { + name = "星界牌组", + text = { + "所有的牌都是{C:dark_edition,T:cry_astral}星界版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_cryazure_deck = { + name = "台风牌组", + text = { + "所有牌都带有一个{C:dark_edition}蔚蓝火漆{}", + "卡牌不能更改蜡封类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_crybanana_deck = { + name = "香蕉牌组", + text = { + "所有的牌都带有{C:attention}香蕉{}贴纸", + "{s:0.8,C:inactive}" + } + }, + b_cry_cryblur_deck = { + name = "模糊牌组", + text = { + "所有的牌都是{C:dark_edition,T:cry_blur}模糊版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_cryecho_deck = { + name = "日食牌组", + text = { + "所有的的牌都是{C:attention,T:m_cry_echo}回响版本{}", + "不能更改增强类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_cryglass_deck = { + name = "易碎牌组", + text = { + "所有的牌都是{C:dark_edition,T:cry_glass}灰质琉璃版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_cryglitched_deck = { + name = "故障牌组", + text = { + "所有的牌都是{C:dark_edition,T:cry_glitched}故障版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}不对,这不就是印错牌组吗?" + } + }, + b_cry_crygold_deck = { + name = "鎏金牌组", + text = { + "所有的牌都是{C:dark_edition,T:cry_gold}鎏金版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_crym_deck = { + name = "M", + text = { + "所有的牌都是{C:dark_edition,T:cry_m}愉快版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_crymosaic_deck = { + name = "马赛克牌组", + text = { + "所有的牌都是{C:dark_edition,T:cry_mosaic}马赛克版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_crynoisy_deck = { + name = "噪声牌组", + text = { + "所有的牌都是{C:dark_edition,T:cry_noisy}噪声版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_cryoversat_deck = { + name = "过曝牌组", + text = { + "所有的牌都是{C:dark_edition,T:cry_oversat}过曝版本{}", + "不能更改版本类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_encoded = { + name = "编码牌组", + text = { + "以一张{C:cry_code,T:j_cry_CodeJoker}代码小丑{}和一张{C:cry_code,T:j_cry_copypaste}复制/粘贴{}开始", + "商店中只出现{C:cry_code}代码牌{}" + } + }, + b_cry_equilibrium = { + name = "均衡牌组", + text = { + "所有卡牌在", + "商店中出现的{C:attention}几率相同{},", + "以{C:attention,T:v_overstock_plus}库存过剩加强版{}开始游戏" + } + }, + b_cry_glowing = { + name = "发光牌组", + text = { + "在击败Boss盲注时,", + "所有小丑牌的数值乘以{X:dark_edition,C:white}X1.25{}", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person" + } + }, + b_cry_infinite = { + name = "无限牌组", + text = { + "你可以选择{C:attention}任意数量的卡牌{}", + "{C:attention}+1{}手牌上限" + } + }, + b_cry_legendary = { + name = "传奇牌组", + text = { + "以一张 {C:legendary}传奇{C:legendary} 小丑牌开始", + "击败Boss盲注后", + "{C:green}1 / 5{} 几率创建另外一张", + "{C:inactive}(必须有空间){}" + } + }, + b_cry_misprint = { + name = "错版牌组", + text = { + "卡牌价格,手牌的数值", + "都是{C:attention}随机{}数值" + } + }, + b_cry_redeemed = { + name = "赎回牌组", + text = { + "购买{C:attention}优惠券{}时,", + "获得其{C:attention}额外等级{}" + } + }, + b_cry_source_deck = { + name = "源代码牌组", + text = { + "所有牌都带有一个{C:dark_edition}绿色蜡封{}", + "卡牌不能更改蜡封类型", + "{s:0.8,C:inactive}" + } + }, + b_cry_spooky = { + name = "万圣节牌组", + text = { + "以一张{C:eternal}永恒{} {C:attention,T:j_cry_chocolate_dice}巧克力骰{}开始", + "每次{C:attention}底注{}之后 ", + "创建一个{C:cry_candy}糖果{}或 {X:cry_cursed,C:white}诅咒{}" + } + }, + b_cry_very_fair = { + name = "超平衡的牌组", + text = { + "每回合{C:blue}-2{}手牌,{C:red}-2{}弃牌", + "{C:attention}优惠券{}不再出现在商店中" + } + }, + b_cry_wormhole = { + name = "虫洞牌组", + text = { + "以一张{C:cry_exotic}域外{C:attention}小丑牌{}开始", + "小丑牌都{C:attention}20X+{}", + "更可能是{C:dark_edition}负片{}", + "{C:attention}-2{}小丑牌槽" + } + } + }, + Blind = { + bl_cry_box = { + name = "盒子", + text = { + "削弱所有的普通小丑" + } + }, + bl_cry_clock = { + name = "时间", + text = { + "时间滚滚流淌", + "在本底注中每过三秒,得分需求增长0.1x" + } + }, + bl_cry_hammer = { + name = "重锤", + text = { + "削弱所有的奇数牌" + } + }, + bl_cry_joke = { + name = "笑料", + text = { + "如果得分超过要求的2倍,", + "将盲注设置为下一个8的倍数", + "8,16,24...以此类推" + } + }, + bl_cry_lavender_loop = { + name = "薰衣草环", + text = { + "在boss回合中每经过1.5秒,盲注要求增加1.25倍" + } + }, + bl_cry_magic = { + name = "魔术", + text = { + "削弱所有的偶数牌" + } + }, + bl_cry_obsidian_orb = { + name = "黑曜宝珠", + text = { + "拥有所有已击败的", + "Boss盲注的能力" + } + }, + bl_cry_oldarm = { + name = "怀旧 手臂", + text = { + "必须出4张或更少的牌" + } + }, + bl_cry_oldfish = { + name = "怀旧 鱼", + text = { + "所有手牌的初始倍率都是1" + } + }, + bl_cry_oldflint = { + name = "怀旧 燧石", + text = { + "包含同花顺的出牌不计分" + } + }, + bl_cry_oldhouse = { + name = "怀旧 房屋", + text = { + "包含同花顺的出牌不计分" + } + }, + bl_cry_oldmanacle = { + name = "怀旧 镣铐", + text = { + "牌型的倍率除以剩余弃牌次数" + } + }, + bl_cry_oldmark = { + name = "怀旧 标记", + text = { + "包含对子的出牌不计分" + } + }, + bl_cry_oldox = { + name = "怀旧 公牛", + text = { + "所有手牌的初始筹码都是0" + } + }, + bl_cry_oldpillar = { + name = "怀旧 支柱", + text = { + "包含顺子的出牌不计分" + } + }, + bl_cry_oldserpent = { + name = "怀旧 蟒蛇", + text = { + "倍率除以所出牌型的牌型等级" + } + }, + bl_cry_pin = { + name = "别针", + text = { + "史诗及以上稀有度的", + "小丑被削弱" + } + }, + bl_cry_pinkbow = { + name = "粉红蝴蝶结", + text = { + "在出牌时随机化手牌中的牌的点数" + } + }, + bl_cry_sapphire_stamp = { + name = "蓝石烙印", + text = { + "选择一张额外的牌", + "出牌时随机丢弃所出的牌其中一张" + } + }, + bl_cry_shackle = { + name = "枷锁", + text = { + "削弱所有的负片小丑" + } + }, + bl_cry_striker = { + name = "打击", + text = { + "削弱所有的稀有小丑" + } + }, + bl_cry_tax = { + name = "强税", + text = { + "每次出牌的得分的上限为", + "盲注要求的0.4倍" + } + }, + bl_cry_tornado = { + name = "绿松旋风", + text = { + "#1#/#2#的概率", + "打出的牌不计分" + } + }, + bl_cry_trick = { + name = "诡计", + text = { + "每次出牌之后,翻转所有", + "手中的牌" + } + }, + bl_cry_vermillion_virus = { + name = "朱红病毒", + text = { + "每次出牌随机替换一张小丑牌" + } + }, + bl_cry_windmill = { + name = "风车", + text = { + "削弱所有的罕见小丑" + } + } + }, + Code = { + c_cry_class = { + name = "://类别", + text = { + "将 {C:cry_code}#1#{} 选中的牌", + "转换为 {C:cry_code}指令下的{} 增强", + "可用指令:{C:cry_code}bonus{},{C:cry_code}mult{},{C:cry_code}wild{},{C:cry_code}glass{}", + "{C:cry_code}steel{},{C:cry_code}stone{},{C:cry_code}gold{},{C:cry_code}lucky{},{C:cry_code}echo{}" + } + }, + c_cry_commit = { + name = "://提交", + text = { + "摧毁一张 {C:cry_code}选中的{} 小丑牌,", + "创造一张 {C:cry_code}新的相同 {C:cry_code}稀有度的{} 小丑牌" + } + }, + c_cry_crash = { + name = "://崩溃", + text = { + "{C:cry_code,E:1}警告!使用此牌会导致游戏崩溃!" + } + }, + c_cry_ctrl_v = { + name = "://CTRL+V", + text = { + "复制一张 {C:cry_code}选择的{} 卡牌或消耗牌" + } + }, + c_cry_delete = { + name = "://删除", + text = { + "{C:cry_code}永久{} 移除一个", + "{C:cry_code}选中的{} 商店物品", + "{C:inactive,s:0.8}本次游戏中该物品不会再出现" + } + }, + c_cry_divide = { + name = "://分割", + text = { + "{C:cry_code}减半{} 当前商店中的", + "所有标价" + } + }, + c_cry_exploit = { + name = "://利用", + text = { + "{C:cry_code}下一{} 手牌", + "", + "被计算为", + "", + "{C:cry_code}指令下的{} 牌型", + "", + "{C:inactive,s:0.8}必须是已发现的手牌类型", + "", + "可用指令:{C:cry_code}full house(葫芦){},{C:cry_code}flush(同花){},{C:cry_code}straight flush(同花顺){},{C:cry_code}four of a kind(四条){}", + "", + "{C:cry_code}three of a kind(三条){},{C:cry_code}pair(对子){},{C:cry_code}two pair(两对){},{C:cry_code}high(高牌){}" + } + }, + c_cry_hook = { + name = "://钩子", + text = { + "选择两张小丑牌", + "使它们 {C:cry_code}钩住{}" + } + }, + c_cry_inst = { + name = "://实例化", + text = { + "抽取与选中的牌同{C:cry_code}点数{}", + "以及{C:cry_code}同花色{}的牌各一张", + "{C:inactive}(如果有可能的话){}" + } + }, + c_cry_machinecode = { + name = "://机器码", + text = { + "" + } + }, + c_cry_malware = { + name = "://木马", + text = { + "给所有的{C:cry_code}无版本手牌{} ", + "添加 {C:dark_edition}故障{}版本" + } + }, + c_cry_merge = { + name = "://合并", + text = { + "将一张 {C:cry_code}消耗牌", + "与一张 {C:cry_code}手牌{} 合并" + } + }, + c_cry_multiply = { + name = "://乘法", + text = { + "选择一张牌,使其所有数值{C:cry_code}翻倍{}", + "直到 {C:cry_code}回合结束{}" + } + }, + c_cry_oboe = { + name = "://偏差1", + text = { + "下一包 {C:cry_code}增强包{} 有", + "{C:cry_code}#1#{} 张额外卡牌和", + "{C:cry_code}#1#{} 次额外选择", + "{C:inactive}(当前为 {C:cry_code}+#2#{C:inactive})" + } + }, + c_cry_patch = { + name = "://补丁", + text = { + "从当前所有可见的物品中移除", + "贴纸和负面效果" + } + }, + c_cry_payload = { + name = "://载荷", + text = { + "下一个被击败的盲注", + "给与 {C:cry_code}X#1#{} 利息(乘法叠加)" + } + }, + c_cry_reboot = { + name = "://重启", + text = { + "补充 {C:blue}手牌{} 和 {C:red}弃牌{},", + "将 {C:cry_code}所有{} 牌返回牌堆", + "并抽取一组 {C:cry_code}新的{} 手牌" + } + }, + c_cry_revert = { + name = "://还原", + text = { + "将 {C:cry_code}游戏状态{} 回溯到", + "{C:cry_code}这一底注的开始时{}" + } + }, + c_cry_rework = { + name = "://重制", + text = { + "摧毁一张 {C:cry_code}选中的{} 小丑牌,", + "创造该小丑牌的 {C:cry_code}重制标签{},并", + "获得一个 {C:cry_code}升级的{} 版本", + "{C:inactive,s:0.8}版本通过收藏中的顺序升级", + "基础,闪箔,镭射,多彩,负片,故障,马赛克,过曝,", + "灰质琉璃,鎏金,模糊,噪声,星界,欢愉,双面,基础" + } + }, + c_cry_run = { + name = "://运行", + text = { + "在 {C:cry_code}盲注", + "期间拜访 {C:cry_code}商店" + } + }, + c_cry_seed = { + name = "://种子", + text = { + "选择一张小丑牌", + "或扑克牌", + "使它们获得 {C:cry_code}灌铅{}效果", + "概率效果必然被触发" + } + }, + c_cry_semicolon = { + name = ";//", + text = { + "结束当前非Boss {C:cry_code}盲注{}", + "{C:cry_code}不获得金钱{}" + } + }, + c_cry_spaghetti = { + name = "://意面", + text = { + "生成一张 {C:cry_code}故障版本的{}", + "食物小丑牌" + } + }, + c_cry_variable = { + name = "://变量", + text = { + "将 {C:cry_code}#1#{} 张选中的牌", + "转换为 {C:cry_code}指令下的{} 牌型", + "可用指令:{C:cry_code}1~13,A,J,Q,K{}" + } + } + }, + Edition = { + e_cry_astral = { + name = "星界", + text = { + "{X:dark_edition,C:white}^#1#{} 倍率" + } + }, + e_cry_blur = { + name = "模糊", + text = { + "{C:attention}重新触发{}此卡", + "{C:green}#1# / #2#{} 的概率", + "重新触发 {C:attention}#3#{}次" + } + }, + e_cry_double_sided = { + name = "双面", + text = { + "此卡可以被", + "{C:attention}翻转{}以展示", + "另一张不同的卡牌" + } + }, + e_cry_glass = { + label = "易碎", + name = "灰质琉璃", + text = { + "{C:white,X:mult} X#3# {} 倍率", + "触发时{C:green}#1# / #2#{} 的概率率不会被摧毁" + } + }, + e_cry_glitched = { + name = "故障&_", + text = { + "此牌上的数值会{C:dark_edition}随机{}变动为", + "{C:attention}X0.1{} 与 {C:attention}X10{}倍", + "{C:inactive}(如果可能的话){}" + } + }, + e_cry_gold = { + label = "鎏金", + name = "鎏金", + text = { + "当使用或触发时额外{C:money}+$#1#{} " + } + }, + e_cry_m = { + name = "欢愉", + text = { + "{C:mult}+#1#{} 倍率", + "真是太愉悦了~" + } + }, + e_cry_mosaic = { + name = "马赛克", + text = { + "{X:chips,C:white} X#1# {} 筹码" + } + }, + e_cry_noisy = { + name = "噪声", + text = { + " " + } + }, + e_cry_oversat = { + name = "过曝", + text = { + "此卡上的所有数值", + "都被 {C:attention}翻倍{}", + "{C:inactive}(如果可能的话)" + } + } + }, + Enhanced = { + m_cry_echo = { + name = "回响牌", + text = { + "当计分时", + "{C:green}#2# / #3#{} 几率", + "{C:attention}重新触发{} #1# 次" + } + } + }, + Joker = { + j_cry_CodeJoker = { + name = "代码小丑", + text = { + "当选择{C:attention}盲注{}时", + "创造一张随机{C:dark_edition}负片{}{C:cry_code}代码牌{}" + } + }, + ["j_cry_Double Scale"] = { + name = "双重天平", + text = { + "成长性{C:attention}小丑{}", + "以{C:attention}平方{}速度增长", + "{C:inactive,s:0.8}(例如 +1, +3, +6, +10){}", + "{C:inactive,s:0.8}(增长为 +1, +2, +3)" + } + }, + j_cry_M = { + name = "M", + text = { + "当选择{C:attention}盲注{}时", + "创造一张{C:dark_edition}负片{}", + "{C:attention}欢乐小丑{}" + } + }, + j_cry_Megg = { + name = "M蛋", + text = { + "出售这张小丑来创造", + "{C:attention}#2#{} 开心小丑", + "在回合结束时增加 {C:attention}#1#{}" + } + }, + j_cry_Scalae = { + name = "刻度", + text = { + "成长性{C:attention}小丑{}成长速度", + "为{C:attention}#1#{}次幂级", + "在回合结束时使幂级成长提升{C:attention}#2#{}", + "{C:inactive,s:0.8}(平方成长、立方成长、四次方、五次方以此类推)", + "{C:inactive,s:0.8}({C:attention,s:0.8}刻度{C:inactive,s:0.8}除外)" + } + }, + j_cry_adroit = { + name = "灵巧小丑", + text = { + "如果打出的手牌包含", + "一个 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_altgoogol = { + name = "怀旧 Googol 游戏牌", + text = { + "出售此牌以复制", + "{C:attention}2{} 张最左侧的小丑", + "{C:inactive,s:0.8}不会复制怀旧 Googol 游戏牌{}" + } + }, + j_cry_antennastoheaven = { + name = "7-4 如指向天堂的天线", + text = { + "每张被计入分数的{C:attention}7{}或{C:attention}4{}", + "增加{X:chips,C:white} X#1# {}筹码", + "{C:inactive}(当前已增加{X:chips,C:white}X#2# {C:inactive}筹码)", + "{C:inactive,s:0.8}(捏他ultrakill关卡名)" + } + }, + j_cry_apjoker = { + name = "AP 弹小丑", + text = { + "对boss盲注提供{X:mult,C:white} X#1# {} 倍率" + } + }, + j_cry_astral_bottle = { + name = "瓶中星球", + text = { + "当出售时, 应用 {C:dark_edition}界星{}", + "和{C:attention}易腐{} 到一张", + "随机的 {C:attention}小丑牌{}" + } + }, + j_cry_big_cube = { + name = "大大方块", + text = { + "{X:chips,C:white} X#1# {} 筹码" + } + }, + j_cry_biggestm = { + name = "超大M", + text = { + "{X:mult,C:white} X#1# {} 倍率直到本轮结束", + "如果 {C:attention}打出牌型{}", + "是一个 {C:attention}#2#{}", + "{C:inactive}(当前 {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}不是胖,只是骨架大。" + } + }, + j_cry_blacklist = { + name = "黑名单", + text = { + "当手牌中有{C:attention}#1#{}或打出", + "{C:chips}筹码{} 和 {C:mult}倍率{} 变为0", + "如果牌组中没有{C:attention}#1#{},自毁", + "{C:inactive,s:0.8}不会改变牌型等级" + } + }, + j_cry_blender = { + name = "Blender", + text = { + "使用{C:cry_code}代码牌{}时", + "获得一张 {C:attention}随机{}消耗牌" + } + }, + j_cry_blurred = { + name = "模糊小丑", + text = { + "选择{C:attention}盲注{}时", + "增加{C:blue}+#1#{}出牌次数" + } + }, + j_cry_bonk = { + name = "咚~", + text = { + "每个{C:attention}小丑{}给予{C:chips}+#1#{}筹码", + "如果{C:attention}牌型{}是{C:attention}#3#{}增加{C:chips}+#2#{}", + "{C:inactive,s:0.8}欢乐小丑转而给予{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}筹码{}" + } + }, + j_cry_bonkers = { + name = "癫狂小丑", + text = { + "如果出的牌中包含", + "一个 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_bonusjoker = { + name = "奖励小丑", + text = { + "每张计分的{C:attention}奖励卡{}有", + " {C:green}#1# / #2#{} 的几率", + "增加一个{C:attention}小丑槽{}或者{C:attention}消耗槽{}", + "{C:inactive,s:0.8}(两种概率均等){}" + } + }, + j_cry_booster = { + name = "补充包小丑", + text = { + "商店{C:attention}+#1#{} 补充包槽位" + } + }, + j_cry_boredom = { + name = "无聊小丑", + text = { + "{C:green}#1# / #2#{} 概率", + "率重新触发{C:attention}所有小丑牌{}和{C:attention}计分牌{}", + "{C:inactive,s:0.8}无法触发{C:attention}无聊小丑{}{}" + } + }, + j_cry_brittle = { + name = "脆脆糖", + text = { + "之后 {C:attention}#1#{} 次出牌,", + "随机添加 {C:attention}石头{}, {C:attention}黄金{}, 或 {C:attention}钢铁{}增强到最右边的记分牌" + } + }, + j_cry_bubblem = { + name = "泡泡 M", + text = { + "如果打出的手牌包含{C:attention}#1#{}", + "生成一张{C:dark_edition}闪箔{C:attention}欢乐小丑{}", + "{C:red,E:2}自毁{}" + } + }, + j_cry_busdriver = { + name = "巴士司机", + text = { + "{C:green}#1# / #3#{} 概率 {C:mult}+#2#{} 倍率", + "或{C:green}1 /4{} 概率 {C:mult}-#2#{} 倍率" + } + }, + j_cry_candy_basket = { + name = "糖果篮子", + text = { + "卖掉这张牌获得 {C:attention}#1#{} {C:cry_candy}张糖果", + " 每击败 {C:attention}2{} 个盲注{C:attention}+#2#{} {C:cry_candy}糖果{}", + "击败{C:attention}Boss 盲注{}{C:attention}+#3#{} {C:cry_candy}糖果{}" + } + }, + j_cry_candy_buttons = { + name = "纽扣糖", + text = { + "之后的 {C:attention}#1#{} 次重掷", + "之花 {C:money}$1{}" + } + }, + j_cry_candy_cane = { + name = "拐杖糖", + text = { + "之后 {C:attention}#1#{} 回合", + " 打出的牌{C:attention}重新触发{}获得{C:money}$#2#" + } + }, + j_cry_candy_dagger = { + name = "糖果匕首", + text = { + "选择 {C:attention}盲注{} 后", + "摧毁最左边的小丑创建一张{C:cry_candy}糖果{}" + } + }, + j_cry_candy_sticks = { + name = "棒棒糖", + text = { + "直到你打出 {C:attention}#1#{}", + "下个Boss盲注的效果失效" + } + }, + j_cry_canvas = { + name = "画布", + text = { + "右侧每有一张{C:blue}稀有度高于普通{}的小丑", + "所有左侧的小丑{C:attention}重新触发{}{C:attention}一次{}" + } + }, + j_cry_caramel = { + name = "焦糖", + text = { + "每打出一张牌时", + "在得分时获得 {X:mult,C:white}X#1#{} 倍率", + "持续 {C:attention}#2#{} 回合" + } + }, + j_cry_chad = { + name = "硬汉", + text = { + "重新触发{C:attention}最左边{}的小丑", + "{C:attention}#1#{}次" + } + }, + j_cry_chili_pepper = { + name = "辣椒", + text = { + "回合结束时", + "这个小丑获得{X:mult,C:white} X#2# {} 倍率", + "{C:attention}#3#{}回合后自毁", + "(当前{X:mult,C:white} X#1# {C:inactive}倍率)" + } + }, + j_cry_chocolate_dice = { + name = "巧克力骰", + text = { + "击败Boss盲注后", + "投掷一个{C:green}d10{} 的判定", + "去开始一个{C:cry_ascendant,E:1}事件{}", + "{C:inactive}(当前: #1#)" + } + }, + j_cry_circulus_pistoris = { + name = "环形转轮", + text = { + "{X:dark_edition,C:white}^#1#{} 筹码, {X:dark_edition,C:white}^#1#{} 倍率", + "如果{C:attention}正好{}剩余 #2#", + "手牌" + } + }, + j_cry_circus = { + name = "马戏团", + text = { + "{C:red}稀有{}小丑每个提供 {X:mult,C:white} X#1# {} 倍率", + "{C:cry_epic}史诗{}小丑每个提供 {X:mult,C:white} X#2# {} 倍率", + "{C:legendary}传奇{}小丑每个提供 {X:mult,C:white} X#3# {} 倍率", + "{C:cry_exotic}域外{}小丑每个提供 {X:mult,C:white} X#4# {} 倍率" + } + }, + j_cry_clash = { + name = "冲突", + text = { + "如果打出的手牌包含", + "一个 {C:attention}#2#", + "{X:mult,C:white} X#1# {} 倍率" + } + }, + j_cry_coin = { + name = "加密货币", + text = { + "出售小丑牌时随机获得{C:money}$#1#{}到{C:money}$#2#{}" + } + }, + j_cry_compound_interest = { + name = "利滚利", + text = { + "回合结束时,获得{C:money}#1#%{}利息", + "每次触发后,利息获取增加{C:money}#2#%{}" + } + }, + j_cry_copypaste = { + name = "复制/粘贴", + text = { + "使用{C:cry_code}代码牌{}时", + "{C:green}#1# / #2#{}概率将其复制回消耗牌槽" + } + }, + j_cry_cotton_candy = { + name = "棉花糖", + text = { + "出售后相邻的", + "{C:attention}小丑{} 变为 {C:dark_edition}负片{}" + } + }, + j_cry_crustulum = { + name = "蛋糕", + text = { + "这张小丑每次在商店", + "{C:attention}重掷{}时获得 {C:chips}+#2#{} 筹码", + "{C:green}所有重掷免费{}", + "{C:inactive}(当前 {C:chips}+#1#{C:inactive} 筹码)" + } + }, + j_cry_cryptidmoment = { + name = " M 接龙", + text = { + "", + "卖掉这张卡时,每张{C:attention}小丑牌{}的售价增加 {C:money}$#1#{}" + } + }, + j_cry_cube = { + name = "小小方块", + text = { + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_curse_sob = { + name = "啜泣", + text = { + "{C:edition,E:1}你无法{} {C:cry_ascendant,E:1}逃跑...{}", + "{C:edition,E:1}你无法{} {C:cry_ascendant,E:1}躲藏...{}", + "{C:dark_edition,E:1}你无法逃脱...{}", + "{C:inactive}(必须有槽位){}" + } + }, + j_cry_cursor = { + name = "光标", + text = { + "在商店购入卡牌时", + "这张小丑{C:chips}+#2#{} 筹码", + "{C:inactive}(当前已{C:chips}+#1#{C:inactive} 筹码)" + } + }, + j_cry_cut = { + name = "剪切", + text = { + "在 {C:attention}离开商店{}时", + "这张小丑随机摧毁一张 {C:cry_code}代码牌{}", + "并且增加 {X:mult,C:white} X#1# {} 倍率", + "{C:inactive}(当前已提供{X:mult,C:white} X#2# {C:inactive} 倍率)" + } + }, + j_cry_delirious = { + name = "错乱小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_discreet = { + name = "低调小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_doodlem = { + name = "涂鸦 M", + text = { + "当{C:attention}选择盲注{}时", + "创造2个随机{C:dark_edition}负片{} {C:attention}消耗牌{}", + "每有1个{C:attention}欢乐小丑{}创造1个额外的{C:attention}消耗牌" + } + }, + j_cry_dropshot = { + name = "一杆入洞", + text = { + "出牌时,每有一张 {C:attention}不计分{}的 {V:1}#2#{}牌", + "这张小丑牌增加 {X:mult,C:white} X#1# {} 倍率", + "花色每回合改变", + "{C:inactive}(当前 {X:mult,C:white} X#3# {C:inactive} 倍率)" + } + }, + j_cry_dubious = { + name = "可疑小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_duos = { + name = "双重奏", + text = { + "{X:mult,C:white} X#1# {} 倍率如果出牌", + "手牌包含{C:attention}#2#" + } + }, + j_cry_duplicare = { + name = "复制", + text = { + "每张{C:attention}小丑牌{}给予", + "{X:dark_edition,C:white}^#1#{} 倍率" + } + }, + j_cry_effarcire = { + name = "充盈", + text = { + "选择{C:attention}盲注{}时", + "将{C:green}整副牌{}抽入手牌", + "{C:inactive,s:0.8}如果你在一倍速时无法掌控我,", + "{C:inactive,s:0.8}你就不配在二倍速时拥有我" + } + }, + j_cry_energia = { + name = "能量", + text = { + "当获得一个{C:attention}标签{}时", + "复制{C:attention}#1#{}个这个标签", + "并下次{C:attention}复制{}的数量", + "增加{C:attention}#2#{}" + } + }, + j_cry_equilib = { + name = "王牌均衡", + text = { + "小丑牌以{C:attention}收藏{}中的顺序出现", + "出牌时创造{C:attention}#1#{} {C:dark_edition}负片{}小丑", + "不会出现{C:cry_exotic,s:0.8}域外{C:inactive,s:0.8}或更稀有的小丑", + "{s:0.8}最后生成的小丑: {C:attention,s:0.8}#2#" + } + }, + j_cry_error = { + name = "{C:dark_edition}E{}{C:red}错误{}{C:dark_edition}O{}{C:red}R{}", + text = { + "nan" + } + }, + j_cry_eternalflame = { + name = "永恒之火", + text = { + "每出售一张牌,", + "这张小丑获得{X:mult,C:white} X#1# {} 倍率", + "(当前{X:mult,C:white} X#2# {C:inactive}倍率)" + } + }, + j_cry_exoplanet = { + name = "系外行星", + text = { + "每张{C:dark_edition}镭射{}卡提供{C:mult}+#1#{} 倍率" + } + }, + j_cry_exponentia = { + name = "指数", + text = { + "触发 {X:red,C:white} X 倍率 {} 时", + "这张小丑获得 {X:dark_edition,C:white} ^#1# {} 倍率", + "{C:inactive}(当前 {X:dark_edition,C:white} ^#2# {C:inactive} 倍率)" + } + }, + j_cry_exposed = { + name = "揭露", + text = { + "重新触发{C:attention}非人头牌{} ", + "额外{C:attention}#1#{}次", + "所有 {C:attention}人头牌{} 都被削弱" + } + }, + j_cry_facile = { + name = "简易", + text = { + "{C:attention}#2#{} 及一下的牌得分", + "{X:dark_edition,C:white}^#1#{} 倍率" + } + }, + j_cry_filler = { + name = "填充物", + text = { + "", + "如果出的牌中包含{C:attention}#2# 提供{X:mult,C:white} X#1# {} 倍率" + } + }, + j_cry_fleshpanopticon = { + name = "肉体监狱", + text = { + "{C:red}X#1#{} {C:attention}Boss 盲注{} 需求", + "当 {C:attention}Boss 盲注{} 被击败后,", + "{C:red}自毁{}, 然后创建", + "一张 {C:dark_edition}负片{} {C:spectral}真理之门{} ", + "{C:inactive,s:0.8}This prison... to hold... me?" + } + }, + j_cry_flip_side = { + name = "两副面孔", + text = { + "{C:dark_edition}双面{}小丑使用", + "它们反面的效果代替正面效果", + "{C:attention}重新触发{}所有{C:dark_edition}双面{}小丑" + } + }, + j_cry_foodm = { + name = "M当劳", + text = { + "{C:mult}+#1#{} 倍率", + "{C:red,E:2}在 {C:attention}#2#{} 回合#3#中自毁{}", + "当{C:attention}欢乐小丑{}被{C:attention}出售{}时增加{C:attention}#4#{}回合", + "{C:inactive,s:0.8}2 麦双层,2 麦鸡{}", + "{C:inactive,s:0.8}大薯条,20 块和大蛋糕{}" + } + }, + j_cry_foolhardy = { + name = "鲁莽x小丑", + text = { + "如果打出的手牌包含", + "一个 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_formidiulosus = { + name = "驱邪", + text = { + "When a {X:cry_cursed,C:white}{} Joker is obtained, destroy it", + "商店结束后创建 {C:attention}#1#{}张 {C:dark_edition}负片{C:cry_candy}糖果{} ", + " 每有一个 {C:cry_candy}糖果{} 被作祟{X:dark_edition,C:white}+^#2#{} 倍率", + "{C:inactive}(当前 {X:dark_edition,C:white}^#3#{C:inactive} 倍率)" + } + }, + j_cry_foxy = { + name = "奸猾小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_fractal = { + name = "分形手指", + text = { + "{C:attention}+#1#{} 手牌选择上限" + } + }, + j_cry_fspinner = { + name = "指尖陀螺", + text = { + "当打出的牌型不是最常用的牌型时", + "这张小丑牌增加{C:chips}+#2#{}筹码", + "{C:inactive}(当前{C:chips}+#1#{C:inactive}筹码)" + } + }, + j_cry_fuckedup = { + name = "烂逼小丑", + text = { + "如果打出的手牌包含", + "一个 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_gardenfork = { + name = "7-1 小径分岔的花园", + text = { + "打出的{C:attention} A{} 和{C:attention} 7{}", + "会在被计分时+{C:money}$#1#{} 块钱", + "{C:inactive,s:0.8}(一本书名,同时也捏他ultrakill关卡名)" + } + }, + j_cry_gemino = { + name = "双子", + text = { + "在回合结束时", + "最左侧的{C:attention}小丑{}", + "所有数值{C:attention}翻倍{}", + "" + } + }, + j_cry_ghost = { + name = "幽灵", + text = { + "回合结束后", + "{C:green}#1# / #2#{} 几率 ", + "{C:attention}作祟{} 一张随机的 {C:attention}小丑", + "{C:green}#1# / #3#{} 几率{E:2,C:red}自毁 {}" + } + }, + j_cry_giggly = { + name = "荒谬小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_goldjoker = { + name = "纯金小丑", + text = { + "回合结束获得{C:money}#1#%{}利息", + "每次{C:attention}黄金卡{}计分时", + "增加{C:money}#2#%{}利息" + } + }, + j_cry_googol_play = { + name = "Googol 游戏牌", + text = { + "{C:green}#1# / #2#{} 概率提供", + "{X:red,C:white} X#3# {} 倍率" + } + }, + j_cry_happy = { + name = ":D", + text = { + "在回合结束时创造一张随机的{C:attention}小丑{}", + "卖掉这张卡以", + "创造一张随机的{C:attention}小丑{}", + "{C:inactive}(必须有空间){}" + } + }, + j_cry_happyhouse = { + name = "快乐之家", + text = { + "{X:dark_edition,C:white}^#1#{} 倍率当", + " {C:attention}114{} 次出牌{}后", + "{C:inactive}(当前 #2#/114){}", + "{C:inactive,s:0.8}没有地方能比得上家!{}" + } + }, + j_cry_home = { + name = "家园", + text = { + "{X:mult,C:white} X#1# {} 倍率", + "如果打出的手牌包含", + "一个{C:attention}#2#" + } + }, + j_cry_hunger = { + name = "消耗之牌", + text = { + "使用一张{C:attention}消耗牌{}", + "获得{C:money}$#1#{}" + } + }, + j_cry_iterum = { + name = "再触", + text = { + "已经出过的牌重新触发{C:attention}#2#{}次", + "并且每张牌提供{X:mult,C:white} X#1# {} 倍率" + } + }, + j_cry_jawbreaker = { + name = "硬糖", + text = { + "击败 {C:attention}Boss 盲注后{} ,", + "让所有小丑数值变为{C:attention}两倍{} ", + "{E:2,C:red}自毁{}" + } + }, + j_cry_jimball = { + name = "小丑球", + text = { + "{C:attention}连续{}打出", + "{C:attention}使用次数最多的牌型{}时", + "获得 {X:mult,C:white} X#1# {} 倍率", + "{C:inactive}(当前{X:mult,C:white} X#2# {C:inactive}倍率)" + } + }, + j_cry_jollysus = { + name = "开心小丑…吗?", + text = { + "当一张小丑被{C:attention}出售{}时", + "创造一张{C:dark_edition}欢愉{}小丑", + "{C:red}每回合只生效一次{}", + "{C:inactive}#1#{}" + } + }, + j_cry_kidnap = { + name = "绑架", + text = { + "每回合结尾获得{C:money}$#2#{}", + "每卖掉一张{C:attention}倍率{}或者{C:attention}筹码{}小丑时", + "增加{C:money}$#1#{}" + } + }, + j_cry_kooky = { + name = "古怪小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_krustytheclown = { + name = "小丑库斯提", + text = { + "当出的每张牌都计分时", + "这张牌增加{X:mult,C:white} X#1# {}倍率", + "{C:inactive}(当前{X:mult,C:white} X#2# {C:inactive}倍率)", + "{C:inactive,s:0.8}(捏他辛普森一家)" + } + }, + j_cry_kscope = { + name = "万花筒", + text = { + "当击败{C:attention}Boss 盲注{}时", + "使一张随机{C:attention}小丑{}获得{C:dark_edition}多彩{}版本" + } + }, + j_cry_lightupthenight = { + name = "7-2 掌灯破暗", + text = { + "每张 {C:attention}7 {}或 {C:attention}2{},", + "在计分时提供{X:mult,C:white}X#1#{}倍率", + "{C:inactive,s:0.8}(捏他ultrakill关卡名)" + } + }, + j_cry_longboi = { + name = "怪物", + text = { + "在回合结束时", + "给未来的", + "这张小丑牌 {X:mult,C:white}X#1#{} 倍率", + "{C:inactive}(当前 {X:mult,C:white}X#2#{C:inactive} 倍率){}" + } + }, + j_cry_loopy = { + name = "环弯愉悦", + text = { + "本回合每出售一张{C:attention}开心{}{C:attention}小丑{}", + "{C:attention}重新触发一次{}所有小丑", + "{C:inactive}(当前有{}{C:attention:} #1#{}{C:inactive} 次重新触发){}", + "{C:inactive,s:0.8}空间不够了...{}" + } + }, + j_cry_lucky_joker = { + name = "幸运小丑", + text = { + "{C:attention}幸运牌{}成功触发时", + "+ {C:money}$#1#{}" + } + }, + j_cry_luigi = { + name = "路易吉", + text = { + "所有小丑提供", + "{X:chips,C:white} X#1# {} 筹码" + } + }, + j_cry_m = { + name = "m", + text = { + "当{C:attention}欢乐小丑{}被售出时", + "这张小丑获得 {X:mult,C:white} X#1# {} 倍率", + "{C:inactive}(当前 {X:mult,C:white} X#2# {C:inactive} 倍率)" + } + }, + j_cry_macabre = { + name = "毛骨悚然", + text = { + "当选择{C:attention}盲注{}时,除了{C:attention}开心小丑{}和{C:legendary}M系小丑{}", + "其余小丑全部摧毁,每摧毁一张小丑就获得一张{C:attention}开心小丑{}" + } + }, + j_cry_magnet = { + name = "冰箱磁铁", + text = { + "回合结束时获得{C:money}$#1#{} ", + "如果有少于或等于{C:attention}#3#{} 张小丑牌,获得 {X:money,C:white} X#2# {}", + "" + } + }, + j_cry_manic = { + name = "狂躁小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_mario = { + name = "马里奥", + text = { + "额外触发所有小丑", + "{C:attention}#1#{}次" + } + }, + j_cry_mask = { + name = "面具", + text = { + "重新触发 {C:attention}人头牌{} cards", + "{C:attention}#1#{} additional 次", + "所有{C:attention}非人头牌{} 被削弱" + } + }, + j_cry_maximized = { + name = "速度拉满", + text = { + "所有{C:attention}人头牌{}都当作是{C:attention}K{},", + "所有{C:attention}数字牌{}都当作是{C:attention}10s{}," + } + }, + j_cry_maze = { + name = "迷宫", + text = { + "所有出牌都看作是每回合{C:attention}第一次出牌{}", + "所有弃牌都看作是每回合{C:attention}第一次弃牌{}" + } + }, + j_cry_mellowcreme = { + name = "南瓜糖", + text = { + "卖掉这张牌", + "所有的{C:attention}消耗牌{} 售价 {C:attention}X#1#倍" + } + }, + j_cry_membershipcard = { + name = "会员卡", + text = { + "Cryptid Discord{}中的每个成员提供{X:mult,C:white}X#1#{} {C:attention}倍率", + "{C:inactive}(当前{X:mult,C:white}X#2#{C:inactive} 倍率)", + "{C:blue,s:0.7}https://discord.gg/unbalanced{}" + } + }, + j_cry_membershipcardtwo = { + name = "老旧会员卡", + text = { + "在{C:attention}Cryptid Discord{}中的每个成员", + "提供{C:chips}+#1#{} Chips", + "{C:inactive}(当前 {C:chips}+#2#{C:inactive} 筹码)", + "{C:blue,s:0.7}https://discord.gg/unbalanced{}" + } + }, + j_cry_meteor = { + name = "流星雨", + text = { + "每张{C:dark_edition}闪箔{}卡都会", + "提供{C:chips}+#1#{} 筹码" + } + }, + j_cry_mneon = { + name = "霓虹 M", + text = { + "在回合结束时获得{C:money}$#2#{}", + "回合结束时每持有一张{C:attention}欢乐小丑{}或者{C:legendary}M 小丑{}", + "增加{C:money}$#1#{}" + } + }, + j_cry_mondrian = { + name = "蒙德里安", + text = { + "若回合结束没有使用弃牌", + "该小丑获得{X:mult,C:white} X#1# {} 倍率", + "{C:inactive}(当前{X:mult,C:white} X#2# {C:inactive} 倍率)" + } + }, + j_cry_monkey_dagger = { + name = "猴子匕首", + text = { + "当选择{C:attention}盲注{}时,", + "摧毁左边的小丑", + "并永久增加其售价的{C:attention}十倍{}", + "到这张{C:chips}筹码{}", + "{C:inactive}(当前{C:chips}+#1#{C:inactive} 筹码)" + } + }, + j_cry_monopoly_money = { + name = "垄断", + text = { + "{C:green}#1# / #2#{} 几率", + "时购买的牌{C:attention}摧毁{}", + "并且售出时 {C:attention}售价减半" + } + }, + j_cry_morse = { + name = "摩尔斯电码", + text = { + "在回合结尾获得 {C:money}$#2#{}", + "每卖掉一张 {C:attention}增强卡牌{}增加 {C:money}$#1#{}" + } + }, + j_cry_mprime = { + name = "使徒·十三", + text = { + "每回合结束时", + "创造一张{C:attention}M小丑{}", + "每个 {C:attention}开心小丑{}或者{C:attention}M小丑{} 提供 {X:dark_edition,C:white}^#1#{} 倍率", + "每 {C:attention}卖出{}一张 {C:attention}开心小丑{}", + "额外提供 {X:dark_edition,C:white}^#2#{}倍率", + "{C:inactive,s:0.8}(使徒·十三 除外)" + } + }, + j_cry_mstack = { + name = "堆叠 M", + text = { + "每出售{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}欢乐小丑{}", + "重新触发所有已打出的牌", + "{C:inactive}(当前{}{C:attention:} #1#{}{C:inactive} 次重新触发){}" + } + }, + j_cry_multjoker = { + name = "倍率小丑", + text = { + "每张计分的倍率牌有", + "{C:green}#1# / #2#{} 概率", + "创造一张{C:spectral}神秘生物{}牌", + "(必须有空间)" + } + }, + j_cry_necromancer = { + name = "死灵法师", + text = { + "当{C:attention}售出{}一张小丑且其售价大于 {C:attention}$0{} ", + "获得一张 {C:attention}本局游戏售出过的{} {C:attention}随机小丑牌{} ", + "并将其 {C:attention}售价{} 变为 {C:attention}$0{}" + } + }, + j_cry_negative = { + name = "负片小丑", + text = { + "{C:dark_edition}+#1#{C:attention} 小丑{} 槽" + } + }, + j_cry_nice = { + name = "nice", + text = { + "如果出牌包含 {C:attention}6{} 和 {C:attention}9{}", + "筹码{C:chips}+#1#{}", + "{C:inactive,s:0.8}nice{}", + "" + } + }, + j_cry_night = { + name = "夜晚", + text = { + "", + "最后一次出牌提供{X:dark_edition,C:white}^#1#{} 倍率,但自毁" + } + }, + j_cry_nosound = { + name = "7-3 无声无忆", + text = { + "每张打出的{C:attention}7{}触发{C:attention:}#1#{}次", + "{C:inactive,s:0.8}(捏他ultrakill关卡名)" + } + }, + j_cry_notebook = { + name = "M记事本=)", + text = { + "每次{C:attention}重掷{}商店有{C:green} #1# / #2#{} 几率增加{C:dark_edition}+1{}个小丑牌槽位", + "如果有{C:attention}#5#{}或以上{C:attention}欢乐小丑{}{C:green}百分百触发{}", + "{C:red}每回合只触发一次{}", + "{C:inactive}(当前{C:dark_edition}+#3#{}{C:inactive} 并且#4#){}" + } + }, + j_cry_number_blocks = { + name = "数字块", + text = { + "在回合结束时获得 {C:money}$#1#{}", + "每持有一张{C:attention}#3#{}手牌", + "增加{C:money}$#2#{}的奖励,", + "每回合卡牌变化" + } + }, + j_cry_nuts = { + name = "坚果", + text = { + "{X:mult,C:white} X#1# {} 倍率如果出牌", + "手牌包含", + "一个{C:attention}#2#" + } + }, + j_cry_nutty = { + name = "疯癫小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_oil_lamp = { + name = "油灯", + text = { + "回合结束后", + "右边 {C:attention}小丑牌{}的售价 {C:attention}x#1#{}" + } + }, + j_cry_oldblueprint = { + name = "残缺蓝图", + text = { + "复制右边小丑牌的能力", + "每回合后有{C:green}#1# / #2#{} 的几率自毁", + "" + } + }, + j_cry_oldcandy = { + name = "怀旧糖果", + text = { + "售出这张牌可以永久获得", + "{C:attention}+#1#{} 手牌上限" + } + }, + j_cry_oldinvisible = { + name = "怀旧隐形小丑", + text = { + "每卖出 {C:attention} 4 {}张小丑牌,随机复制 {C:attention}一张{}小丑牌", + "无法复制{s:0.8}怀旧隐形小丑{}(当前 #1#/4)", + "" + } + }, + j_cry_panopticon = { + name = "全景监狱", + text = { + "所有出牌都视为本回合{C:attention}最后一次出牌{}" + } + }, + j_cry_penetrating = { + name = "渗透小丑", + text = { + "如果打出的手牌包含", + "一个 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_pickle = { + name = "腌黄瓜", + text = { + "当跳过 {C:attention}盲注{} 时,创造{C:attention}#1#{} 个标签", + "当选择 {C:attention}盲注{} 时,则减少{C:red}#2#{} " + } + }, + j_cry_pirate_dagger = { + name = "海盗匕首", + text = { + "当选择{C:attention}盲注{}时,", + "摧毁右边的小丑", + "并获得其售价的{C:attention}四分之一{}", + "作为{X:chips,C:white} X筹码 {}", + "{C:inactive}(当前{X:chips,C:white} X#1# {C:inactive} 筹码)" + } + }, + j_cry_pity_prize = { + name = "遗憾奖", + text = { + "当你跳过 {C:attention}补充包{} 获得一个随机的 {C:attention}标签{}" + } + }, + j_cry_pot_of_jokes = { + name = "小丑之壶", + text = { + "{C:attention}#1#{}手牌上限,", + "每回合增加{C:blue}#2#{} " + } + }, + j_cry_primus = { + name = "素数", + text = { + "这张小丑获得 {X:dark_edition,C:white} ^#1# {} 倍率", + "如果手中打出的所有牌都是", + "{C:attention}A{}、{C:attention}2{}、{C:attention}3{}、{C:attention}5{} 或 {C:attention}7{}", + "{C:inactive}(当前 {X:dark_edition,C:white} ^#2# {C:inactive} 倍率)" + } + }, + j_cry_python = { + name = "Python", + text = { + "", + "每使用一张{C:cry_code}代码牌{},这张牌增加{X:mult,C:white} X#1# {}倍率", + "{C:inactive}(当前已提供{X:mult,C:white} X#2# {C:inactive} 倍率)" + } + }, + j_cry_queens_gambit = { + name = "后翼弃兵", + text = { + "打出{C:attention}皇家同花顺{}时", + "摧毁{C:attention}计分的 Q{}", + "创造一张{C:dark_edition}负片{}{C:red}稀有{}{C:attention} 小丑{}" + } + }, + j_cry_quintet = { + name = "五重奏", + text = { + "{X:mult,C:white} X#1# {} 倍率如果出牌", + "手牌包含", + "一个{C:attention}#2#" + } + }, + j_cry_redbloon = { + name = "红色气球", + text = { + "{C:attention}#2#{}回合#3#后{C:red,E:2}自毁{}并获得{C:money}$#1#{}" + } + }, + j_cry_redeo = { + name = "回溯", + text = { + "当花费{C:money}$#2#{} {C:inactive}($#3#){} 时", + "{C:attention}-#1#{} 底注", + "每次使用{C:attention,s:0.8}指数增加{}花费", + "{C:money,s:0.8}下次增加:{s:1,c:money}$#4#" + } + }, + j_cry_rescribere = { + name = "售者再择", + text = { + "售出{C:attention}小丑{}时,", + "将其效果添加到", + "所有其他小丑上", + "{C:inactive,s:0.8}不影响其他售者再择{}" + } + }, + j_cry_reverse = { + name = "反转牌", + text = { + "如果{C:attention}弃牌牌型{}是{C:attention}#1#{}", + "用{C:dark_edition}闪箔{C:attention}开心小丑{}", + "填满所有空的小丑槽位 {C:inactive}(最多 100){}", + "{C:red,E:2}自毁{}", + "{C:inactive,s:0.8}终极逆袭{}" + } + }, + j_cry_rnjoker = { + name = "随机小丑", + text = { + "每次{C:attention}底注{}时随机能力" + } + }, + j_cry_sacrifice = { + name = "献祭", + text = { + "当使用一张{C:spectral}幻灵牌{}时", + "创造1个{C:green}罕见{}小丑", + "和3个{C:attention}开心小丑{}", + "", + "{C:red}每回合生效一次{}", + "{C:inactive}#1#{}" + } + }, + j_cry_sapling = { + name = "树苗", + text = { + "当{C:attention}#2#{} {C:inactive}[#1#]{} 张{C:attention}增强牌{}计分后", + "卖掉此牌", + "以获得一张{C:cry_epic}史诗{} {C:attention}小丑{}" + } + }, + j_cry_savvy = { + name = "精明小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_scrabble = { + name = "拼M游戏", + text = { + "当打出手牌时,", + "会有{C:green}#1#/#2#{}几率生成一张", + "{C:dark_edition}欢愉{C:green}罕见{}小丑" + } + }, + j_cry_seal_the_deal = { + name = "契约已成", + text = { + "打出本回合{C:attention}最后一手牌{}时,", + "每张计分的卡获得{C:attention}随机蜡封{}" + } + }, + j_cry_shrewd = { + name = "敏锐小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_silly = { + name = "傻傻小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_smallestm = { + name = "超小M", + text = { + "如果 {C:attention}扑克手牌{}", + "是一个 {C:attention}#1#{}", + "创造两个 a {C:cry_jolly}M 标签", + "{C:inactive,s:0.8}好吧,基本上我很小" + } + }, + j_cry_soccer = { + name = "合众为一", + text = { + "{C:attention}+#1#{} 小丑牌槽位", + "{C:attention}+#1#{} 补充包槽位", + "{C:attention}+#1#{} 手牌上限", + "{C:attention}+#1#{} 消耗牌槽位", + "{C:attention}+#1#{} 商店栏位" + } + }, + j_cry_spaceglobe = { + name = "寰宇天体仪", + text = { + "当打出{C:attention}#3#{}{C:attention}牌型{}时", + "获得{X:chips,C:white}X#2#{} 筹码", + "每次触发变化牌型", + "{C:inactive}(当前{} {X:chips,C:white}X#1#{} {C:inactive}筹码){}" + } + }, + j_cry_speculo = { + name = "镜像", + text = { + "离开{C:attention}商店{}时", + "随机复制一张{C:attention}小丑{}并增加{C:dark_edition}负片{}", + "{C:inactive,s:0.8}不复制镜像{}" + } + }, + j_cry_spy = { + name = "间谍", + text = { + "{X:mult,C:white} X#2# {} 倍率, {C:dark_edition}+1{C:attention} 小丑槽{} ", + "{C:inactive} #1# 是间谍!" + } + }, + j_cry_stardust = { + name = "星尘", + text = { + "{C:dark_edition}多彩{}卡", + "每张提供{X:mult,C:white}X#1#{} 倍率" + } + }, + j_cry_stella_mortis = { + name = "星逝", + text = { + "{C:attention}商店{}结束时", + "摧毁一张随机{C:planet}星球{}牌", + "并在获得 {X:dark_edition,C:white} ^#1# {} 倍率", + "{C:inactive}(当前 {X:dark_edition,C:white} ^#2# {C:inactive} 倍率)" + } + }, + j_cry_stronghold = { + name = "堡垒", + text = { + "如果打出的手牌包含", + "一个 {C:attention}#2#", + "{X:mult,C:white} X#1# {} 倍率" + } + }, + j_cry_subtle = { + name = "微妙小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_supercell = { + name = "supercell", + text = { + "{C:chips}+#1#{} 筹码, {C:mult}+#1#{} 倍率,", + "{X:chips,C:white}X#2#{} 筹码, {X:mult,C:white}X#2#{} 倍率", + "在回合结束时获得 {C:money}$#3#{}" + } + }, + j_cry_sus = { + name = "sus小丑", + text = { + "在回合结束时,{C:attention}随机复制{}一张手牌", + "摧毁其他所有手牌", + "{C:hearts,s:0.8}优先选择红桃K{}" + } + }, + j_cry_swarm = { + name = "虫群", + text = { + "{X:mult,C:white} X#1# {} 倍率如果出牌", + "手牌包含", + "一个{C:attention}#2#" + } + }, + j_cry_sync_catalyst = { + name = "同步催化剂", + text = { + "平衡 {C:chips}筹码{} 和 {C:mult}倍率{}", + "{C:inactive,s:0.8}嘿!我之前见过这个!(等离子牌组效果)" + } + }, + j_cry_tax_fraud = { + name = "逃税", + text = { + "回合结束后", + "每有一个 {C:attention}租用小丑{}", + "获得 {C:attention}$#1#{}" + } + }, + j_cry_tenebris = { + name = "暗黑", + text = { + "{C:dark_edition}+#1#{C:attention} 小丑{}槽", + "在回合结束时获得 {C:money}$#2#{}" + } + }, + j_cry_translucent = { + name = "半透明小丑", + text = { + "卖掉这张小丑时,随机复制一张小丑", + "并且添加易腐贴纸和香蕉贴纸", + "(不会复制对应小丑的腐烂进度)" + } + }, + j_cry_treacherous = { + name = "诡谲小丑", + text = { + "如果打出的手牌包含", + "一个 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_trick_or_treat = { + name = "不给糖就捣蛋", + text = { + " {C:attention}出售时{}:", + "{C:green}#1# / #2#{} 几率创建 {C:attention}2{}个 {C:cry_candy}糖果", + "否则 创建一个 {X:cry_cursed,C:white}诅咒小丑{} ", + "{C:inactive}(可以溢出)" + } + }, + j_cry_tricksy = { + name = "诡诈小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:chips}+#1#{} 筹码" + } + }, + j_cry_triplet_rhythm = { + name = "三琴定音", + text = { + "计分牌中刚好有", + " {C:attention}3 张 3 {}时", + "提供{X:mult,C:white} X#1# {} 倍率" + } + }, + j_cry_tropical_smoothie = { + name = "热带风味冰沙", + text = { + "卖掉这张牌", + "让所拥有的小丑数值{C:attention}X1.5{}" + } + }, + j_cry_unity = { + name = "团结", + text = { + "{X:mult,C:white} X#1# {} 倍率如果出牌", + "手牌包含", + "一个{C:attention}#2#" + } + }, + j_cry_universe = { + name = "宇宙", + text = { + "每张{C:dark_edition}界星{} ", + "提供 {X:dark_edition,C:white}^#1#{} 倍率" + } + }, + j_cry_universum = { + name = "宇宙", + text = { + "{C:attention}牌型{}升级时获得", + "{X:red,C:white} X#1# {} 倍率和 {X:blue,C:white} X#1# {} 筹码" + } + }, + j_cry_unjust_dagger = { + name = "不公匕首", + text = { + "当选择{C:attention}盲注{}时,", + "摧毁左边的小丑", + "并获得其售价的{C:attention}五分之一{}", + "作为{X:mult,C:white} X倍率 {}", + "{C:inactive}(当前{X:mult,C:white} X#1# {C:inactive} 倍率)" + } + }, + j_cry_verisimile = { + name = "虚实", + text = { + "当任何概率", + "{C:green}成功{}触发时,", + "这张小丑获得{X:red,C:white}X 倍率{}", + "等于其列出的{C:attention}概率", + "{C:inactive}(当前 {X:mult,C:white} X#1# {C:inactive} 倍率)" + } + }, + j_cry_virgo = { + name = "室女座", + text = { + "如果 {C:attention}果出牌牌型{} 包含一个 {C:attention}#2#{}", + "该小丑增加 {C:money}$#1#{} {C:attention}售价{}", + "出售这张牌时", + "每 {C:money}$4{} 的 {C:attention}出售价值{}{C:dark_edition}创造一张多彩{} {C:attention}开心小丑{},", + " {C:inactive}(至少 1){}" + } + }, + j_cry_wacky = { + name = "疯狂小丑", + text = { + "如果出的牌中包含 {C:attention}#2#", + "{C:red}+#1#{} 倍率" + } + }, + j_cry_waluigi = { + name = "瓦路易吉", + text = { + "所有小丑提供", + "{X:mult,C:white} X#1# {} 倍率" + } + }, + j_cry_wario = { + name = "瓦里奥", + text = { + "触发任意小丑时", + "获得{C:money}$#1#{}" + } + }, + j_cry_wee_fib = { + name = "微波那契数列", + text = { + "这个小丑获得", + "{C:mult}+#2#{} 倍率,当打出的每张", + "{C:attention}A{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, 或 {C:attention}8{}", + "计分时", + "{C:inactive}(当前 {C:mult}+#1#{C:inactive} 倍率)" + } + }, + j_cry_weegaming = { + name = "2D", + text = { + "每张计分的2", + "重新触发 {C:attention:}#1#{} 次", + "{C:inactive,s:0.8}我们身处游戏中吗 {}" + } + }, + j_cry_wheelhope = { + name = "希望之轮", + text = { + "当{C:attention}命运之轮{}效果失败", + "这张牌获得{X:mult,C:white} X#1# {} 倍率", + "{C:inactive}(当前{X:mult,C:white} X#2# {C:inactive} 倍率)" + } + }, + j_cry_whip = { + name = "长鞭", + text = { + "这张小丑获得 {X:mult,C:white} X#1# {} 倍率", + "如果 {C:attention}出牌手牌{} 包含", + "不同花色的{C:attention}2{} 和 {C:attention}7{}", + "{C:inactive}(当前 {X:mult,C:white} X#2# {C:inactive} 倍率)" + } + }, + j_cry_wrapped = { + name = "包裹糖果", + text = { + "每回合创建一个随机的{C:attention}食物小丑{}", + " {C:attention}#1#{} 回合后", + "{C:red,E:2}自毁{}" + } + }, + j_cry_wtf = { + name = "我操!?", + text = { + "如果打出的手牌包含", + "一个 {C:attention}#2#", + "{X:mult,C:white} X#1# {} 倍率" + } + } + }, + Other = { + banana = { + name = "香蕉", + text = { + "{C:green}每回合#1#/#2#{}概率被", + "摧毁" + } + }, + blurred_sdm0 = { + name = "a", + text = { + "{C:inactive,s:0.8}我讨厌这张牌- SDM_0, 2024{}" + } + }, + cry_azure_seal = { + name = "蔚蓝火漆", + text = { + "打出附带该火漆的牌时", + "{C:red}摧毁{}这张牌", + "之后创造 {C:attention}#1#{} 张 {C:dark_edition}负片{}{C:planet}对应牌型的星球牌{}" + } + }, + cry_banana_booster = { + name = "香蕉补充包", + text = { + "包中的所有卡牌", + "都带有{C:attention}香蕉{}贴纸" + } + }, + cry_banana_consumeable = { + name = "香蕉消耗牌", + text = { + "{C:green}#1# / #2#{}概率", + "使用时无效" + } + }, + cry_banana_voucher = { + name = "香蕉优惠券", + text = { + "每回合{C:green}#1# / #2#{}概率", + "失去此券" + } + }, + cry_eternal_booster = { + name = "永恒补充包", + text = { + "包中的所有卡牌", + "都带有{C:attention}永恒{}贴纸" + } + }, + cry_eternal_voucher = { + name = "永恒优惠券", + text = { + "不能被摧毁" + } + }, + cry_flickering = { + name = "闪烁", + text = { + "{C:attention}#1#{} 次触发后摧毁", + "{C:inactive}(剩余{C:attention}#2#{C:inactive} 次)" + } + }, + cry_flickering_desc = { + name = "闪烁", + text = { + "{C:attention}#1#{} 次触发后摧毁" + } + }, + cry_green_seal = { + name = "秩绿火漆", + text = { + "仅当被打出且不计分时", + "创造一张 {C:cry_code}代码{} 卡牌", + "{C:inactive}(必须有空间)" + } + }, + cry_hooked = { + name = "连锁", + text = { + "当这个小丑被{C:cry_code}触发{}时,", + "触发{C:cry_code}#1#{}" + } + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}更新{s:0.7} 默认情况下被禁用 ({C:attention,s:0.7}HTTPS模块{s:0.7})" + } + }, + cry_perishable_booster = { + name = "易腐", + text = { + "包中的所有卡牌", + "都带有{C:attention}易腐{}贴纸" + } + }, + cry_perishable_consumeable = { + name = "易腐消耗牌", + text = { + "回合结束时被削弱" + } + }, + cry_perishable_voucher = { + name = "易腐优惠券", + text = { + "一定回合后被削弱", + "{C:attention}#1#{} 回合", + "{C:inactive}({C:attention}#2#{C:inactive} 剩余)" + } + }, + cry_pinned_booster = { + name = "固定补充包", + text = { + "包中的所有卡牌", + "都带有{C:attention}固定{}贴纸" + } + }, + cry_pinned_consumeable = { + name = "固定消耗牌", + text = { + "不能使用其他", + "非{C:attention}固定{}消耗牌" + } + }, + cry_pinned_voucher = { + name = "固定优惠券", + text = { + "在商店中保留", + "直到被兑换" + } + }, + cry_possessed = { + name = "作祟", + text = { + "{C:attention}禁用{} 此牌效果", + "如果可能的话{C:attention}反转{}此牌的效果", + "随着 {C:attention}幽灵{}的摧毁而摧毁" + } + }, + cry_rental_booster = { + name = "租赁补充包", + text = { + "包中的所有卡牌", + "都带有{C:attention}租赁{}贴纸" + } + }, + cry_rental_consumeable = { + name = "租赁消耗牌", + text = { + "回合结束时和使用时失去 {C:money}$#1#{}" + } + }, + cry_rental_voucher = { + name = "租赁优惠券", + text = { + "回合结束时失去 {C:money}$#1#{}" + } + }, + cry_rigged = { + name = "灌铅", + text = { + "这个小丑上{C:cry_code}所有的{}概率效果", + "都{C:cry_code}必定触发" + } + }, + ev_cry_choco0 = { + name = "", + text = { + "激活事件后", + "{C:cry_ascendant,E:1}事件{} 的具体信息将会出现" + } + }, + ev_cry_choco1 = { + name = "1: 恶灵附身", + text = { + "{C:attention}小丑牌{} 和打出的牌有", + "{C:green}1 / 3{} 几率获得闪烁", + "创建一个 {C:attention}幽灵", + "{C:inactive,s:0.7}你被幽灵附身了", + "{C:inactive,s:0.7}意识时而清醒,时而模糊" + } + }, + ev_cry_choco10 = { + name = "10: 珍贵古董", + text = { + "一张售价{C:money}$50{}的 {C:legendary}传奇{} {C:attention}小丑{} ", + "出现在商店 {C:attention}优惠券{}槽位", + "只有作为商店 {C:attention}最后一件物品{} 时才能购买", + "{C:inactive,s:0.7}你已经引起了遗物之灵的注意,", + "{C:inactive,s:0.7}但想要平息它,可没那么简单" + } + }, + ev_cry_choco2 = { + name = "2: 鬼屋", + text = { + "不能跳过 {C:attention}盲注{}", + "商店中只能 {C:attention}重掷{} 一次", + "{C:attention}优惠券{} 售价翻倍", + "{C:inactive,s:0.7}幽灵们已经占领这里!", + "{C:inactive,s:0.7}千万别碰任何东西,快点逃出去!" + } + }, + ev_cry_choco3 = { + name = "3: 女巫药剂", + text = { + "创建3个 {C:attention}魔药", + " {C:attention}小盲注{}之后使用一个", + "或者 {C:attention}所有的{} 减益效果都会在这个{C:attention}底注{}生效", + "{C:inactive,s:0.7}你被女巫绑架了!", + "{C:inactive,s:0.7}她盯着你,递上三瓶药剂", + "{C:inactive,s:0.7}选一个吧,不然她可要替你做决定了。" + } + }, + ev_cry_choco4 = { + name = "4: 月之深渊", + text = { + "打出的牌有 {C:green}1 / 4{} 几率", + "变为随机的 {C:club}梅花{} 人头牌", + "将 {C:attention}倍率{} 除以打出人头牌的数量", + "{C:inactive,s:0.7}即使是一个心地善良、", + "{C:inactive,s:0.7}夜晚祈祷的男人……" + } + }, + ev_cry_choco5 = { + name = "5: 吸血鬼", + text = { + "移除所有打出的牌的 {C:attention}增强{} ", + "并有{C:green}1 / 3{} 几率摧毁", + "{C:heart}红桃{} 和 {C:diamond}方块{} cards", + "{C:inactive,s:0.7}在夜深人静时要小心", + "{C:inactive,s:0.7,E:1}因为它们在阴影中{C:inactive,s:0.7} 寻找机会来满足自己的欲望……" + } + }, + ev_cry_choco6 = { + name = "6: 选一个吧", + text = { + "{C:attention}回合结束{}打开一个随机的{C:attention}补充包{} ", + "{C:inactive,s:0.7}在你漫步街道时", + "{C:inactive,s:0.7}突然发现一箱各式各样的补充包。不妨来一个吧!" + } + }, + ev_cry_choco7 = { + name = "7: 节日气氛", + text = { + "创建三个 {C:attention}不给糖就捣蛋{} 和一个 {C:attention}糖果篮子", + "每回合商店有一个 {C:attention}不给糖就捣蛋{}", + "获得{C:cry_candy}糖果{} 可以得到{C:money}$3{}", + "{C:inactive,s:0.7}整个社区都为恐怖的盛会装饰一新,", + "{C:inactive,s:0.7}快来享受这节日气氛吧!" + } + }, + ev_cry_choco8 = { + name = "8: 糖果雨", + text = { + "击败 {C:attention}盲注{} 后, 每剩余一次出牌获得 1 个{C:cry_candy}糖果{}", + "当获得一个{C:cry_candy}糖果{}后,获得一个 {C:attention}食物小丑{}", + "{C:inactive,s:0.7}糖果从天而降!", + "{C:inactive,s:0.7,E:1}快点,抓住你能抓到的所有糖果!" + } + }, + ev_cry_choco9 = { + name = "9: 鬼魅财富", + text = { + "获得 {C:money}$20", + "所有赚到的 {C:money}钱{} 都变为 {C:attention}双倍", + "{C:inactive,s:0.7}你已故的亲戚们的幽灵在午夜时分来访!", + "{C:inactive,s:0.7}他们默默地将一袋钱放在你的手中,", + "{C:inactive,s:0.7}温暖一笑,随即化为虚影消散在空气中~" + } + }, + food_jokers = { + name = "食物小丑", + text = { + "{s:0.8}Gros Michel,鸡蛋,冰淇淋,卡文迪许,", + "{s:0.8}乌龟豆,饮料可乐,爆米花,拉面,", + "{s:0.8}苏打水,泡菜,辣椒,焦糖,", + "{s:0.8}怀旧糖果,快餐M,", + "{s:0.8}切奶酪,咖啡馆美食,樱桃,", + "{s:0.8}全糖可乐,星果,火锅,", + "{s:0.8}幸运饼干,瑞士小丑,塔利亚费罗,", + "{s:0.8}皇家佳丽,优质葡萄酒,神秘苏打水,", + "{s:0.8}爆米花袋,火鸡晚餐,咖啡,", + "{s:0.8}蜡烛服务,燃烧的西瓜,", + "{s:0.8}燃烧的樱桃,软玉米饼,脆玉米饼,", + "{s:0.8}玉米片,幽灵可乐,汉堡,披萨" + } + }, + p_cry_code_jumbo_1 = { + name = "巨型代码包", + text = { + "选择 {C:attention}#1#{} 张最多", + "{C:attention}#2#{C:cry_code} 代码{} 卡牌" + } + }, + p_cry_code_mega_1 = { + name = "超级代码包", + text = { + "选择 {C:attention}#1#{} 张最多", + "{C:attention}#2#{C:cry_code} 代码{} 卡牌" + } + }, + p_cry_code_normal_1 = { + name = "代码补充包", + text = { + "选择 {C:attention}#1#{} 张最多", + "{C:attention}#2#{C:cry_code} 代码{} 卡牌" + } + }, + p_cry_code_normal_2 = { + name = "代码补充包", + text = { + "选择 {C:attention}#1#{} 张最多", + "{C:attention}#2#{C:cry_code} 代码{} 卡牌" + } + }, + p_cry_empowered = { + name = "究极幻灵包", + text = { + "选择 {C:attention}#1#{} 张,最多", + "{C:attention}#2#{C:spectral} 幻灵{}牌", + "{s:0.8,C:inactive}(由强化标签生成)" + } + }, + p_cry_meme_1 = { + name = "玩梗包", + text = { + "选择 {C:attention}#1#{} 张,最多", + "可达 {C:attention}#2# 张玩梗的小丑{}" + } + }, + p_cry_meme_three = { + name = "玩梗包3", + text = { + "选择 {C:attention}#1#{} 张,最多", + "可达 {C:attention}#2# 张玩梗的小丑{}" + } + }, + p_cry_meme_two = { + name = "玩梗包2", + text = { + "选择 {C:attention}#1#{} 张,最多", + "可达 {C:attention}#2# 张玩梗的小丑{}" + } + }, + undiscovered_code = { + name = "未发现", + text = { + "在未设定的", + "游戏中购买或使用", + "此卡以了解其作用" + } + }, + undiscovered_unique = { + name = "未发现", + text = { + "在未设定的", + "游戏中购买或使用", + "此卡以了解其作用" + } + } + }, + Planet = { + c_cry_Kaikki = { + name = "Kaikki", + text = { + "({V:1}等级.#4#{})({V:2}等级.#5#{})({V:3}等级.#6#{})", + "升级", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "以及 {C:attention}#3#{}", + "(芬兰语)" + } + }, + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}等级.#4#{})({V:2}等级.#5#{})({V:3}等级.#6#{})", + "升级", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "以及 {C:attention}#3#{}", + "(芬兰语Club)" + } + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}等级.#4#{})({V:2}等级.#5#{})({V:3}等级.#6#{})", + "升级", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "以及 {C:attention}#3#{}", + "(芬兰语Spade)" + } + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}等级.#4#{})({V:2}等级.#5#{})({V:3}等级.#6#{})", + "升级", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "以及 {C:attention}#3#{}", + "(芬兰语Heart)" + } + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}等级.#4#{})({V:2}等级.#5#{})({V:3}等级.#6#{})", + "升级", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "以及 {C:attention}#3#{}", + "(芬兰语Diamond)" + } + }, + c_cry_asteroidbelt = { + name = "小行星带", + text = { + "{S:0.8}({S:0.8,V:1}等级.#1#{S:0.8}){} 升级", + "{C:attention}#2#", + "{C:mult}+#3#{} 倍率和", + "{C:chips}+#4#{} 筹码" + } + }, + c_cry_marsmoons = { + name = "火卫一和土卫二", + text = { + "{S:0.8}({S:0.8,V:1}等级.#1#{S:0.8}){} 升级", + "{C:attention}#2#", + "{C:mult}+#3#{} 倍率和", + "{C:chips}+#4#{} 筹码" + } + }, + c_cry_nstar = { + name = "中子星", + text = { + "本局游戏中", + "每使用过一张中子星", + "升级一次随机扑克牌型", + "{C:inactive}(当前{C:attention} #1#{C:inactive}){}" + } + }, + c_cry_planetlua = { + name = "星球.lua", + text = { + "{C:green}#1# / #2#{} 的几率", + "升级所有种类", + "{C:legendary,E:1}扑克牌型{} {C:attention}1{} 级" + } + }, + c_cry_universe = { + name = "该死的宇宙", + text = { + "{S:0.8}({S:0.8,V:1}等级.#1#{S:0.8}){} 升级", + "{C:attention}#2#", + "{C:mult}+#3#{} 倍率和", + "{C:chips}+#4#{} 筹码" + } + }, + c_cry_void = { + name = "空", + text = { + "{S:0.8}({S:0.8,V:1}等级.#1#{S:0.8}){} 升级", + "{C:attention}#2#", + "{C:mult}+#3#{} 倍率和", + "{C:chips}+#4#{} 筹码" + } + } + }, + Sleeve = { + sleeve_cry_bountiful_sleeve = { + name = "丰饶的露台牌套", + text = { + "每次{C:attention}出牌{} 或 {C:attention}弃牌{}后", + "固定抽五张牌" + } + }, + sleeve_cry_ccd_sleeve = { + name = "CCD 牌套", + text = { + "每张卡牌也是", + "一张 {C:attention}随机{} 消耗牌" + } + }, + sleeve_cry_conveyor_sleeve = { + name = "传送带牌套", + text = { + "{C:attention}不能{} 移动小丑卡", + "回合开始时,", + "{C:attention}复制{} 最右边的小丑卡", + "并且 {C:attention}销毁{} 最左边的小丑卡" + } + }, + sleeve_cry_critical_sleeve = { + name = "暴击牌套", + text = { + "每打出一手牌后,", + "{C:green}四分之一{} 的几率获得 {X:dark_edition,C:white} ^2 {} 倍", + "{C:green}八分之一{} 的几率获得 {X:dark_edition,C:white} ^0.5 {} 倍" + } + }, + sleeve_cry_encoded_sleeve = { + name = "编码牌套", + text = { + "开始时获得一张 {C:cry_code,T:j_cry_CodeJoker}代码小丑卡{}", + "和一张 {C:cry_code,T:j_cry_copypaste}复制/粘贴卡{}", + "商店中只出现 {C:cry_code}代码牌{}" + } + }, + sleeve_cry_equilibrium_sleeve = { + name = "平衡牌套", + text = { + "所有卡牌在", + "商店中的 {C:attention}出现几率相同{}", + "开始时拥有", + "{C:attention,T:v_overstock_plus}+2 商店栏位" + } + }, + sleeve_cry_infinite_sleeve = { + name = "无限牌套", + text = { + "你可以选择 {C:attention}任意数量", + "的卡牌" + } + }, + sleeve_cry_legendary_sleeve = { + name = "传奇牌套", + text = { + "以一张 {C:legendary}传奇{C:legendary} 小丑牌开始", + "击败Boss盲注后", + "{C:green}1 / 5{} 几率创建另外一张", + "{C:inactive}(必须有空间){}" + } + }, + sleeve_cry_misprint_sleeve = { + name = "错印牌套", + text = { + "卡牌的数值", + "是 {C:attention}随机{} 的" + } + }, + sleeve_cry_redeemed_sleeve = { + name = "赎回牌套", + text = { + "当购买{C:attention}优惠券{} 时,", + "会获得对应 {C:attention}高级{} 版" + } + }, + sleeve_cry_spooky_sleeve = { + name = "万圣节牌套", + text = { + "以一张{C:eternal}永恒{} {C:attention,T:j_cry_chocolate_dice}巧克力骰{}开始", + "每次{C:attention}底注{}之后 ", + "创建一个{C:cry_candy}糖果{}或 {X:cry_cursed,C:white}诅咒{}" + } + }, + sleeve_cry_wormhole_sleeve = { + name = "虫洞牌套", + text = { + "开始时获得一张 {C:cry_exotic}域外{C:attention} 小丑卡", + "小丑卡 {C:attention}20倍{} 更有可能", + "成为 {C:dark_edition}负片{} 卡", + "{C:attention}-2{} 小丑卡插槽" + } + } + }, + Spectral = { + c_cry_adversary = { + name = "对敌者", + text = { + "{C:red}所有{} {C:attention}小丑牌{} 变为 {C:dark_edition}负片{},", + "本局游戏的小丑牌购买价格{C:red}翻倍{}" + } + }, + c_cry_analog = { + name = "模拟", + text = { + "复制 {C:attention}#1#{} 张", + "随机 {C:attention}小丑{}", + "摧毁所有其他小丑,{C:attention}+#2#{} 底注" + } + }, + c_cry_chambered = { + name = "腔体", + text = { + "复制 {C:attention}#1#{}张 {C:attention}随机{}", + "的{C:dark_edition}负片{}", + "消耗牌" + } + }, + c_cry_conduit = { + name = "导水管", + text = { + "交换两张所选择的(小丑)牌的 {C:attention}版本{}" + } + }, + c_cry_gateway = { + name = "真理之门", + text = { + "生成一张随机的", + "{C:cry_exotic,E:1}域外{C:attention} 小丑{}", + "摧毁所有其他小丑" + } + }, + c_cry_hammerspace = { + name = "次元之袋", + text = { + "所有手牌获得特殊{C:dark_edition}增强{}效果", + "此增强效果会将它们变为随机{C:attention}消耗牌{}" + } + }, + c_cry_lock = { + name = "锁定", + text = { + "从 {C:red}所有{} 小丑中,", + "移除 {C:red}所有{} 贴纸", + "然后随机给一张小丑牌增加 {C:purple,E:1}永恒{}贴纸" + } + }, + c_cry_pointer = { + name = "://指针", + text = { + "创造一张", + "任选 {C:cry_code}卡牌", + "{C:inactive,s:0.8}(域外小丑 #1# 排除)", + "(输入对应名字)" + } + }, + c_cry_replica = { + name = "复录", + text = { + "转换所有手牌", + "为一张 {C:attention}随机{}", + "手牌" + } + }, + c_cry_ritual = { + name = "仪典", + text = { + "增强 {C:attention}#1#{} 张选定的卡牌", + "为 {C:dark_edition}负片{}, {C:dark_edition}马赛克{}", + "或 {C:dark_edition}星界{} " + } + }, + c_cry_source = { + name = "源码", + text = { + "向你手中的", + "{C:attention}#1#{} 选定卡牌", + "添加一个 {C:cry_code}绿色蜡封{}" + } + }, + c_cry_summoning = { + name = "召唤", + text = { + "创造一张随机的", + "{C:cry_epic}史诗{} {C:joker}小丑{},摧毁", + "一张随机 {C:joker}小丑{}" + } + }, + c_cry_trade = { + name = "交易", + text = { + "{C:attention}失去{} 一张随机优惠券,", + "获得 {C:attention}2{} 张随机优惠券" + } + }, + c_cry_typhoon = { + name = "台风", + text = { + "向手中 {C:attention}#1#{} 选择的", + "牌中添加 {C:cry_azure}天蓝火漆{}" + } + }, + c_cry_vacuum = { + name = "虚空", + text = { + "从 {C:red}所有{} 手中的牌,", + "移除 {C:red}所有 {C:green}修改{}", + "每移除一个 {C:green}修改{},获得 {C:money}$#1#{}", + "{C:inactive,s:0.7}(例如: 强化、蜡封、版本)" + } + }, + c_cry_white_hole = { + name = "白洞", + text = { + "{C:attention}移除{} 所有手牌等级,", + "升级 {C:legendary,E:1}最常用{} 扑克牌型", + "每移除1级,最常用牌型升{C:attention}3{}级" + } + } + }, + Stake = { + stake_cry_amber = { + colour = "Amber", + name = "琥珀注", + text = { + "商店的{C:attention}补充包-1{} ", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_ascendant = { + colour = "Ascendant", + name = "飞升注", + text = { + "商店栏位{C:attention}-1{}", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_azure = { + colour = "Azure", + name = "蔚蓝注", + text = { + "所有小丑牌的数值降低", + "{C:attention}20%{}", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_blossom = { + colour = "Blossom", + name = "花锦注", + text = { + "{C:attention}最终{}Boss版盲注可以出现在", + "{C:attention}任何{}底注中", + "{s:0.8,C:inactive}之前所有赌注也都起效{}", + "" + } + }, + stake_cry_bronze = { + colour = "Bronze", + name = "青铜注", + text = { + "优惠券价格增加 {C:attention}50%{}", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_brown = { + colour = "Brown", + name = "棕色注", + text = { + "所有的 {C:attention}贴纸(如永恒,易腐,租赁){} 互相兼容", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_crimson = { + colour = "Crimson", + name = "猩红注", + text = { + "优惠券只会在 {C:attention}双数{} 底注 时刷新", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_cyan = { + colour = "Cyan", + name = "靛青注", + text = { + "{C:green}罕见的{} 和 {C:red}稀有的{} 小丑牌出现概率", + "减少", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_dawn = { + colour = "Dawn", + name = "黎明注", + text = { + "塔罗牌和幻灵牌的选择目标 {C:attention}减 1{}", + "{s:0.8,C:inactive}(最少 1 张){}", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_diamond = { + colour = "Diamond", + name = "钻石注", + text = { + "获胜的底注变成{C:attention}10{}", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_ember = { + colour = "Ember", + name = "余烬注", + text = { + "所有卡牌出售时不再产生金钱", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_emerald = { + colour = "Emerald", + name = "翡翠注", + text = { + "卡牌、包和优惠券可以是 {C:attention}面朝下{} 的", + "{s:0.8,C:inactive}(购买前无法查看)", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_glass = { + colour = "Glass", + name = "玻璃注", + text = { + "卡牌在得分时可能会被 {C:attention}摧毁", + "{}{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_gray = { + colour = "Gray", + name = "灰色注", + text = { + "重新投掷的费用每次增加 {C:attention}$2{}", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_horizon = { + colour = "Horizon", + name = "地平线注", + text = { + "选择盲注时,增加一张", + "{C:attention}随机卡牌{} 到牌堆", + " {s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_jade = { + colour = "Jade", + name = "碧玉注", + text = { + "抽取手牌时有概率是 {C:attention}面朝下{} 的", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_pink = { + colour = "Pink", + name = "粉红注", + text = { + " {C:attention}底注 {}提升时,过关需求分数再次增速", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_platinum = { + colour = "Platinum", + name = "铂金注", + text = { + "小盲注 {C:attention}不会出现{}", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_quartz = { + colour = "Quartz", + name = "石英注", + text = { + "商店有可能出现{C:attention}固定{}小丑牌", + "{s:0.8,C:inactive}(固定在最左侧的位置){}", + "{s:0.8,C:inactive}之前所有赌注也都起效" + } + }, + stake_cry_ruby = { + colour = "Ruby", + name = "红玉注", + text = { + "{C:attention}大{} 盲注有可能变为", + "{C:attention}Boss{} 盲注", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_sapphire = { + colour = "Sapphire", + name = "蓝晶注", + text = { + "在 底注 结束时,失去当前总资金的 {C:attention}25%{} ", + "{s:0.8,C:inactive}(最高失去至 $10)", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_twilight = { + colour = "Twilight", + name = "暮光注", + text = { + "商店有可能出现{C:attention}香蕉{}小丑牌", + "{s:0.8,C:inactive}(每回合有 1/10 的几率销毁){}", + "{s:0.8,C:inactive}之前所有赌注也都起效" + } + }, + stake_cry_verdant = { + colour = "Verdant", + name = "翠绿注", + text = { + "所需分数会随每个 {C:attention}底注加速增加", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + }, + stake_cry_yellow = { + colour = "Yellow", + name = "灿黄注", + text = { + "{C:attention}永恒,易腐,租赁等贴纸{}", + "有可能出现在所有可以购买的物品上", + "{s:0.8,C:inactive}之前所有赌注也都起效{}" + } + } + }, + Tag = { + tag_cry_astral = { + name = "星界标签", + text = { + "下一张在商店里的小丑", + "增加 {C:dark_edition}星界版本{}并且免费" + } + }, + tag_cry_banana = { + name = "香蕉标签", + text = { + "创造{C:attention}#1#", + "{C:inactive}(必须有空间){}" + } + }, + tag_cry_better_voucher = { + name = "黄金优惠券标签", + text = { + "下个商店增加一张 T{C:attention}#1#{} 等级优惠券" + } + }, + tag_cry_bettertop_up = { + name = "进阶充值标签", + text = { + "创造 {C:attention}#1#张{C:green}罕见{}小丑", + "{C:inactive}(必须有空间){}" + } + }, + tag_cry_blur = { + name = "模糊标签", + text = { + "下一张在商店里的小丑", + "增加 {C:dark_edition}模糊版本{}并且免费" + } + }, + tag_cry_booster = { + name = "增强包标签", + text = { + "下一个 {C:cry_code}增强包{} 有", + "{C:attention}双倍{} 卡牌数量", + "{C:attention}双倍{} 选择次数" + } + }, + tag_cry_bundle = { + name = "组合标签", + text = { + "创造一个 {C:attention}标准标签{},{C:tarot}吊饰标签{},", + "{C:attention}小丑标签{},和 {C:planet}流星标签" + } + }, + tag_cry_cat = { + name = "猫猫标签", + text = { + "喵。", + "{C:inactive}等级 {C:dark_edition}#1#" + } + }, + tag_cry_console = { + name = "控制台标签", + text = { + "给予一个免费", + "{C:cry_code}程序包" + } + }, + tag_cry_double_m = { + name = "M标签", + text = { + "商店增加一张 {C:legendary}M系列小丑{}" + } + }, + tag_cry_empowered = { + name = "强化标签", + text = { + "给予一个 {C:spectral}究极幻灵{} 包", + "包括 {C:legendary,E:1}灵魂{} 和 {C:cry_exotic,E:1}传送门{}" + } + }, + tag_cry_epic = { + name = "史诗标签", + text = { + "下个商店有一张半价的", + "{C:cry_epic}史诗小丑" + } + }, + tag_cry_gambler = { + name = "赌徒标签", + text = { + "{C:green}#1# / #2#{} 的几率", + "创造一个 {C:cry_exotic,E:1}强化{} 标签" + } + }, + tag_cry_glass = { + name = "灰质标签", + text = { + "下一张在商店里的小丑", + "增加 {C:dark_edition}灰质琉璃{}版本并且免费" + } + }, + tag_cry_glitched = { + name = "故=@+/?”障", + text = { + "下《##一张商", + "//“小&获得 {C:dark_edition}故#_《障&效果并且免@{}" + } + }, + tag_cry_gold = { + name = "鎏金标签", + text = { + "下一张在商店里的小丑", + "增加 {C:dark_edition}鎏金版本{}并且免费" + } + }, + tag_cry_gourmand = { + name = "美餐标签", + text = { + "商店里增加一张", + "免费的{C:attention}食物小丑{}" + } + }, + tag_cry_loss = { + name = "loss", + text = { + "给予一个免费", + "的{C:cry_ascendant}玩梗{} 包", + "(标签名是个外网梗)" + } + }, + tag_cry_m = { + name = "愉悦~标签", + text = { + "下一张在商店里的小丑", + "增加 {C:dark_edition}愉悦~{}版本并且免费" + } + }, + tag_cry_memory = { + name = "记忆标签", + text = { + "创造 {C:attention}#1#{} 份", + "本局游戏上一次使用的 {C:attention}标签{}", + "{s:0.8,C:inactive}复制类标签除外", + "{s:0.8,C:inactive}当前: {s:0.8,C:attention}#2#" + } + }, + tag_cry_mosaic = { + name = "马赛克标签", + text = { + "下一张在商店里的小丑", + "增加 {C:dark_edition}马赛克版本{}并且免费" + } + }, + tag_cry_oversat = { + name = "过曝标签", + text = { + "下一张在商店里的小丑", + "增加 {C:dark_edition}过曝版本{}并且免费" + } + }, + tag_cry_quadruple = { + name = "四方标签", + text = { + "给予 {C:attention}#1#{} 份", + "下一个选择的 {C:attention}标签", + "{s:0.8,C:inactive}复制类标签除外" + } + }, + tag_cry_quintuple = { + name = "五重标签", + text = { + "给予 {C:attention}#1#{} 份", + "下一个选择的 {C:attention}标签", + "{s:0.8,C:inactive}复制类标签除外" + } + }, + tag_cry_rework = { + name = "重制标签", + text = { + "商店有一张", + "{C:dark_edition}#1# {C:cry_code}#2#" + } + }, + tag_cry_schematic = { + name = "原理图标签", + text = { + "下个商店有一张", + "{C:attention}头脑风暴{} " + } + }, + tag_cry_scope = { + name = "机遇标签", + text = { + "下回合增加1出牌次数", + "增加1弃牌次数" + } + }, + tag_cry_triple = { + name = "三连标签", + text = { + "给予 {C:attention}#1#{} 份", + "下一个选择的 {C:attention}标签", + "{s:0.8,C:inactive}复制类标签除外" + } + } + }, + Tarot = { + c_cry_automaton = { + name = "机械人", + text = { + "创造 {C:attention}#1#", + "随机 {C:cry_code}代码{} 卡", + "{C:inactive}(必须有空间)" + } + }, + c_cry_eclipse = { + name = "日食", + text = { + "增强 {C:attention}#1#{} 选定的卡牌", + "为 {C:attention}回响卡" + } + }, + c_cry_meld = { + name = "融合", + text = { + "选择一张 {C:attention}小丑{} 或", + "{C:attention}扑克牌{} 将其变成", + "{C:dark_edition}双面{} " + } + }, + c_cry_theblessing = { + name = "祝福", + text = { + "创造 {C:attention}1{}", + "随机 {C:attention}消耗牌{}", + "{C:inactive}(必须有空间){}" + } + } + }, + Unique = { + c_cry_potion = { + name = "魔药", + text = { + "使用后获取一种未知的 ", + "{C:attention}魔力{}", + "{C:inactive,s:0.7}从巧克力骰子中获取" + } + } + }, + Voucher = { + v_cry_asteroglyph = { + name = "星象文字", + text = { + "设置底注为 {C:attention}#1#{}" + } + }, + v_cry_blankcanvas = { + name = "空白画布", + text = { + "{C:attention}+#1#{} 手牌上限" + } + }, + v_cry_clone_machine = { + name = "克隆机器", + text = { + "双倍标签变成", + "{C:attention}五重标签{},并且", + " {C:attention}4X{} 常见" + } + }, + v_cry_command_prompt = { + name = "命令提示符", + text = { + "{C:cry_code}代码{} 牌可以", + "出现在 {C:attention}商店{}" + } + }, + v_cry_copies = { + name = "多重复制", + text = { + "双倍标签变成", + "{C:attention}三连标签{},并且", + "是 {C:attention}2X{} 常见" + } + }, + v_cry_curate = { + name = "策展", + text = { + "所有牌出现时均", + "带有 {C:dark_edition}版本{}" + } + }, + v_cry_dexterity = { + name = "灵巧", + text = { + "永久", + "每回合获得 {C:blue}+#1#{} 手牌" + } + }, + v_cry_double_down = { + name = "双倍下注", + text = { + "每轮之后,", + " {C:dark_edition}双面{} 牌背面的所有数值 {X:dark_edition,C:white} X1.5 {}" + } + }, + v_cry_double_slit = { + name = "成对裂隙", + text = { + "{C:attention}融合{} 可以", + "出现在商店和", + "秘术包中" + } + }, + v_cry_double_vision = { + name = "重影", + text = { + "{C:dark_edition}双面{} 牌出现", + "{C:attention}4X{} 更频繁" + } + }, + v_cry_fabric = { + name = "宇宙结构", + text = { + "{C:dark_edition}+#1#{} 小丑槽" + } + }, + v_cry_grapplinghook = { + name = "抓钩", + text = { + "{C:attention}+#1#{} 牌", + "选择限制", + "{C:inactive,s:0.7}你可以用它做很多事情,比你想象的要多得多。{}" + } + }, + v_cry_hyperspacetether = { + name = "超时空钩锁", + text = { + "{C:attention}+#1#{} 牌", + "选择限制", + "{C:inactive,s:0.7}注意:未来会有额外的{}", + "{C:inactive,s:0.7}功能{}" + } + }, + v_cry_massproduct = { + name = "规模量产", + text = { + "商店中的所有牌和包", + "费用为 {C:attention}$1{}" + } + }, + v_cry_moneybean = { + name = "金钱魔豆", + text = { + "提高每轮获得的", + "利息上限至 {C:money}$#1#{}" + } + }, + v_cry_overstock_multi = { + name = "多重库存", + text = { + "{C:attention}+#1#{} 卡槽和", + "{C:attention}+#1#{} 补充包槽", + "在商店中可用" + } + }, + v_cry_pacclimator = { + name = "星球适应器", + text = { + "{C:planet}星球{} 牌出现的概率", + "{C:attention}X#1#{} ", + "购买此优惠券后本赛局所有", + "{C:planet}星球{}牌都{C:green}免费{}" + } + }, + v_cry_pairamount_plus = { + name = "至高山巅", + text = { + "打出的牌中每包含一个对子", + "{C:attention}重新触发{}一次所有 M 小丑" + } + }, + v_cry_pairing = { + name = "配对", + text = { + "{C:attention}重新触发{} 所有 M 小丑", + "如果出牌是 {C:attention}一对" + } + }, + v_cry_quantum_computing = { + name = "量子计算", + text = { + "{C:cry_code}代码{} 牌生成时概率", + "带有 {C:dark_edition}负片{} 版本" + } + }, + v_cry_repair_man = { + name = "修理工", + text = { + "{C:attention}重新触发{} 所有 M 小丑", + "如果出牌包含 {C:attention}一对" + } + }, + v_cry_rerollexchange = { + name = "重掷交换", + text = { + "所有重掷", + "费用 {C:attention}$2{}" + } + }, + v_cry_satellite_uplink = { + name = "卫星串联", + text = { + "{C:cry_code}代码{} 牌可能", + "出现在任何", + "{C:attention}天体包{}中" + } + }, + v_cry_scope = { + name = "银河望远镜", + text = { + "为出牌创造 {C:planet}星球", + "{C:attention}牌型{}", + "{C:inactive}(必须有空间){}" + } + }, + v_cry_stickyhand = { + name = "粘粘手", + text = { + "选择限制", + "{C:attention}+#1#{} 牌" + } + }, + v_cry_tacclimator = { + name = "塔罗适应器", + text = { + "{C:tarot}塔罗{} 牌出现的概率", + "{C:attention}X#1#{} ", + "购买此优惠券后本赛局所有", + "{C:tarot}塔罗{}牌都{C:green}免费{}" + } + }, + v_cry_tag_printer = { + name = "标签打印机", + text = { + "双倍标签变成", + "{C:attention}四方标签{},并且", + " {C:attention}3X{} 常见" + } + }, + v_cry_threers = { + name = "读,写,算", + text = { + "永久", + "每回合获得 {C:red}+#1#{} 弃牌" + } + } + } + }, + misc = { + achievement_descriptions = { + ach_cry_ace_in_crash = "check_for_unlock({type = ace_in_crash})", + ach_cry_blurred_blurred_joker = "获得模糊的模糊小丑", + ach_cry_break_infinity = "在次出牌中获得1.79e308筹码", + ach_cry_bullet_hell = "拥有15个AP小丑", + ach_cry_cryptid_the_cryptid = "用神秘生物复制神秘生物", + ach_cry_exodia = "拥有5个域外小丑", + ach_cry_freak_house = "打出由6和9的红心组成的同花顺,同时拥有Nice", + ach_cry_googol_play_pass = "灌铅一张Googol Play卡", + ach_cry_haxxor = "使用作弊代码", + ach_cry_home_realtor = "在底注 8之前激活快乐之家(不使用均衡牌组或反物质牌组)", + ach_cry_jokes_on_you = "在底注 1上触发笑料boss的效果并赢得比赛", + ach_cry_niw_uoy = "达到底注 -8", + ach_cry_now_the_fun_begins = "获得画布", + ach_cry_patience_virtue = "在打出第一手牌之前等待薰衣草环 2分钟并击败盲注", + ach_cry_perfectly_balanced = "使用超平衡牌组击败飞升注", + ach_cry_pull_request = "让 ://提交 生成它摧毁的相同小丑", + ach_cry_traffic_jam = "击败所有高峰时段挑战", + ach_cry_ult_full_skip = "在1回合内获胜", + ach_cry_used_crash = "使用://崩溃", + ach_cry_what_have_you_done = "删除或献祭一张域外小丑牌" + }, + achievement_names = { + ach_cry_ace_in_crash = "口袋ACE", + ach_cry_blurred_blurred_joker = "法律盲点", + ach_cry_break_infinity = "突破无限", + ach_cry_bullet_hell = "子弹地狱", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "艾克佐迪亚", + ach_cry_freak_house = "怪物之家", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "黑客", + ach_cry_home_realtor = "房产经纪人", + ach_cry_jokes_on_you = "现在谁是笑料?", + ach_cry_niw_uoy = "!了赢你", + ach_cry_now_the_fun_begins = "乐趣开始了", + ach_cry_patience_virtue = "耐心是美德", + ach_cry_perfectly_balanced = "完美平衡", + ach_cry_pull_request = "拉取请求", + ach_cry_traffic_jam = "交通堵塞", + ach_cry_ult_full_skip = "终极全跳过", + ach_cry_used_crash = "早告诉过你", + ach_cry_what_have_you_done = "你到底做了什么?!" + }, + challenge_names = { + c_cry_ballin = "炫酷风采", + c_cry_boss_rush = "进入地牢", + c_cry_dagger_war = "匕首战争", + c_cry_joker_poker = "小丑扑克", + c_cry_onlycard = "单卡挑战", + c_cry_rng = "随机挑战", + c_cry_rush_hour = "高峰时段 I", + c_cry_rush_hour_ii = "高峰时段 II", + c_cry_rush_hour_iii = "高峰时段 III", + c_cry_sticker_sheet = "贴纸纸张", + c_cry_sticker_sheet_plus = "贴纸纸张+" + }, + dictionary = { + b_code_cards = "代码牌", + b_flip = "翻转", + b_merge = "融合", + b_pull = "拉", + b_unique_cards = "特殊牌", + cry_active = "Active", + cry_again_q = "还想试一次 ", + cry_code_apply = "应用", + cry_code_apply_previous = "应用之前的", + cry_code_cancel = "取消", + cry_code_create = "创建", + cry_code_create_previous = "创建之前的", + cry_code_enh = "输入增强", + cry_code_enter_card = "输入一张牌", + cry_code_execute = "执行", + cry_code_exploit = "利用", + cry_code_exploit_previous = "利用之前的", + cry_code_hand = "输入扑克手牌", + cry_code_rank = "输入等级", + cry_critical_hit_ex = "致命一击!", + cry_critical_miss_ex = "致命失误!", + cry_curse = "祸根", + cry_curse_ex = "祸根!", + cry_debuff_obsidian_orb = "拥有所有已击败 Boss 的能力", + cry_debuff_oldarm = "必须打4张或更少的牌", + cry_debuff_oldflint = "同花不计分", + cry_debuff_oldhouse = "葫芦不计分", + cry_debuff_oldmark = "包含对子的牌型不计分", + cry_debuff_oldpillar = "顺子不计分", + cry_epic = "史诗", + cry_exotic = "域外", + cry_feat_achievements = "成就", + ["cry_feat_antimatter deck"] = "反物质牌组", + cry_feat_blinds = "盲注", + cry_feat_challenges = "挑战", + ["cry_feat_code cards"] = "代码牌", + ["cry_feat_enhanced decks"] = "增强牌组", + ["cry_feat_epic jokers"] = "史诗小丑", + ["cry_feat_exotic jokers"] = "域外小丑", + ["cry_feat_https module"] = "HTTPS 模块", + cry_feat_jokerdisplay = "展示小丑 (没有意义)", + ["cry_feat_m jokers"] = "M 小丑", + cry_feat_menu = "自定义主菜单", + ["cry_feat_misc."] = "杂项", + ["cry_feat_misc. decks"] = "杂项牌组", + ["cry_feat_misc. jokers"] = "杂项小丑", + ["cry_feat_more stakes"] = "注", + cry_feat_planets = "星球", + cry_feat_sleeves = "牌套", + cry_feat_spectrals = "幻灵", + cry_feat_spooky = "万圣节更新", + cry_feat_tags = "标签", + ["cry_feat_timer mechanics"] = "计时器机制", + cry_feat_vouchers = "优惠券", + cry_gaming = "游戏中", + cry_gaming_ex = "开赌!", + cry_good_luck_ex = "祝你好运!", + cry_hand_bulwark = "碉堡", + cry_hand_clusterfuck = "一坨", + cry_hand_ultpair = "至尊对子", + cry_hooked_ex = "被勾住!", + cry_inactive = "Inactive", + cry_jolly_ex = "红红火火恍恍惚惚红红火火恍恍惚惚!", + cry_m = "M", + cry_m_ex = "M!", + cry_m_minus = "m", + cry_minus_round = "-1 回合", + cry_mus_code = "代码牌 (://LETS_BREAK_THE_GAME)", + cry_mus_exotic = "域外小丑 (by AlexZGreat)", + cry_mus_high_score = "高分 (最终 Boss [For Your Computer] by AlexZGreat)", + cry_mus_jimball = "小丑球 (Funkytown by Lipps Inc.)", + cry_no_triggers = "无触发次数!", + cry_notif_jimball_1 = "小丑球", + cry_notif_jimball_2 = "版权提示", + cry_notif_jimball_d1 = "小丑球播放的音乐 Funkytown,", + cry_notif_jimball_d2 = "是受到版权保护的", + cry_notif_jimball_d3 = "不能用于流媒体和视频。", + cry_plus_cryptid = "+1 神秘生物", + cry_potion1 = "-1 所有牌型等级", + cry_potion2 = "X1.15 盲注要求", + cry_potion3 = "-1 出牌和弃牌", + cry_set_enable_features = "选择要启用的功能(游戏重新启动后生效):", + cry_set_features = "功能", + cry_set_music = "音乐", + cry_sobbing = "救救我...", + cry_sus_ex = "骗子!", + cry_unredeemed = "Unredeemed...", + k_code = "代码", + k_cry_candy = "糖果", + k_cry_cursed = "诅咒", + k_cry_epic = "史诗", + k_cry_exotic = "域外", + k_cry_meme_pack = "玩梗包", + k_cry_program_pack = "代码包", + k_disable_music = "禁用音乐", + k_end_blind = "结束盲注", + k_hooked_ex = "钩住了!", + k_planet_disc = "恒星环盘", + k_planet_satellite = "人造卫星", + k_planet_universe = "真实的宇宙", + k_unique = "特殊" + }, + labels = { + banana = "香蕉", + code = "代码", + cry_astral = "星界", + cry_azure_seal = "蔚蓝火漆", + cry_blur = "模糊", + cry_double_sided = "双面", + cry_epic = "史诗", + cry_exotic = "域外", + cry_flickering = "闪烁", + cry_glass = "易碎", + cry_glitched = "故障", + cry_gold = "鎏金", + cry_green_seal = "绿色火漆", + cry_hooked = "钩住", + cry_m = "愉快", + cry_mosaic = "马赛克", + cry_noisy = "噪声", + cry_oversat = "过曝", + cry_possessed = "作祟", + cry_rigged = "灌铅", + food_jokers = "食物小丑", + k_cry_candy = "糖果", + k_cry_cursed = "诅咒", + k_cry_epic = "史诗", + k_cry_exotic = "域外", + unique = "特殊" + }, + poker_hand_descriptions = { + cry_Bulwark = { + "5 张无等级,无花色的牌" + }, + cry_Clusterfuck = { + "至少 8 张不包含", + "对子、同花或顺子的牌" + }, + cry_UltPair = { + "两个两对,其中,他们之间共有两种花色", + "每对两对是一种花色", + "他们之间共有两种花色" + }, + cry_WholeDeck = { + "在一次出牌中,包含", + "52张牌的牌组中的每一张牌", + "你疯了?" + } + }, + poker_hands = { + cry_Bulwark = "碉堡", + cry_Clusterfuck = "一坨", + cry_UltPair = "至尊对子", + cry_WholeDeck = "一整副牌" + }, + rnj_loc_txts = { + actions = { + add_dollars = { + "获得 {C:money}$#2#{}" + }, + make_joker = { + "创造 {C:attention}#2# 小丑{}牌" + }, + make_planet = { + "创造 {C:attention}#2#{C:planet} 星球{} 牌" + }, + make_spectral = { + "创造 {C:attention}#2#{C:spectral} 幻灵{} 牌" + }, + make_tarot = { + "创造 {C:attention}#2#{C:tarot} 塔罗{} 牌" + } + }, + conds = { + big = { + "如果 {C:attention}盲注{} 是 {C:attention}大 {C:attention}盲注{}" + }, + boss = { + "如果 {C:attention}盲注{} 是 {C:attention}Boss {C:attention}盲注{}" + }, + buy_common = { + "如果是 {C:blue}普通{} {C:attention}小丑{}" + }, + buy_uncommon = { + "如果是 {C:green}罕见{} {C:attention}小丑{}" + }, + common = { + "如果是 {C:blue}普通{} {C:attention}小丑{}" + }, + discards_left = { + "如果回合结束时剩余 {C:red}#3#{} 次丢弃" + }, + face = { + "如果卡牌是 {C:attention}面{} 卡" + }, + first = { + "如果是 {C:attention}第一次出牌{}" + }, + first_discard = { + "如果是 {C:attention}第一次 {C:attention}丢弃{}" + }, + hands_left = { + "如果回合结束时剩余 {C:blue}#3#{} 手牌" + }, + joker = { + "如果卡牌是 {C:attention}小丑{}" + }, + last = { + "如果是 {C:attention}最后一次{} 出牌" + }, + last_discard = { + "如果是 {C:attention}最后一次 {C:attention}丢弃{}" + }, + non_boss = { + "如果 {C:attention}盲注{} 是 {C:attention}非Boss {C:attention}盲注{}" + }, + odds = { + "以 {C:green}#4# {C:green}中 {C:green}#3#{} 的几率" + }, + or_less = { + "如果手牌包含 {C:attention}#3#{} 或更少卡牌" + }, + or_more = { + "如果手牌包含 {C:attention}#3#{} 或更多卡牌" + }, + planet = { + "如果卡牌是 {C:planet}星球{} 卡" + }, + poker_hand = { + "如果手牌是 {C:attention}#3#{}" + }, + rank = { + "如果卡牌的等级是 {C:attention}#3#{}" + }, + rare = { + "如果是 {C:red}稀有{} {C:attention}小丑{}" + }, + small = { + "如果 {C:attention}盲注{} 是 {C:attention}小 {C:attention}盲注{}" + }, + spectral = { + "如果卡牌是 {C:spectral}幻灵{} 卡" + }, + suit = { + "如果卡牌是 {V:1}#3#{}" + }, + tarot = { + "如果卡牌是 {C:tarot}塔罗{} 卡" + }, + uncommon = { + "如果是 {C:green}罕见{} {C:attention}小丑{}" + } + }, + contexts = { + after = { + "每个 {C:attention}手牌{} 后" + }, + before = { + "每个 {C:attention}手牌{} 前" + }, + buying_card = { + "购买卡牌时" + }, + debuffed_hand = { + "如果已打出 {C:attention}手牌{} 被削弱" + }, + discard = { + "为每张丢弃的卡牌" + }, + end_of_round = { + "回合结束时" + }, + ending_shop = { + "在 {C:attention}商店{} 结束时" + }, + first_hand_drawn = { + "回合开始时" + }, + individual_hand_end = { + "回合结束时手中的每张卡牌" + }, + individual_hand_score = { + "结算期间为手中的每张卡牌" + }, + individual_play = { + "为每张计分的卡牌" + }, + joker_main = { + + }, + open_booster = { + "当 {C:attention}补充包{} 被打开时" + }, + other_joker = { + "每个 {C:attention}小丑{}" + }, + playing_card_added = { + "每次将 {C:attention}手牌{} 添加到牌组时" + }, + pre_discard = { + "每次丢弃前" + }, + remove_playing_cards = { + "卡牌被销毁时" + }, + repetition_hand = { + "重新触发手中的卡牌" + }, + repetition_play = { + "重新触发已打出的卡牌" + }, + reroll_shop = { + "重roll商店" + }, + selling_card = { + "出售卡牌时" + }, + selling_self = { + "出售此卡时" + }, + setting_blind = { + "选择 {C:attention}盲注{} 时" + }, + skip_blind = { + "跳过 {C:attention}盲注{} 时" + }, + skipping_booster = { + "跳过任何 {C:attention}补充包{} 时" + }, + using_consumeable = { + "使用 {C:attention}消耗牌{} 卡牌时" + } + }, + stats = { + h_size = { + "{C:attention}+#2#{} 手牌数目" + }, + money = { + "{C:money}+$#2#{} 金钱" + }, + plus_chips = { + "{C:blue}+#2#{} 筹码" + }, + plus_mult = { + "{C:red}+#2#{} 增加倍数" + }, + x_chips = { + "{X:blue,C:white} X#2#{} 筹码" + }, + x_mult = { + "{X:red,C:white} X#2#{} 倍数" + } + }, + stats_inactive = { + h_size = { + "{C:inactive}(当前 {C:attention}+#1#{C:inactive} 手牌数目)" + }, + money = { + "{C:inactive}(当前 {C:money}+$#1#{C:inactive})" + }, + plus_chips = { + "{C:inactive}(当前 {C:blue}+#1#{C:inactive} 筹码)" + }, + plus_mult = { + "{C:inactive}(当前 {C:red}+#1#{C:inactive} 倍率)" + }, + x_chips = { + "{C:inactive}(当前 {X:blue,C:white} X#1# {C:inactive} 筹码)" + }, + x_mult = { + "{C:inactive}(当前 {X:red,C:white} X#1# {C:inactive} 倍率)" + } + } + }, + v_dictionary = { + a_candy = { + "+#1# 糖果" + }, + a_powchips = { + "^#1# 筹码" + }, + a_powchips_minus = { + "-^#1# 筹码" + }, + a_powmult = { + "^#1# 倍率" + }, + a_powmult_minus = { + "-^#1# 倍率" + }, + a_powmultchips = { + "^#1# 倍率+筹码" + }, + a_powmultchips_minus = { + "-^#1# 倍率+筹码" + }, + a_round = { + "+#1# 回合" + }, + a_round_minus = { + "-#1# 回合" + }, + a_tag = { + "#1# 标签" + }, + a_tags = { + "#1# 标签" + }, + a_xchips = { + "X#1# 筹码" + }, + a_xchips_minus = { + "-X#1# 筹码" + }, + cry_art = { + "美术: #1#" + }, + cry_code = { + "程序: #1#" + }, + cry_idea = { + "想法: #1#" + }, + cry_sticker_desc = { + "使用这张小丑在", + "#2#Stake#3# 难度", + "赢得 #2##1#" + }, + cry_sticker_name = { + "#1# 贴纸" + } + }, + v_text = { + ch_c_all_rnj = { + "所有小丑都是 {C:attention}RNJoker{}" + }, + ch_c_cry_all_banana = { + "所有小丑都是 {C:eternal}香蕉{}" + }, + ch_c_cry_all_perishable = { + "所有小丑都是 {C:eternal}易腐{}" + }, + ch_c_cry_all_pinned = { + "所有小丑都是 {C:eternal}固定{}" + }, + ch_c_cry_all_rental = { + "所有小丑都是 {C:eternal}租赁{}" + }, + ch_c_cry_no_boosters = { + "{C:attention}补充包{}不再出现在商店中" + }, + ch_c_cry_no_consumables = { + "不再出现{C:attention}消耗牌{} " + }, + ch_c_cry_no_rerolls = { + "禁止 {C:attention}重掷{}" + }, + ch_c_cry_no_tags = { + "跳过功能已 {C:attention}禁用{}" + }, + ch_c_cry_no_vouchers = { + "{C:attention}优惠券{} 不再出现在商店中" + }, + ch_c_cry_rush_hour = { + "所有 Boss 盲注都是 {C:attention}时钟{} 或 {C:attention}薰衣草循环" + }, + ch_c_cry_rush_hour_ii = { + "所有盲注都是 {C:attention}Boss 盲注{}" + }, + ch_c_cry_rush_hour_iii = { + "{C:attention}时钟{} 和 {C:attention}薰衣草循环{} 的规模是 {C:attention}两倍{} 快" + }, + ch_c_cry_sticker_sheet_plus = { + "所有可购买的物品都有所有贴纸" + } + }, + very_fair_quips = { + { + "L", + "没有优惠券", + "给你" + }, + { + "傻瓜", + "你以为我会", + "给你优惠券?" + }, + { + "不行!", + "这里没有优惠券!", + "(崩溃版)" + }, + { + "操作水平问题", + "想象一下能", + "获得优惠券的水平" + }, + { + "小丑球", + "管理层", + "忘记补货了" + }, + { + "哎呀!", + "没有优惠券", + "" + }, + { + "你这个小丑,", + "你在这还在", + "看什么看呢?哈哈" + }, + { + "优惠券", + "在", + "另一个城堡" + }, + { + "$0", + "空白优惠券", + "(明白了吗?)" + }, + { + "错误", + "无法对 NIL 值进行算术运算", + "(T4优惠券.lua)" + }, + { + "100% OFF", + "所有优惠券", + "(有人已经买了)" + }, + { + "稍后再试", + "提示:你没有", + "足够的钱" + }, + { + "啊?", + "优惠券 ", + "那甚至不是一个词..." + }, + { + "按住 R", + "补货", + "所有优惠券" + }, + { + "你知道吗?", + "按 ALT+F4", + "会得到免费优惠券!" + }, + { + "对不起,", + "由于预算削减", + "没有优惠券" + }, + { + "拨打 1-600-JIMBO", + "来评价你的", + "优惠券体验" + }, + { + "击败", + "底注 39 BOSS 盲注", + "以补货" + }, + { + "来个魔术", + "我把这张优惠券", + "变消失了~" + }, + { + "为什么优惠券像", + "写字台一样?" + }, + { + "我们已经撤回了", + "你的优惠券,它们", + "在其他局中会更好用" + }, + { + "为什么叫优惠券", + "既不优惠", + "又没有券" + }, + { + "对不起", + "优惠券正在经历", + "VOUCHIFIA ABORTUS" + }, + { + "不幸的是", + "优惠券重写更新", + "已被取消" + }, + { + "击败", + "BOSS 盲注", + "以改变……啥都不变" + }, + { + "鸟儿在歌唱,花儿在绽放", + "像你这样的孩子...", + "没有优惠券可买" + }, + { + "我们很抱歉", + "由于沙门氏菌污染", + "所有优惠券都已被召回" + }, + { + "优惠券未能到达", + "由于商店布局超出", + "200% 预算" + }, + { + "你喜欢", + "买优惠券,对吧", + "你是优惠券买家" + }, + { + "优惠券", + "!E", + "优惠券池" + }, + { + "没有", + "优惠券" + }, + { + "没有圣诞老人", + "也没有优惠券" + }, + { + "", + "优惠券't", + "" + }, + { + "你", + "刚刚输掉了", + "游戏" + }, + { + "我可以给你", + "一个漂亮的鸡蛋", + "在这些困难时期?" + }, + { + "出门摸摸草", + "而不是玩", + "这个牌组" + }, + { + "你现在可以", + "玩蓝色牌组", + "对吧" + }, + { + "免费域外小丑!", + "快来拿,在它还没", + "卖完之前(已售罄)" + }, + { + "证明他们错了", + "购买一张隐形", + "优惠券,仅需 $10" + }, + { + "", + "没有优惠券?", + "" + }, + { + "看到这个广告了吗?", + "如果你看到了,那就说明它有效", + "你也可以拥有它" + }, + { + "你现在错过了", + "至少 5 张优惠券", + "噔噔等噔~" + }, + { + "10", + "20 没有优惠券 XD", + "30 返回 10" + }, + { + "优惠券", + "是一项高级功能", + "$199.99 JOLLARS 解锁" + }, + { + "真正的无优惠券! ! ", + "仅限飞升注", + "超平衡的牌组" + }, + { + "享受你的", + "优惠券体验吗?在 JESTELP 上", + "给我们一个五星评价" + }, + { + "免费优惠券", + "火辣的单身优惠券就在你附近", + "使用这个技巧快速获得优惠券" + }, + { + "隆重介绍", + "第一张 T0 优惠券!", + "(即将来到 Cryptid 1.0)" + }, + { + "一张优惠券!", + "但它只是想象中的", + "我还以为你想要呢" + }, + { + "关闭广告拦截器", + "没有广告,我们就不能", + "向你出售优惠券" + }, + { + "如果你有", + "关于这个的问题", + "请通过 NORESPONSE@JMAIL.COM 发送电子邮件给我们" + }, + { + "没有足够的钱", + "来购买这张优惠券", + "所以我们为什么要把它放在这里?" + }, + { + "想要优惠券吗?", + "那就闭嘴", + "你不能得到任何优惠券 笑死" + }, + { + "^$%& 不", + "优惠券 ^%&% %&$^% 给你", + "$%&%%$ %&$&*%$^" + }, + { + "一张优惠券 (相信)", + "|/|", + "|/|" + }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-." + }, + { + "开始游戏 > 新一局", + "盯着屏幕什么也不做", + "一个小时或两个小时" + }, + { + "我们非常抱歉", + "上一个人紧急购买了", + "所有优惠券" + }, + { + "没有优惠券", + "可买的感觉如何" + }, + { + "小丑球掷出 1", + "把所有优惠券", + "扔进了一个沟里" + }, + { + "尝试索引", + "字段 '优惠券'", + "(一个 NIL 值)" + }, + { + "哦,你真的以为阅读所有这些行会让你的优惠券回来?", + "很抱歉告诉你,这副牌中没有你所寻求的优惠券。", + "这个异常长的文本是为了浪费你的时间和精力而设计的。" + }, + { + "访问", + "https://youtu.be/p7YXXieghto", + "获取免费优惠券" + } + } + } +} \ No newline at end of file diff --git a/Cryptid/localization/zh_TW.lua b/Cryptid/localization/zh_TW.lua new file mode 100644 index 0000000..65b3b86 --- /dev/null +++ b/Cryptid/localization/zh_TW.lua @@ -0,0 +1,3314 @@ +--I couldn't get Meme Packs to work without crashing +--yes somehow that was harder than RNJoker +return { + descriptions = { + Back = { + b_cry_antimatter = { + name = "Antimatter Deck", + text = { + "Applies the {C:legendary,E:1}upsides{}", + "of {C:attention}every{} deck", + }, + }, + b_cry_beta = { + name = "Nostalgic Deck", + text = { + "{C:attention}Joker{} and {C:attention}Consumable{}", + "slots are {C:attention}combined", + "{C:attention}Nostalgic{} Blinds replace", + "their updated Blind" + }, + }, + b_cry_blank = { + name = "Blank Deck", + text = { + "{C:inactive,E:1}Does nothing?", + }, + }, + b_cry_CCD = { + name = "CCD Deck", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + b_cry_conveyor = { + name = "Conveyor Deck", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + b_cry_critical = { + name = "Critical Deck", + text = { + "After each hand played,", + "{C:green}#1# in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}#1# in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + b_cry_encoded = { + name = "Encoded Deck", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + b_cry_equilibrium = { + name = "Deck of Equilibrium", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}Overstock Plus", + }, + }, + b_cry_glowing = { + name = "Glowing Deck", + text = { + "Multiply the values of", + "all Jokers by {X:dark_edition,C:white} X1.25 {}", + "when Boss Blind is defeated", + "{X:cry_jolly,C:white,s:0.8} Jolly#1#Open#1#Winner#1#-#1#wawa#1#person", --peak loc_vars right here + }, + }, + b_cry_infinite = { + name = "Infinite Deck", + text = { + "You can select {C:attention}any", + "number of cards", + "{C:attention}+1{} hand size", + }, + }, + b_cry_misprint = { + name = "Misprint Deck", + text = { + "Values of cards", + "and poker hands", + "are {C:attention}randomized", + }, + }, + b_cry_redeemed = { + name = "Redeemed Deck", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + b_cry_very_fair = { + name = "Very Fair Deck", + text = { + "{C:blue}-2{} hands, {C:red}-2{} discards", + "every round", + "{C:attention}Vouchers{} no longer", + "appear in the shop", + }, + }, + b_cry_wormhole = { + name = "Wormhole Deck", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + b_cry_legendary = { + name = "Legendary Deck", + text = { + "Start with an {C:legendary}Legendary{C:legendary} Joker", + "{C:green}1 in 5{} chance to create another", + "when Boss Blind is defeated {C:inactive}(must have room){}", + }, + }, + }, + Blind = { + bl_cry_box = { + name = "The Box", + text = { + "All Common Jokers", + "are debuffed", + }, + }, + bl_cry_clock = { + name = "The Clock", + text = { + "+0.1X blind requirements every", + "3 seconds spent this ante", + }, + }, + bl_cry_hammer = { + name = "The Hammer", + text = { + "All cards with odd", + "rank are debuffed", + }, + }, + bl_cry_joke = { + name = "The Joke", + text = { + "If score is >2X requirements,", + "set ante to multiple of #1#", + }, + }, + bl_cry_magic = { + name = "The Magic", + text = { + "All cards with even", + "rank are debuffed", + }, + }, + bl_cry_lavender_loop = { + name = "Lavender Loop", + text = { + "1.25X blind requirements every", + "1.5 seconds spent this round", + }, + }, + bl_cry_obsidian_orb = { + name = "Obsidian Orb", + text = { + "Applies abilities of", + "all defeated bosses", + }, + }, + bl_cry_oldarm = { + name = "Nostalgic Arm", + text = { + "Must play 4", + "or fewer cards", + }, + }, + bl_cry_oldfish = { + name = "Nostalgic Fish", + text = { + "All hands start", + "with 1 Mult", + }, + }, + bl_cry_oldflint = { + name = "Nostalgic Flint", + text = { + "No Flushes", + }, + }, + bl_cry_oldhouse = { + name = "Nostalgic House", + text = { + "No Full Houses", + }, + }, + bl_cry_oldmanacle = { + name = "Nostalgic Manacle", + text = { + "Divide Mult by discards", + }, + }, + bl_cry_oldmark = { + name = "Nostalgic Mark", + text = { + "No hands that", + "contain a Pair", + }, + }, + bl_cry_oldox = { + name = "Nostalgic Ox", + text = { + "All hands start", + "with 0 Chips", + }, + }, + bl_cry_oldpillar = { + name = "Nostalgic Pillar", + text = { + "No Straights", + }, + }, + bl_cry_oldserpent = { + name = "Nostalgic Serpent", + text = { + "Divide Mult by level", + "of played poker hand", + }, + }, + bl_cry_pin = { + name = "The Pin", + text = { + "Jokers with Epic or higher", + "rarity are debuffed", + }, + }, + bl_cry_pinkbow = { + name = "Pink Bow", + text = { + "Randomize rank of cards", + "held in hand on play", + }, + }, + bl_cry_sapphire_stamp = { + name = "Sapphire Stamp", + text = { + "Select an extra card, deselect", + "random card before scoring", + }, + }, + bl_cry_shackle = { + name = "The Shackle", + text = { + "All Negative Jokers", + "are debuffed", + }, + }, + bl_cry_striker = { + name = "The Striker", + text = { + "All Rare Jokers", + "are debuffed", + }, + }, + bl_cry_tax = { + name = "The Tax", + text = { + "Score per hand capped at", + "0.4X blind requirements", + }, + }, + bl_cry_tornado = { + name = "Turquoise Tornado", + text = { + "#1# in #2# chance for", + "played hand to not score", + }, + }, + bl_cry_trick = { + name = "The Trick", + text = { + "After each hand, flip all", + "face-up cards held in hand", + }, + }, + bl_cry_vermillion_virus = { + name = "Vermillion Virus", + text = { + "One random Joker", + "replaced every hand", + }, + }, + bl_cry_windmill = { + name = "The Windmill", + text = { + "All Uncommon Jokers", + "are debuffed", + }, + }, + }, + Code = { + c_cry_class = { + name = "://CLASS", + text = { + "Convert {C:cry_code}#1#{} selected card", + "to a {C:cry_code}chosen{} enhancement", + }, + }, + c_cry_commit = { + name = "://COMMIT", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}new{} Joker", + "of the {C:cry_code}same rarity", + }, + }, + c_cry_crash = { + name = "://CRASH", + text = { + "{C:cry_code,E:1}Don't.", + }, + }, + c_cry_delete = { + name = "://DELETE", + text = { + "{C:cry_code}Permanently{} remove a", + "{C:cry_code}selected{} shop item", + "{C:inactive,s:0.8}Item cannot appear again this run", + }, + }, + c_cry_divide = { + name = "://DIVIDE", + text = { + "{C:cry_code}Halve{} all listed prices", + "in current shop", + }, + }, + c_cry_exploit = { + name = "://EXPLOIT", + text = { + "The {C:cry_code}next{} hand played", + "is calculated as a", + "{C:cry_code}chosen{} poker hand", + "{C:inactive,s:0.8}Secret hands must be", + "{C:inactive,s:0.8}discovered to be valid", + }, + }, + c_cry_hook = { + name = "HOOK://", + text = { + "Select two Jokers", + "to become {C:cry_code}Hooked", + }, + }, + c_cry_machinecode = { + name = "://MACHINECODE", + text = { + "", + }, + }, + c_cry_malware = { + name = "://MALWARE", + text = { "Add {C:dark_edition}Glitched{} to all", "cards {C:cry_code}held in hand" }, + }, + c_cry_merge = { + name = "://MERGE", + text = { + "Merge a selected {C:cry_code}consumable", + "with a selected {C:cry_code}playing card", + }, + }, + c_cry_multiply = { + name = "://MULTIPLY", + text = { + "{C:cry_code}Double{} all values of", + "a selected {C:cry_code}Joker{} until", + "end of round", + }, + }, + c_cry_payload = { + name = "://PAYLOAD", + text = { + "Next defeated Blind", + "gives {C:cry_code}X#1#{} interest", + }, + }, + c_cry_oboe = { + name = "://OFFBYONE", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:cry_code}#1#{} extra card and", + "{C:cry_code}#1#{} extra choice", + "{C:inactive}(Currently {C:cry_code}+#2#{C:inactive})", + }, + }, + c_cry_reboot = { + name = "://REBOOT", + text = { + "Replenish {C:blue}Hands{} and {C:red}Discards{},", + "return {C:cry_code}all{} cards to deck", + "and draw a {C:cry_code}new{} hand", + }, + }, + c_cry_revert = { + name = "://REVERT", + text = { + "Set {C:cry_code}game state{} to", + "start of {C:cry_code}this Ante{}", + }, + }, + c_cry_rework = { + name = "://REWORK", + text = { + "Destroy a {C:cry_code}selected{} Joker,", + "create a {C:cry_code}Rework Tag{} with", + "an {C:cry_code}upgraded{} edition", + "{C:inactive,s:0.8}Upgrades using order in the Collection", + }, + }, + c_cry_run = { + name = "://RUN", + text = { + "Visit a {C:cry_code}shop", + "during a {C:cry_code}Blind", + }, + }, + c_cry_seed = { + name = "://SEED", + text = { + "Select a Joker", + "or playing card", + "to become {C:cry_code}Rigged", + }, + }, + c_cry_semicolon = { + name = ";//", + text = { "Ends current non-Boss {C:cry_code}Blind{}", "{C:cry_code}without{} cashing out" }, + }, + c_cry_spaghetti = { + name = "://SPAGHETTI", + text = { + "Create a {C:cry_code}Glitched", + "Food Joker", + }, + }, + c_cry_variable = { + name = "://VARIABLE", + text = { + "Convert {C:cry_code}#1#{} selected cards", + "to a {C:cry_code}chosen{} rank", + }, + }, + }, + Edition = { + e_cry_astral = { + name = "Astral", + text = { + "{X:dark_edition,C:white}^#1#{} Mult", + }, + }, + e_cry_blur = { + name = "Blurred", + text = { + "{C:attention}Retrigger{} this", + "card {C:attention}1{} time", + "{C:green}#1# in #2#{} chance", + "to retrigger {C:attention}#3#{}", + "additional time", + }, + }, + e_cry_double_sided = { + name = "Double-Sided", + text = { + "This card can be", + "{C:attention}flipped{} to reveal", + "a different card", + "{C:inactive}(Blank side can be merged", + "{C:inactive}with another card)", + }, + }, + e_cry_glass = { + name = "Fragile", + label = "Fragile", + text = { + "{C:white,X:mult} X#3# {} Mult", + "{C:green}#1# in #2#{} chance this", + "card isn't {C:red}destroyed", + "when triggered", + }, + }, + e_cry_glitched = { + name = "Glitched", + text = { + "All values on this card", + "are {C:dark_edition}randomized{}", + "between {C:attention}X0.1{} and {C:attention}X10{}", + "{C:inactive}(If possible){}", + }, + }, + e_cry_gold = { + name = "Golden", + label = "Golden", + text = { + "Earn {C:money}$#1#{} when used", + "or triggered", + }, + }, + e_cry_m = { + name = "Jolly", + text = { + "{C:mult}+#1#{} Mult", + "This card is feeling", + "rather {C:attention}jolly{}", + }, + }, + e_cry_mosaic = { + name = "Mosaic", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + e_cry_noisy = { + name = "Noisy", + text = { + "???", + }, + }, + e_cry_oversat = { + name = "Oversaturated", + text = { + "All values", + "on this card", + "are {C:attention}doubled{}", + "{C:inactive}(If possible)", + }, + }, + }, + Enhanced = { + m_cry_echo = { + name = "Echo Card", + text = { + "{C:green}#2# in #3#{} chance to", + "{C:attention}retrigger{} #1# additional", + "times when scored", + }, + }, + }, + Joker = { + j_cry_altgoogol = { + name = "Nostalgic Googol Play Card", + text = { + "Sell this card to create", + "{C:attention}2{} copies of the leftmost {C:attention}Joker{}", + "{C:inactive,s:0.8}Does not copy Nostalgic Googol Play Cards{}", + }, + }, + j_cry_antennastoheaven = { + name = "...Like Antennas to Heaven", + text = { + "This Joker gains", + "{X:chips,C:white} X#1# {} Chips when each", + "played {C:attention}7{} or {C:attention}4{} is scored", + "{C:inactive}(Currently {X:chips,C:white}X#2# {C:inactive} Chips)", + }, + }, + j_cry_apjoker = { + name = "AP Joker", + text = { "{X:mult,C:white} X#1# {} Mult against {C:attention}Boss Blinds{}" }, + }, + j_cry_big_cube = { + name = "Big Cube", + text = { + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_biggestm = { + name = "Huge", + text = { + "{X:mult,C:white} X#1# {} Mult until end", + "of round if {C:attention}poker hand{}", + "is a {C:attention}#2#{}", + "{C:inactive}(Currently {C:attention}#3#{}{C:inactive}){}", + "{C:inactive,s:0.8}not fat, just big boned.", + }, + }, + j_cry_blender = { + name = "Blender", + text = { + "Create a {C:attention}random{}", + "consumable when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_blurred = { + name = "Blurred Joker", + text = { + "Gain {C:blue}+#1#{} hand(s) when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_bonk = { + name = "Bonk", + text = { + "Each {C:attention}Joker{} gives {C:chips}+#1#{} Chips", + "Increase amount by {C:chips}+#2#{} if", + "{C:attention} poker hand{} is a {C:attention}#3#{}", + "{C:inactive,s:0.8}Jolly Jokers give{} {C:chips,s:0.8}+#4#{} {C:inactive,s:0.8}Chips instead{}", + }, + }, + j_cry_bonusjoker = { + name = "Bonus Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Bonus{} card to increase", + "{C:attention}Joker{} or {C:attention}Consumable slots", + "by {C:dark_edition}1{} when scored", + "{C:red}Works twice per round", + "{C:inactive,s:0.8}(Equal chance for each){}", + }, + }, + j_cry_booster = { + name = "Booster Joker", + text = { + "{C:attention}+#1#{} Booster Pack slot", + "available in shop", + }, + }, + j_cry_boredom = { + name = "Boredom", + text = { + "{C:green}#1# in #2#{} chance to", + "{C:attention}retrigger{} each {C:attention}Joker{}", + "or {C:attention}played card{}", + "{C:inactive,s:0.8}Does not affect other Boredom{}", + }, + }, + j_cry_bubblem = { + name = "Bubble M", + text = { + "Create a {C:dark_edition}Foil {C:attention}Jolly Joker{}", + "if played hand contains", + "a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_busdriver = { + name = "Bus Driver", + text = { + "{C:green}#1# in #3#{} chance", + "for {C:mult}+#2#{} Mult", + "{C:green}1 in 4{} chance", + "for {C:mult}-#2#{} Mult", + }, + }, + j_cry_canvas = { + name = "Canvas", + text = { + "{C:attention}Retrigger{} all {C:attention}Jokers{} to the left", + "once for {C:attention}every{} non-{C:blue}Common{C:attention} Joker{}", + "to the right of this Joker", + }, + }, + j_cry_caramel = { + name = "Caramel", + text = { + "Each played card gives", + "{X:mult,C:white}X#1#{} Mult when scored", + "for the next {C:attention}#2#{} rounds", + }, + }, + j_cry_chad = { + name = "Chad", + text = { + "Retrigger {C:attention}leftmost{} Joker", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_chili_pepper = { + name = "Chili Pepper", + text = { + "This Joker gains {X:mult,C:white} X#2# {} Mult", + "at end of round,", + "{C:red,E:2}self destructs{} after {C:attention}#3#{} rounds", + "{C:inactive}(Currently{} {X:mult,C:white} X#1# {} {C:inactive}Mult){}", + }, + }, + j_cry_circulus_pistoris = { + name = "Circulus Pistoris", + text = { + "{X:dark_edition,C:white}^#1#{} Chips and {X:dark_edition,C:white}^#1#{} Mult", + "if {C:attention}exactly{} #2#", + "hands remaining", + }, + }, + j_cry_circus = { + name = "Circus", + text = { + "{C:red}Rare{} Jokers each give {X:mult,C:white} X#1# {} Mult", + "{C:cry_epic}Epic{} Jokers each give {X:mult,C:white} X#2# {} Mult", + "{C:legendary}Legendary{} Jokers each give {X:mult,C:white} X#3# {} Mult", + "{C:cry_exotic}Exotic{} Jokers each give {X:mult,C:white} X#4# {} Mult", + }, + }, + j_cry_CodeJoker = { + name = "Code Joker", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:cry_code}Code Card{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_coin = { + name = "Crypto Coin", + text = { + "Earn between", + "{C:money}$#1#{} and {C:money}$#2#{} for", + "each Joker {C:attention}sold{}", + }, + }, + j_cry_compound_interest = { + name = "Compound Interest", + text = { + "Earn {C:money}#1#%{} of total money", + "at end of round,", + "increases by {C:money}#2#%{} per", + "consecutive payout", + }, + }, + j_cry_copypaste = { + name = "Copy/Paste", + text = { + "When a {C:cry_code}Code{} card is used,", + "{C:green}#1# in #2#{} chance to add a copy", + "to your consumable area", + "{C:inactive}(Must have room)", + }, + }, + j_cry_crustulum = { + name = "Crustulum", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "per {C:attention}reroll{} in the shop", + "{C:green}All rerolls are free{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} chips)", + }, + }, + j_cry_cryptidmoment = { + name = "M Chain", + text = { + "Sell this card to", + "add {C:money}$#1#{} of {C:attention}sell value{}", + "to every {C:attention}Joker{} card", + }, + }, + j_cry_cube = { + name = "Cube", + text = { + "{C:chips}+#1#{} Chips", + }, + }, + j_cry_curse_sob = { + name = "Sob", + text = { + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}run...{}", + "{C:edition,E:1}you cannot{} {C:cry_ascendant,E:1}hide...{}", + "{C:dark_edition,E:1}you cannot escape...{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_cursor = { + name = "Cursor", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "for each card {C:attention}purchased{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_cut = { + name = "Cut", + text = { + "This Joker destroys", + "a random {C:cry_code}Code{} card", + "and gains {X:mult,C:white} X#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_delirious = { + name = "Delirious Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_discreet = { + name = "Discreet Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_doodlem = { + name = "Doodle M", + text = { + "Create 2 {C:dark_edition}Negative{} {C:attention}consumables{}", + "when {C:attention}Blind{} is selected", + "Create 1 more {C:attention}consumable", + "for each {C:attention}Jolly Joker{}", + }, + }, + ["j_cry_Double Scale"] = { + name = "Double Scale", + text = { + "Scaling {C:attention}Jokers{}", + "scale {C:attention}quadratically", + "{C:inactive,s:0.8}(ex. +1, +3, +6, +10)", + "{C:inactive,s:0.8}(grows by +1, +2, +3)", + }, + }, + j_cry_dropshot = { + name = "Dropshot", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult for", + "each played, {C:attention}nonscoring{} {V:1}#2#{} card,", + "suit changes every round", + "{C:inactive}(Currently {X:mult,C:white} X#3# {C:inactive} Mult)", + }, + }, + j_cry_dubious = { + name = "Dubious Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_duos = { + name = "The Duos", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_duplicare = { + name = 'Duplicare', + text = { + "Every {C:attention}Joker{} gives", + "{X:dark_edition,C:white}^#1#{} Mult" + } + }, + j_cry_effarcire = { + name = "Effarcire", + text = { + "Draw {C:green}full deck{} to hand", + "when {C:attention}Blind{} is selected", + "{C:inactive,s:0.8}\"If you can't handle me at my 1x,", + "{C:inactive,s:0.8}you don't deserve me at my 2x\"", + }, + }, + j_cry_energia = { + name = "Energia", + text = { + "When a {C:attention}Tag{} is acquired,", + "create {C:attention}#1#{} copies of it", + "and {C:attention}increase{} the number of", + "copies by {C:attention}#2#", + }, + }, + j_cry_equilib = { + name = "Ace Aequilibrium", + text = { + "Jokers appear using the", + "order from the {C:attention}Collection{}", + "Create {C:attention}#1#{} {C:dark_edition}Negative{} Joker(s)", + "when hand is played", + "{C:cry_exotic,s:0.8}Exotic {C:inactive,s:0.8}or better Jokers cannot appear", + "{s:0.8}Last Joker Generated: {C:attention,s:0.8}#2#", + }, + }, + j_cry_error = { + name = "{C:red}ERR{}{C:dark_edition}O{}{C:red}R{}", + text = { + "", + }, + }, + j_cry_eternalflame = { + name = "Eternal Flame", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "for each card {C:attention}sold{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_exoplanet = { + name = "Exoplanet", + text = { + "{C:dark_edition}Holographic{} cards", + "each give {C:mult}+#1#{} Mult", + }, + }, + j_cry_exponentia = { + name = "Exponentia", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "when {X:red,C:white} XMult {} is triggered", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_facile = { + name = "Facile", + text = { + "{X:dark_edition,C:white}^#1#{} Mult if", + "played cards are scored", + "{C:attention}#2#{} or fewer times", + }, + }, + j_cry_filler = { + name = "The Filler", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_fractal = { + name = "Fractal Fingers", + text = { + "{C:attention}+#1#{} card selection limit", + }, + }, + j_cry_flip_side = { + name = "On the Flip Side", + text = { + "{C:dark_edition}Double-Sided{} Jokers use", + "their back side for effects", + "instead of the front side", + "{C:attention}Retrigger{} all {C:dark_edition}Double-Sided{} Jokers" + }, + }, + j_cry_foodm = { + name = "Fast Food M", + text = { + "{C:mult}+#1#{} Mult", + "{C:red,E:2}self destructs{} in {C:attention}#2#{} round(s)", + "Increases by {C:attention}#3#{} round when", + "{C:attention}Jolly Joker{} is {C:attention}sold{}", + "{C:inactive,s:0.8}2 McDoubles, 2 McChickens{}", + "{C:inactive,s:0.8}Large Fries, 20 Piece & Large Cake{}", + }, + }, + j_cry_foxy = { + name = "Foxy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_fspinner = { + name = "Fidget Spinner", + text = { + "This Joker gains {C:chips}+#2#{} Chips", + "if hand played is {C:attention}not{}", + "most played {C:attention}poker hand{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_gardenfork = { + name = "Garden of Forking Paths", + text = { + "Earn {C:money}$#1#{} if {C:attention}played hand{}", + "contains an {C:attention}Ace{} and a {C:attention}7{}", + }, + }, + j_cry_gemino = { + name = "Gemini", + text = { + "{C:attention}Double{} all values", + "of leftmost {C:attention}Joker", + "at end of round", + }, + }, + j_cry_giggly = { + name = "Absurd Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_goldjoker = { + name = "Gold Joker", + text = { + "Earn {C:money}#1#%{} of total", + "money at end of round", + "Payout increases by {C:money}#2#%{}", + "when each played {C:attention}Gold{}", + "card is scored", + }, + }, + j_cry_googol_play = { + name = "Googol Play Card", + text = { + "{C:green}#1# in #2#{} chance for", + "{X:red,C:white} X#3# {} Mult", + }, + }, + j_cry_happy = { + name = ":D", + text = { + "Create a random {C:attention}Joker{}", + "at end of round", + "Sell this card to", + "create a random {C:attention}Joker{}", + "{C:inactive}(Must have room){}", + }, + }, + j_cry_happyhouse = { + name = "Happy House", + text = { + "{X:dark_edition,C:white}^#1#{} Mult only after", + "playing {C:attention}114{} hands{}", + "{C:inactive}(Currently #2#/114){}", + "{C:inactive,s:0.8}There is no place like home!{}", + }, + }, + j_cry_home = { + name = "The Home", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_hunger = { + name = "Consume-able", + text = { + "Earn {C:money}$#1#{} when", + "using a {C:attention}consumable{}", + }, + }, + j_cry_iterum = { + name = "Iterum", + text = { + "Retrigger all cards played", + "{C:attention}#2#{} time(s),", + "each played card gives", + "{X:mult,C:white} X#1# {} Mult when scored", + }, + }, + j_cry_jimball = { + name = "Jimball", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "per {C:attention}consecutive{} hand played", + "while playing your", + "most played {C:attention}poker hand", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_jollysus = { + name = "Jolly Joker?", + text = { + "Create a {C:dark_edition}Jolly{} Joker", + "when a Joker is {C:attention}sold{}", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + "{C:inactive,s:0.8}Seems legit...{}", + }, + }, + j_cry_kidnap = { + name = "Kidnapping", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{}", + "when a {C:attention}Type Mult{} or", + "{C:attention}Type Chips{} Joker is sold", + }, + }, + j_cry_kooky = { + name = "Kooky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_krustytheclown = { + name = "Krusty the Clown", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when", + "each played {C:attention}card{} is scored", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_kscope = { + name = "Kaleidoscope", + text = { + "Add {C:dark_edition}Polychrome{} to", + "a random {C:attention}Joker{} when", + "{C:attention}Boss Blind{} is defeated", + }, + }, + j_cry_lightupthenight = { + name = "Light Up the Night", + text = { + "Each played {C:attention}7{} or {C:attention}2{}", + "gives {X:mult,C:white}X#1#{} Mult when scored", + }, + }, + j_cry_longboi = { + name = "Monster", + text = { + "Give future copies of", + "this Joker {X:mult,C:white}X#1#{} Mult", + "at end of round", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult){}", + }, + }, + j_cry_loopy = { + name = "Loopy", + text = { + "{C:attention}Retrigger{} all Jokers", + "once for each {C:attention}Jolly{}", + "{C:attention}Joker{} sold this round", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} Retrigger(s)){}", + "{C:inactive,s:0.8}There wasn't enough room...{}", + }, + }, + j_cry_lucky_joker = { + name = "Lucky Joker", + text = { + "Earn {C:money}$#1#{} every time a", + "{C:attention}Lucky{} card {C:green}successfully{}", + "triggers", + }, + }, + j_cry_luigi = { + name = "Luigi", + text = { + "All Jokers give", + "{X:chips,C:white} X#1# {} Chips", + }, + }, + j_cry_m = { + name = "m", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "when {C:attention}Jolly Joker{} is sold", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_M = { + name = "M", + text = { + "Create a {C:dark_edition}Negative{}", + "{C:attention}Jolly Joker{} when", + "{C:attention}Blind{} is selected", + }, + }, + j_cry_macabre = { + name = "Macabre Joker", + text = { + "When {C:attention}Blind{} is selected,", + "destroys each {C:attention}Joker{} except", + "{C:legendary}M Jokers{} and {C:attention}Jolly Jokers{}", + "and create 1 {C:attention}Jolly Joker{}", + "for each destroyed card", + }, + }, + j_cry_magnet = { + name = "Fridge Magnet", + text = { + "Earn {C:money}$#1#{} at end of round", + "This earns {X:money,C:white} X#2# {} if there are", + "{C:attention}#3#{} or fewer {C:attention}Joker{} cards", + }, + }, + j_cry_manic = { + name = "Manic Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_mario = { + name = "Mario", + text = { + "Retrigger all Jokers", + "{C:attention}#1#{} additional time(s)", + }, + }, + j_cry_maximized = { + name = "Maximized", + text = { + "All {C:attention}face{} cards", + "are considered {C:attention}Kings{},", + "all {C:attention}numbered{} cards", + "are considered {C:attention}10s{}", + }, + }, + j_cry_maze = { + name = "Labyrinth", + text = { + "All hands are considered the", + "{C:attention}first hand{} of each round,", + "all discards are considered the", + "{C:attention}first discard{} of each round", + }, + }, + j_cry_Megg = { + name = "Megg", + text = { + "Sell this card to create", + "{C:attention}#2#{} Jolly #3#, increase", + "by {C:attention}#1#{} at end of round", + }, + }, + j_cry_membershipcard = { + name = "Membership Card", + text = { + "{X:mult,C:white}X#1#{} Mult for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {X:mult,C:white}X#2#{C:inactive} Mult)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_membershipcardtwo = { + name = "Old Membership Card", --Could probably have a diff Name imo + text = { + "{C:chips}+#1#{} Chips for each member", + "in the {C:attention}Cryptid Discord{}", + "{C:inactive}(Currently {C:chips}+#2#{C:inactive} Chips)", + "{C:blue,s:0.7}https://discord.gg/eUf9Ur6RyB{}", + }, + }, + j_cry_meteor = { + name = "Meteor Shower", + text = { + "{C:dark_edition}Foil{} cards each", + "give {C:chips}+#1#{} Chips", + }, + }, + j_cry_mneon = { + name = "Neon M", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by", + "{C:money}$#1#{} for each {C:attention}Jolly Joker{}", + "or {C:legendary}M Joker{} at", + "end of round", + }, + }, + j_cry_mondrian = { + name = "Mondrian", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if no {C:attention}discards{} were", + "used this round", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_monkey_dagger = { + name = "Monkey Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and permanently add {C:attention}ten times{}", + "its sell value to this {C:chips}Chips{}", + "{C:inactive}(Currently {C:chips}+#1#{C:inactive} Chips)", + }, + }, + j_cry_morse = { + name = "Morse Code", + text = { + "Earn {C:money}$#2#{} at end of round", + "Increase payout by {C:money}$#1#{} when", + "a card with an {C:attention}Edition{} is sold", + }, + }, + j_cry_mprime = { + name = "Tredecim", + text = { + "Create an {C:legendary}M Joker{} at end of round", + "Each {C:attention}Jolly Joker{} or {C:legendary}M Joker", + "gives {X:dark_edition,C:white}^#1#{} Mult", + "Increase amount by {X:dark_edition,C:white}^#2#{}", + "when a {C:attention}Jolly Joker{} is {C:attention}sold", + "{C:inactive,s:0.8}(Tredecim excluded)", + }, + }, + j_cry_mstack = { + name = "M Stack", + text = { + "Retrigger all cards played", + "once for every", + "{C:attention}#2#{} {C:inactive}[#3#]{} {C:attention}Jolly Jokers{} sold", + "{C:inactive}(Currently{}{C:attention:} #1#{}{C:inactive} retriggers){}", + }, + }, + j_cry_multjoker = { + name = "Mult Joker", + text = { + "{C:green}#1# in #2#{} chance for each", + "played {C:attention}Mult{} card to create", + "a {C:spectral}Cryptid{} card when scored", + "{C:inactive}(Must have room)", + }, + }, + j_cry_negative = { + name = "Negative Joker", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + }, + }, + j_cry_nice = { + name = "Nice", + text = { + "{C:chips}+#1#{} Chips if played hand", + "contains a {C:attention}6{} and a {C:attention}9", + "{C:inactive,s:0.8}Nice.{}", + }, + }, + j_cry_night = { + name = "Night", + text = { + "{X:dark_edition,C:white}^#1#{} Mult on final", + "hand of round", + "{E:2,C:red}self destructs{} on", + "final hand of round", + }, + }, + j_cry_nosound = { + name = "No Sound, No Memory", + text = { + "Retrigger each played {C:attention}7{}", + "{C:attention:}#1#{} additional time(s)", + }, + }, + j_cry_notebook = { + name = "Notebook", + text = { + "{C:green} #1# in #2#{} chance to gain {C:dark_edition}+1{} Joker", + "slot per {C:attention}reroll{} in the shop", + "{C:green}Always triggers{} if there are", + "{C:attention}#5#{} or more {C:attention}Jolly Jokers{}", + "{C:red}Works once per round{}", + "{C:inactive}(Currently {C:dark_edition}+#3#{}{C:inactive} and #4#){}", + }, + }, + j_cry_number_blocks = { + name = "Number Blocks", + text = { + "Earn {C:money}$#1#{} at end of round", + "Increase payout by {C:money}$#2#{}", + "for each {C:attention}#3#{} held in hand,", + "rank changes every round", + }, + }, + j_cry_nuts = { + name = "The Nuts", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_nutty = { + name = "Nutty Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_oldblueprint = { + name = "Old Blueprint", + text = { + "Copies ability of", + "{C:attention}Joker{} to the right", + "{C:green}#1# in #2#{} chance this", + "card is destroyed", + "at end of round", + }, + }, + j_cry_oldcandy = { + name = "Nostalgic Candy", + text = { + "Sell this card to", + "permanently gain", + "{C:attention}+#1#{} hand size", + }, + }, + j_cry_oldinvisible = { + name = "Nostalgic Invisible Joker", + text = { + "{C:attention}Duplicate{} a random", + "{C:attention}Joker{} every {C:attention}4", + "Joker cards sold", + "{s:0.8}Nostalgic Invisible Joker Excluded{}", + "{C:inactive}(Currently #1#/4){}", + }, + }, + j_cry_panopticon = { + name = "Panopticon", + text = { + "All hands are considered the", + "{C:attention}last hand{} of each round", -- +$4 + }, + }, + j_cry_pickle = { + name = "Pickle", + text = { + "When {C:attention}Blind{} is skipped, create", + "{C:attention}#1#{} Tags, reduced by", + "{C:red}#2#{} when {C:attention}Blind{} is selected", + }, + }, + j_cry_pirate_dagger = { + name = "Pirate Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the right", + "and gain {C:attention}one-fourth{} of", + "its sell value as {X:chips,C:white} XChips {}", + "{C:inactive}(Currently {X:chips,C:white} X#1# {C:inactive} Chips)", + }, + }, + j_cry_pot_of_jokes = { + name = "Pot of Jokes", + text = { + "{C:attention}#1#{} hand size,", + "increases by", + "{C:blue}#2#{} every round", + }, + }, + j_cry_primus = { + name = "Primus", + text = { + "This Joker gains {X:dark_edition,C:white} ^#1# {} Mult", + "if all cards in played hand are", + "{C:attention}Aces{}, {C:attention}2s{}, {C:attention}3s{}, {C:attention}5s{}, or {C:attention}7s{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_python = { + name = "Python", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when a", + "{C:cry_code}Code{} card is used", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_queens_gambit = { + name = "Queen's Gambit", + text = { + "If {C:attention}poker hand{} is a", + "{C:attention}Royal Flush{}, destroy scored", + "{C:attention}Queen{} and create a", + "{C:dark_edition}Negative {}{C:red}Rare{}{C:attention} Joker{}", + }, + }, + j_cry_quintet = { + name = "The Quintet", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_redbloon = { + name = "Red Bloon", + text = { + "Earn {C:money}$#1#{} in {C:attention}#2#{} round(s)", + "{C:red,E:2}self destructs{}", + }, + }, + j_cry_redeo = { + name = "Redeo", + text = { + "{C:attention}-#1#{} Ante when", + "{C:money}$#2#{} {C:inactive}($#3#){} spent", + "{s:0.8}Requirements increase", + "{C:attention,s:0.8}exponentially{s:0.8} per use", + "{C:money,s:0.8}Next increase: {s:1,c:money}$#4#", + }, + }, + j_cry_rescribere = { + name = 'Rescribere', + text = { + "When a {C:attention}Joker{} is sold,", + "add its effects to", + "every other Joker", + "{C:inactive,s:0.8}Does not affect other Rescribere{}" + } + }, + j_cry_reverse = { + name = "Reverse Card", + text = { + "Fill all empty Joker slots {C:inactive}(Max 100){}", + "with {C:dark_edition}Holographic{} {C:attention}Jolly Jokers{} if", + "{C:attention}discarded poker hand{} is a {C:attention}#1#{}", + "{C:red,E:2}self destructs{}", + "{C:inactive,s:0.8}The ULTIMATE comeback{}", + }, + }, + j_cry_rnjoker = { + name = "RNJoker", + text = { + "Randomize abilities each {C:attention}Ante{}", + }, + }, + j_cry_sacrifice = { + name = "Sacrifice", + text = { + "Create an {C:green}Uncommon{} Joker", + "and 3 {C:attention}Jolly Jokers{} when", + "a {C:spectral}Spectral{} card is used", + "{C:red}Works once per round{}", + "{C:inactive}#1#{}", + }, + }, + j_cry_sapling = { + name = "Sapling", + text = { + "After scoring {C:attention}#2#{} {C:inactive}[#1#]{} Enhanced", + "cards, sell this card to", + "create an {C:cry_epic}Epic{} {C:attention}Joker{}", + "{C:inactive,s:0.8}Will create a {C:red,s:0.8}Rare{} {C:attention,s:0.8}Joker{}", + "{C:inactive,s:0.8}if {C:cry_epic,s:0.8}Epic{} {C:inactive,s:0.8}Jokers are disabled{}", + }, + }, + j_cry_savvy = { + name = "Savvy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_Scalae = { + name = "Scalae", + text = { + "Scaling {C:attention}Jokers{} scale", + "as a degree-{C:attention}#1#{} polynomial", + "raise degree by {C:attention}#2#{}", + "at end of round", + "{C:inactive,s:0.8}({C:attention,s:0.8}Scalae{C:inactive,s:0.8} excluded)", + }, + }, + j_cry_scrabble = { + name = "Scrabble Tile", + text = { + "{C:green}#1# in #2#{} chance to create", + "a {C:dark_edition}Jolly {C:green}Uncommon{} Joker", + "when hand is played", + }, + }, + j_cry_seal_the_deal = { + name = "Seal the Deal", + text = { + "Add a {C:attention}random seal{} to each card", + "scored on {C:attention}final hand{} of round", + }, + }, + j_cry_shrewd = { + name = "Shrewd Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_silly = { + name = "Silly Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_smallestm = { + name = "Tiny", + text = { + "Create a {C:cry_jolly}Double M", + "tag if {C:attention}poker hand{}", + "is a {C:attention}#1#{}", + "{C:inactive,s:0.8}ok so basically i'm very smol", + }, + }, + j_cry_soccer = { + name = "One for All", --changed the name from latin because this isn't exotic + text = { + "{C:attention}+#1#{} Joker slot", + "{C:attention}+#1#{} Booster Pack slot", + "{C:attention}+#1#{} hand size", + "{C:attention}+#1#{} consumable slot", + "{C:attention}+#1#{} card in shop", + }, + }, + j_cry_spaceglobe = { + name = "Celestial Globe", + text = { + "This Joker gains {X:chips,C:white}X#2#{} Chips", + "if {C:attention}poker hand{} is a {C:attention}#3#{},", + "Hand changes after increase{}", + "{C:inactive}(Currently{} {X:chips,C:white}X#1#{} {C:inactive}Chips){}", + }, + }, + j_cry_speculo = { + name = "Speculo", + text = { + "Creates a {C:dark_edition}Negative{} copy", + "of a random {C:attention}Joker{}", + "at the end of the {C:attention}shop", + "{C:inactive,s:0.8}Does not copy other Speculo{}", + }, + }, + j_cry_stardust = { + name = "Stardust", + text = { + "{C:dark_edition}Polychrome{} cards", + "each give {X:mult,C:white}X#1#{} Mult", + }, + }, + j_cry_stella_mortis = { + name = "Stella Mortis", + text = { + "This Joker destroys a", + "random {C:planet}Planet{} card", + "and gains {X:dark_edition,C:white} ^#1# {} Mult", + "at the end of the {C:attention}shop{}", + "{C:inactive}(Currently {X:dark_edition,C:white} ^#2# {C:inactive} Mult)", + }, + }, + j_cry_subtle = { + name = "Subtle Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_supercell = { + name = "Supercell", + text = { + "{C:chips}+#1#{} Chips, {C:mult}+#1#{} Mult,", + "{X:chips,C:white}X#2#{} Chips, {X:mult,C:white}X#2#{} Mult", + "Earn {C:money}$#3#{} at", + "end of round", + }, + }, + j_cry_sus = { + name = "SUS", + text = { + "At end of round, create", + "a {C:attention}copy{} of a random", + "card {C:attention}held in hand{},", + "destroy all others", + "{C:attention,s:0.8}Kings{s:0.8} of {C:hearts,s:0.8}Hearts{s:0.8} are prioritized", + }, + }, + j_cry_swarm = { + name = "The Swarm", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_sync_catalyst = { + name = "Sync Catalyst", + text = { + "Balances {C:chips}Chips{} and {C:mult}Mult{}", + "{C:inactive,s:0.8}Hey! I've seen this one before!", + }, + }, + j_cry_tenebris = { + name = "Tenebris", + text = { + "{C:dark_edition}+#1#{C:attention} Joker{} slots", + "Earn {C:money}$#2#{} at end of round", + }, + }, + j_cry_translucent = { + name = "Translucent Joker", + text = { + "Sell this card to create", + "a {C:attention}Banana Perishable{} copy", + "of a random {C:attention}Joker{}", + "{s:0.8,C:inactive}(Copy bypasses perish compat)", + }, + }, + j_cry_tricksy = { + name = "Tricksy Joker", + text = { + "{C:chips}+#1#{} Chips if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_triplet_rhythm = { + name = "Triplet Rhythm", + text = { + "{X:mult,C:white} X#1# {} Mult if scoring hand", + "contains {C:attention}exactly{} three {C:attention}3s", + }, + }, + j_cry_unity = { + name = "The Unity", + text = { + "{X:mult,C:white} X#1# {} Mult if played", + "hand contains", + "a {C:attention}#2#", + }, + }, + j_cry_universum = { + name = "Universum", + text = { + "{C:attention}Poker hands{} gain", + "{X:red,C:white} X#1# {} Mult and {X:blue,C:white} X#1# {} Chips", + "when leveled up", + }, + }, + j_cry_unjust_dagger = { + name = "Unjust Dagger", + text = { + "When {C:attention}Blind{} is selected,", + "destroy Joker to the left", + "and gain {C:attention}one-fifth{} of", + "its sell value as {X:mult,C:white} XMult {}", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_verisimile = { + name = "Non Verisimile", + text = { + "When any probability", + "is {C:green}successfully{} triggered,", + "this Joker gains {X:red,C:white}XMult{}", + "equal to its listed {C:attention}odds", + "{C:inactive}(Currently {X:mult,C:white} X#1# {C:inactive} Mult)", + }, + }, + j_cry_virgo = { + name = "Virgo", + text = { + "This Joker gains {C:money}$#1#{} of {C:attention}sell value{}", + "if {C:attention}poker hand{} contains a {C:attention}#2#{}", + "Sell this card to create a", + "{C:dark_edition}Polychrome{} {C:attention}Jolly Joker{} for", + "every {C:money}$4{} of {C:attention}sell value{} {C:inactive}(Min 1){}", + }, + }, + j_cry_wacky = { + name = "Wacky Joker", + text = { + "{C:red}+#1#{} Mult if played", + "hand contains", + "a {C:attention}#2#" + } + }, + j_cry_waluigi = { + name = "Waluigi", + text = { + "All Jokers give", + "{X:mult,C:white} X#1# {} Mult", + }, + }, + j_cry_wario = { + name = "Wario", + text = { + "All Jokers give", + "{C:money}$#1#{} when triggered", + }, + }, + j_cry_wee_fib = { + name = "Weebonacci", + text = { + "This Joker gains", + "{C:mult}+#2#{} Mult when each played", + "{C:attention}Ace{}, {C:attention}2{}, {C:attention}3{}, {C:attention}5{}, or {C:attention}8{}", + "is scored", + "{C:inactive}(Currently {C:mult}+#1#{C:inactive} Mult)", + }, + }, + j_cry_weegaming = { + name = "2D", + text = { + "Retrigger each played {C:attention}2{}", --wee gaming + "{C:attention:}#1#{} additional time(s)", --wee gaming? + "{C:inactive,s:0.8}Wee Gaming?{}", + }, + }, + j_cry_wheelhope = { + name = "Wheel of Hope", + text = { + "This Joker gains", + "{X:mult,C:white} X#1# {} Mult when failing", + "a {C:attention}Wheel of Fortune{}", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + j_cry_whip = { + name = "The WHIP", + text = { + "This Joker gains {X:mult,C:white} X#1# {} Mult", + "if {C:attention}played hand{} contains a", + "{C:attention}2{} and {C:attention}7{} of different suits", + "{C:inactive}(Currently {X:mult,C:white} X#2# {C:inactive} Mult)", + }, + }, + }, + Planet = { + c_cry_Klubi = { + name = "Klubi", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Lapio = { + name = "Lapio", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_nstar = { + name = "Neutron Star", + text = { + "Upgrade a random", + "poker hand by", + "{C:attention}1{} level for each", + "{C:attention}Neutron Star{} used", + "in this run", + "{C:inactive}(Currently{C:attention} #1#{C:inactive}){}", + }, + }, + c_cry_planetlua = { + name = "Planet.lua", + text = { + "{C:green}#1# in #2#{} chance to", + "upgrade every", + "{C:legendary,E:1}poker hand{}", + "by {C:attention}1{} level", + }, + }, + c_cry_Sydan = { + name = "Sydan", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + c_cry_Timantti = { + name = "Timantti", + text = { + "({V:1}lvl.#4#{})({V:2}lvl.#5#{})({V:3}lvl.#6#{})", + "Level up", + "{C:attention}#1#{},", + "{C:attention}#2#{},", + "and {C:attention}#3#{}", + }, + }, + }, + Sleeve = { + sleeve_cry_ccd_sleeve = { + name = "CCD Sleeve", + text = { + "Every card is also", + "a {C:attention}random{} consumable", + }, + }, + sleeve_cry_conveyor_sleeve = { + name = "Conveyor Sleeve", + text = { + "Jokers may {C:attention}not{} be moved", + "At start of round,", + "{C:attention}duplicate{} rightmost Joker", + "and {C:attention}destroy{} leftmost Joker", + }, + }, + sleeve_cry_critical_sleeve = { + name = "Critical Sleeve", + text = { + "After each hand played,", + "{C:green}1 in 4{} chance for {X:dark_edition,C:white} ^2 {} Mult", + "{C:green}1 in 8{} chance for {X:dark_edition,C:white} ^0.5 {} Mult", + }, + }, + sleeve_cry_encoded_sleeve = { + name = "Encoded Sleeve", + text = { + "Start with a {C:cry_code,T:j_cry_CodeJoker}Code Joker{}", + "and a {C:cry_code,T:j_cry_copypaste}Copy/Paste{}", + "Only {C:cry_code}Code Cards{} appear in shop", + }, + }, + sleeve_cry_equilibrium_sleeve = { + name = "Balanced Sleeve", + text = { + "All cards have the", + "{C:attention}same chance{} of", + "appearing in shops,", + "start run with", + "{C:attention,T:v_overstock_plus}+2 Shop Slots", + }, + }, + sleeve_cry_infinite_sleeve = { + name = "Unlimited Sleeve", + text = { + "You can select {C:attention}any", + "number of cards", + --someone do the hand size thing for me + }, + }, + sleeve_cry_misprint_sleeve = { + name = "Misprinted Sleeve", + text = { + "Values of cards", + "are {C:attention}randomized", + }, + }, + sleeve_cry_redeemed_sleeve = { + name = "Redeemed Sleeve", + text = { + "When a {C:attention}Voucher{} is purchased,", + "gain its {C:attention}extra tiers", + }, + }, + sleeve_cry_wormhole_sleeve = { + name = "Wormhole Sleeve", + text = { + "Start with an {C:cry_exotic}Exotic{C:attention} Joker", + "Jokers are {C:attention}20X{} more", + "likely to be {C:dark_edition}Negative", + "{C:attention}-2{} Joker slots", + }, + }, + }, + Spectral = { + c_cry_analog = { + name = "Analog", + text = { + "Create {C:attention}#1#{} copies of a", + "random {C:attention}Joker{}, destroy", + "all other Jokers, {C:attention}+#2#{} Ante", + }, + }, + c_cry_gateway = { + name = "Gateway", + text = { + "Create a random", + "{C:cry_exotic,E:1}Exotic{C:attention} Joker{}, destroy", + "all other Jokers", + }, + }, + c_cry_hammerspace = { + name = "Hammerspace", + text = { + "Apply random {C:attention}consumables{}", + "as if they were {C:dark_edition}Enhancements{}", + "to cards held in hand", + }, + }, + c_cry_lock = { + name = "Lock", + text = { + "Remove {C:red}all{} stickers", + "from {C:red}all{} Jokers,", + "then apply {C:purple,E:1}Eternal{}", + "to a random {C:attention}Joker{}", + }, + }, + c_cry_pointer = { + name = "POINTER://", + text = { + "Create a card", + "of {C:cry_code}your choice", + "{C:inactive,s:0.8}(Exotic Jokers #1#excluded)", + }, + }, + c_cry_replica = { + name = "Replica", + text = { + "Convert all cards", + "held in hand", + "to a {C:attention}random{}", + "card held in hand", + }, + }, + c_cry_source = { + name = "Source", + text = { + "Add a {C:cry_code}Green Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_summoning = { + name = "Summoning", + text = { + "Create a random", + "{C:cry_epic}Epic{} {C:joker}Joker{}, destroy", + "one random {C:joker}Joker{}", + }, + }, + c_cry_trade = { + name = "Trade", + text = { + "{C:attention}Lose{} a random Voucher,", + "gain {C:attention}2{} random Vouchers", + }, + }, + c_cry_typhoon = { + name = "Typhoon", + text = { + "Add an {C:cry_azure}Azure Seal{}", + "to {C:attention}#1#{} selected", + "card in your hand", + }, + }, + c_cry_vacuum = { + name = "Vacuum", + text = { + "Removes {C:red}all {C:green}modifications{}", + "from {C:red}all{} cards held in hand,", + "Earn {C:money}$#1#{} per {C:green}modification{} removed", + "{C:inactive,s:0.7}(ex. Enhancements, Seals, Editions)", + }, + }, + c_cry_white_hole = { + name = "White Hole", + text = { + "{C:attention}Remove{} all hand levels,", + "upgrade {C:legendary,E:1}most played{} poker hand", + "by {C:attention}3{} for each removed level", + }, + }, + }, + Stake = { + stake_cry_pink = { + name = "Pink Stake", + colour = "Pink", --this is used for auto-generated sticker localization + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_brown = { + name = "Brown Stake", + colour = "Brown", + text = { + "All {C:attention}stickers{} are compatible", + "with each other", + }, + }, + stake_cry_yellow = { + name = "Yellow Stake", + colour = "Yellow", + text = { + "{C:attention}Stickers{} can appear on", + "all purchasable items", + }, + }, + stake_cry_jade = { + name = "Jade Stake", + colour = "Jade", + text = { + "Cards can be drawn {C:attention}face down{}", + }, + }, + stake_cry_cyan = { + name = "Cyan Stake", + colour = "Cyan", + text = { + "{C:green}Uncommon{} and {C:red}Rare{} Jokers are", + "less likely to appear", + }, + }, + stake_cry_gray = { + name = "Gray Stake", + colour = "Gray", + text = { + "Rerolls increase by {C:attention}$2{} each", + }, + }, + stake_cry_crimson = { + name = "Crimson Stake", + colour = "Crimson", + text = { + "Vouchers restock on {C:attention}even{} Antes", + }, + }, + stake_cry_diamond = { + name = "Diamond Stake", + colour = "Diamond", + text = { + "Must beat Ante {C:attention}10{} to win", + }, + }, + stake_cry_amber = { + name = "Amber Stake", + colour = "Amber", + text = { + "{C:attention}-1{} Booster Pack slot", + }, + }, + stake_cry_bronze = { + name = "Bronze Stake", + colour = "Bronze", + text = { + "Vouchers are {C:attention}50%{} more expensive", + }, + }, + stake_cry_quartz = { + name = "Quartz Stake", + colour = "Quartz", + text = { + "Jokers can be {C:attention}Pinned{}", + "{s:0.8,C:inactive}(Stays pinned to the leftmost position){}", + }, + }, + stake_cry_ruby = { + name = "Ruby Stake", + colour = "Ruby", + text = { + "{C:attention}Big{} Blinds can become", + "{C:attention}Boss{} Blinds", + }, + }, + stake_cry_glass = { + name = "Glass Stake", + colour = "Glass", + text = { + "Cards can {C:attention}shatter{} when scored", + }, + }, + stake_cry_sapphire = { + name = "Sapphire Stake", + colour = "Sapphire", + text = { + "Lose {C:attention}25%{} of current money", + "at end of Ante", + "{s:0.8,C:inactive}(Up to $10){}", + }, + }, + stake_cry_emerald = { + name = "Emerald Stake", + colour = "Emerald", + text = { + "Cards, packs, and vouchers", + "can be {C:attention}face down{}", + "{s:0.8,C:inactive}(Unable to be viewed until purchased){}", + }, + }, + stake_cry_platinum = { + name = "Platinum Stake", + colour = "Platinum", + text = { + "Small Blinds are {C:attention}removed{}", + }, + }, + stake_cry_twilight = { + name = "Twilight Stake", + colour = "Twilight", + text = { + "Cards can be {C:attention}Banana{}", + "{s:0.8,C:inactive}(1 in 10 chance of being destroyed each round){}", + }, + }, + stake_cry_verdant = { + name = "Verdant Stake", + colour = "Verdant", + text = { + "Required score scales", + "faster for each {C:attention}Ante", + }, + }, + stake_cry_ember = { + name = "Ember Stake", + colour = "Ember", + text = { + "All items give no money when sold", + }, + }, + stake_cry_dawn = { + name = "Dawn Stake", + colour = "Dawn", + text = { + "Tarots and Spectrals target {C:attention}1", + "fewer card", + "{s:0.8,C:inactive}(Minimum of 1){}", + }, + }, + stake_cry_horizon = { + name = "Horizon Stake", + colour = "Horizon", + text = { + "When blind selected, add a", + "{C:attention}random card{} to deck", + }, + }, + stake_cry_blossom = { + name = "Blossom Stake", + colour = "Blossom", + text = { + "{C:attention}Final{} Boss Blinds can appear", + "in {C:attention}any{} Ante", + }, + }, + stake_cry_azure = { + name = "Azure Stake", + colour = "Azure", + text = { + "Values on Jokers are reduced", + "by {C:attention}20%{}", + }, + }, + stake_cry_ascendant = { + name = "Ascendant Stake", + colour = "Ascendant", + text = { + "{C:attention}-1{} Shop slot", + }, + }, + }, + Tag = { + tag_cry_astral = { + name = "Astral Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Astral{}", + }, + }, + tag_cry_banana = { + name = "Banana Tag", + text = { + "Creates {C:attention}#1#", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_bettertop_up = { + name = "Better Top-up Tag", + text = { + "Creates up to {C:attention}#1#", + "{C:green}Uncommon{} Jokers", + "{C:inactive}(Must have room){}", + }, + }, + tag_cry_better_voucher = { + name = "Golden Voucher Tag", + text = { + "Adds one Tier {C:attention}#1#{} Voucher", + "to the next shop", + }, + }, + tag_cry_blur = { + name = "Blurred Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Blurred{}", + }, + }, + tag_cry_booster = { + name = "Booster Tag", + text = { + "Next {C:cry_code}Booster Pack{} has", + "{C:attention}double{} cards and", + "{C:attention}double{} choices", + }, + }, + tag_cry_bundle = { + name = "Bundle Tag", + text = { + "Create a {C:attention}Standard Tag{}, {C:tarot}Charm Tag{},", + "{C:attention}Buffoon Tag{}, and {C:planet}Meteor Tag", + }, + }, + tag_cry_cat = { + name = "Cat Tag", + text = { "Meow.", "{C:inactive}Level {C:dark_edition}#1#" }, + }, + tag_cry_console = { + name = "Console Tag", + text = { + "Gives a free", + "{C:cry_code}Program Pack", + }, + }, + tag_cry_double_m = { + name = "Double M Tag", + text = { + "Shop has a", + "{C:dark_edition}Jolly {C:legendary}M Joker{}", + }, + }, + tag_cry_empowered = { + name = "Empowered Tag", + text = { + "Gives a free {C:spectral}Spectral Pack", + "with {C:legendary,E:1}The Soul{} and {C:cry_exotic,E:1}Gateway{}", + }, + }, + tag_cry_epic = { + name = "Epic Tag", + text = { + "Shop has a half-price", + "{C:cry_epic}Epic Joker", + }, + }, + tag_cry_gambler = { + name = "Gambler's Tag", + text = { + "{C:green}#1# in #2#{} chance to create", + "an {C:cry_exotic,E:1}Empowered Tag", + }, + }, + tag_cry_glass = { + name = "Fragile Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Fragile{}", + }, + }, + tag_cry_glitched = { + name = "Glitched Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Glitched{}", + }, + }, + tag_cry_gold = { + name = "Golden Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Golden{}", + }, + }, + tag_cry_gourmand = { + name = "Gourmand Tag", + text = { + "Shop has a free", + "{C:attention}Food Joker", + }, + }, + tag_cry_loss = { + name = "Loss", + text = { + "Gives a free", + "{C:cry_ascendant}Meme Pack", + }, + }, + tag_cry_m = { + name = "Jolly Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Jolly{}", + }, + }, + tag_cry_memory = { + name = "Memory Tag", + text = { + "Create {C:attention}#1#{} copies of", + "the last {C:attention}Tag{} used", + "during this run", + "{s:0.8,C:inactive}Copying Tags excluded", + "{s:0.8,C:inactive}Currently: {s:0.8,C:attention}#2#", + }, + }, + tag_cry_mosaic = { + name = "Mosaic Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Mosaic{}", + }, + }, + tag_cry_oversat = { + name = "Oversaturated Tag", + text = { + "Next base edition shop", + "Joker is free and", + "becomes {C:dark_edition}Oversaturated{}", + }, + }, + tag_cry_quadruple = { + name = "Quadruple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_quintuple = { + name = "Quintuple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + tag_cry_rework = { + name = "Rework Tag", + text = { + "Shop has a(n)", + "{C:dark_edition}#1# {C:cry_code}#2#", + }, + }, + tag_cry_schematic = { + name = "Schematic Tag", + text = { + "Shop has a", + "{C:attention}Brainstorm", + }, + }, + tag_cry_scope = { + name = "Scope Tag", + text = { + "{C:attention}+#1# {C:blue}hands{} and", + "{C:red}discards{} next round", + }, + }, + tag_cry_triple = { + name = "Triple Tag", + text = { + "Gives {C:attention}#1#{} copies of the", + "next selected {C:attention}Tag", + "{s:0.8,C:inactive}Copying Tags excluded", + }, + }, + }, + Tarot = { + c_cry_automaton = { + name = "The Automaton", + text = { + "Creates up to {C:attention}#1#", + "random {C:cry_code}Code{} card", + "{C:inactive}(Must have room)", + }, + }, + c_cry_eclipse = { + name = "The Eclipse", + text = { + "Enhances {C:attention}#1#{} selected card", + "into an {C:attention}Echo Card", + }, + }, + c_cry_meld = { + name = "Meld", + text = { + "Select a {C:attention}Joker{} or", + "{C:attention}playing card{} to", + "become {C:dark_edition}Double-Sided", + }, + }, + c_cry_theblessing = { + name = "The Blessing", + text = { + "Creates {C:attention}1{}", + "random {C:attention}consumable{}", + "{C:inactive}(Must have room){}", + }, + }, + }, + Voucher = { + v_cry_asteroglyph = { + name = "Asteroglyph", + text = { + "Set Ante to {C:attention}#1#{}", + }, + }, + v_cry_blankcanvas = { + name = "Blank Canvas", + text = { + "{C:attention}+#1#{} hand size", + }, + }, + v_cry_clone_machine = { + name = "Clone Machine", + text = { + "Double Tags become", + "{C:attention}Quintuple Tags{} and", + "are {C:attention}4X{} as common", + }, + }, + v_cry_command_prompt = { + name = "Command Prompt", + text = { + "{C:cry_code}Code{} cards", + "can appear", + "in the {C:attention}shop{}", + }, + }, + v_cry_copies = { + name = "Copies", + text = { + "Double Tags become", + "{C:attention}Triple Tags{} and are", + "{C:attention}2X{} as common", + }, + }, + v_cry_curate = { + name = "Curate", + text = { + "All cards", + "appear with", + "an {C:dark_edition}Edition{}", + }, + }, + v_cry_dexterity = { + name = "Dexterity", + text = { + "Permanently", + "gain {C:blue}+#1#{} hand(s)", + "each round", + }, + }, + v_cry_double_down = { + name = "Double Down", + text = { + "After every round,", + "{X:dark_edition,C:white} X1.5 {} to all values", + "on the back of", + "{C:dark_edition}Double-Sided{} cards" + }, + }, + v_cry_double_slit = { + name = "Double Slit", + text = { + "{C:attention}Meld{} can appear", + "in the shop and", + "Arcana Packs", + }, + }, + v_cry_double_vision = { + name = "Double Vision", + text = { + "{C:dark_edition}Double-Sided{} cards appear", + "{C:attention}4X{} more frequently", + }, + }, + v_cry_fabric = { + name = "Universal Fabric", + text = { + "{C:dark_edition}+#1#{} Joker slot(s)", + }, + }, + v_cry_massproduct = { + name = "Mass Production", + text = { + "All cards and packs", + "in shop cost {C:attention}$1{}", + }, + }, + v_cry_moneybean = { + name = "Money Beanstalk", + text = { + "Raise the cap on", + "interest earned in", + "each round to {C:money}$#1#{}", + }, + }, + v_cry_overstock_multi = { + name = "Multistock", + text = { + "{C:attention}+#1#{} card slot(s) and", + "{C:attention}+#1#{} booster pack slot(s)", + "available in shop", + }, + }, + v_cry_pacclimator = { + name = "Planet Acclimator", + text = { + "{C:planet}Planet{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:planet}Planet{}", + "cards are {C:green}free{}", + }, + }, + v_cry_pairamount_plus = { + name = "Pairamount Plus", + text = { + "{C:attention}Retrigger{} all M Jokers", + "once for every Pair", + "{C:attention}contained{} in played hand", + }, + }, + v_cry_pairing = { + name = "Pairing", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand is a {C:attention}Pair", + }, + }, + v_cry_quantum_computing = { + name = "Quantum Computing", + text = { + "{C:cry_code}Code{} cards can spawn", + "with {C:dark_edition}Negative{} edition", + }, + }, + v_cry_repair_man = { + name = "Repair Man", + text = { + "{C:attention}Retrigger{} all M Jokers", + "if played hand contains a {C:attention}Pair", + }, + }, + v_cry_rerollexchange = { + name = "Reroll Exchange", + text = { + "All rerolls", + "cost {C:attention}$2{}", + }, + }, + v_cry_satellite_uplink = { + name = "Satellite Uplink", + text = { + "{C:cry_code}Code{} cards may", + "appear in any of", + "the {C:attention}Celestial Packs{}", + }, + }, + v_cry_scope = { + name = "Galactic Scope", + text = { + "Create the {C:planet}Planet", + "card for played", + "{C:attention}poker hand{}", + "{C:inactive}(Must have room){}", + }, + }, + v_cry_tacclimator = { + name = "Tarot Acclimator", + text = { + "{C:tarot}Tarot{} cards appear", + "{C:attention}X#1#{} more frequently", + "in the shop", + "All future {C:tarot}Tarot{}", + "cards are {C:green}free{}", + }, + }, + v_cry_tag_printer = { + name = "Tag Printer", + text = { + "Double Tags become", + "{C:attention}Quadruple Tags{} and", + "are {C:attention}3X{} as common", + }, + }, + v_cry_threers = { + name = "The 3 Rs", + text = { + "Permanently", + "gain {C:red}+#1#{} discard(s)", + "each round", + }, + }, + v_cry_stickyhand = { + name = "Sticky Hand", + text = { + "{C:attention}+#1#{} card", + "selection limit", + }, + }, + v_cry_grapplinghook = { + name = "Grappling Hook", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + v_cry_hyperspacetether = { + name = "Hyperspace Tether", + text = { + "{C:attention}+#1#{} card", + "selection limit", + "{C:inactive,s:0.7}NOTE: Will have extra{}", + "{C:inactive,s:0.7}functionality later{}", + }, + }, + }, + Other = { + banana = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "destroyed each round", + }, + }, + cry_rigged = { + name = "Rigged", + text = { + "All {C:cry_code}listed{} probabilities", + "are {C:cry_code}guaranteed", + }, + }, + cry_hooked = { + name = "Hooked", + text = { + "When this Joker is {C:cry_code}triggered{},", + "trigger {C:cry_code}#1#", + }, + }, + food_jokers = { + name = "Food Jokers", + text = { + "{s:0.8}Gros Michel, Egg, Ice Cream, Cavendish,", + "{s:0.8}Turtle Bean, Diet Cola, Popcorn, Ramen,", + "{s:0.8}Seltzer, Pickle, Chili Pepper, Caramel,", + "{s:0.8}Nostalgic Candy, Fast Food M, etc.", + }, + }, + cry_https_disabled = { + name = "M", + text = { + "{C:attention,s:0.7}Updating{s:0.7} is disabled by default ({C:attention,s:0.7}HTTPS Module{s:0.7})", + }, + }, + --i am so sorry for this + --actually some of this needs to be refactored at some point + cry_eternal_booster = { + name = "Eternal", + text = { + "All cards in pack", + "are {C:attention}Eternal{}", + }, + }, + cry_perishable_booster = { + name = "Perishable", + text = { + "All cards in pack", + "are {C:attention}Perishable{}", + }, + }, + cry_rental_booster = { + name = "Rental", + text = { + "All cards in pack", + "are {C:attention}Rental{}", + }, + }, + cry_pinned_booster = { + name = "Pinned", + text = { + "All cards in pack", + "are {C:attention}Pinned{}", + }, + }, + cry_banana_booster = { + name = "Banana", + text = { + "All cards in pack", + "are {C:attention}Banana{}", + }, + }, + cry_eternal_voucher = { + name = "Eternal", + text = { + "Can't be traded", + }, + }, + cry_perishable_voucher = { + name = "Perishable", + text = { + "Debuffed after", + "{C:attention}#1#{} rounds", + "{C:inactive}({C:attention}#2#{C:inactive} remaining)", + }, + }, + cry_rental_voucher = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at", + "end of round", + }, + }, + cry_pinned_voucher = { + name = "Pinned", + text = { + "Remains in shop", + "until redeemed", + }, + }, + cry_banana_voucher = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance of being", + "unredeemed each round", + }, + }, + cry_perishable_consumeable = { + name = "Perishable", + text = { + "Debuffed at", + "end of round", + }, + }, + cry_rental_consumeable = { + name = "Rental", + text = { + "Lose {C:money}$#1#{} at end of", + "round, and on use", + }, + }, + cry_pinned_consumeable = { + name = "Pinned", + text = { + "Can't use other", + "non-{C:attention}Pinned{} consumables", + }, + }, + cry_banana_consumeable = { + name = "Banana", + text = { + "{C:green}#1# in #2#{} chance to do", + "nothing on use", + }, + }, + p_cry_code_normal_1 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_normal_2 = { + name = "Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_jumbo_1 = { + name = "Jumbo Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_code_mega_1 = { + name = "Mega Program Pack", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:cry_code} Code{} cards", + }, + }, + p_cry_empowered = { + name = "Spectral Pack [Empowered Tag]", + text = { + "Choose {C:attention}#1#{} of up to", + "{C:attention}#2#{C:spectral} Spectral{} cards", + "{s:0.8,C:inactive}(Generated by Empowered Tag)", + }, + }, + p_cry_meme_1 = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_two = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + p_cry_meme_three = { + name = "Meme Pack", + text = { + "Choose {C:attention}#1#{} of", + "up to {C:attention}#2# Meme Jokers{}", + }, + }, + undiscovered_code = { + name = "Not Discovered", + text = { + "Purchase or use", + "this card in an", + "unseeded run to", + "learn what it does" + } + }, + cry_green_seal = { + name = "Green Seal", + text = { + "Creates a {C:cry_code}Code{} card", + "when played and unscoring", + "{C:inactive}(Must have room)", + }, + }, + cry_azure_seal = { + name = "Azure Seal", + text = { + "Create {C:attention}#1#{} {C:dark_edition}Negative{}", + "{C:planet}Planets{} for played", + "{C:attention}poker hand{}, then", + "{C:red}destroy{} this card", + }, + }, + }, + }, + misc = { + achievement_names = { + ach_cry_ace_in_crash = "Pocket ACE", + ach_cry_blurred_blurred_joker = "Legally Blind", + ach_cry_bullet_hell = "Bullet Hell", + ach_cry_break_infinity = "Break Infinity", + ach_cry_cryptid_the_cryptid = "Cryptid the Cryptid", + ach_cry_exodia = "Exodia", + ach_cry_freak_house = "Freak House", + ach_cry_googol_play_pass = "Googol Play Pass", + ach_cry_haxxor = "H4xx0r", + ach_cry_home_realtor = "Home Realtor", + ach_cry_jokes_on_you = "Joke's on You, Pal!", + ach_cry_niw_uoy = "!niW uoY", + ach_cry_now_the_fun_begins = "Now the Fun Begins", + ach_cry_patience_virtue = "Patience is a Virtue", + ach_cry_perfectly_balanced = "Perfectly Balanced", + ach_cry_pull_request = "Pull Request", + ach_cry_traffic_jam = "Traffic Jam", + ach_cry_ult_full_skip = "Ultimate Full Skip", + ach_cry_used_crash = "We Told You Not To", + ach_cry_what_have_you_done = "WHAT HAVE YOU DONE?!", + }, + achievement_descriptions = { + ach_cry_ace_in_crash = 'check_for_unlock({type = "ace_in_crash"})', + ach_cry_blurred_blurred_joker = "Obtain Blurred Blurred Joker", + ach_cry_bullet_hell = "Have 15 AP Jokers", + ach_cry_break_infinity = "Score 1.79e308 Chips in a single hand", + ach_cry_cryptid_the_cryptid = "Use Cryptid on Cryptid", + ach_cry_exodia = "Have 5 Exotic Jokers", + ach_cry_freak_house = "Play a Flush House consisting of 6s and 9s of Hearts whilst possessing Nice", + ach_cry_googol_play_pass = "Rig a Googol Play Card", + ach_cry_haxxor = "Use a cheat code", + ach_cry_home_realtor = "Activate Happy House before Ante 8 (without DoE/Antimatter)", + ach_cry_jokes_on_you = "Trigger The Joke's effect on Ante 1 and win the run", + ach_cry_niw_uoy = "Reach Ante -8", + ach_cry_now_the_fun_begins = "Obtain Canvas", + ach_cry_patience_virtue = "Wait out Lavender Loop for 2 minutes before playing first hand and beat the blind", + ach_cry_perfectly_balanced = "Beat Very Fair Deck on Ascendant Stake", + ach_cry_pull_request = "Have ://COMMIT spawn the same Joker that it destroyed", + ach_cry_traffic_jam = "Beat all Rush Hour challenges", + ach_cry_ult_full_skip = "Win in 1 round", + ach_cry_used_crash = "Use ://CRASH", + ach_cry_what_have_you_done = "Delete or Sacrifice an Exotic Joker", + }, + challenge_names = { + c_cry_ballin = "Ballin'", + c_cry_boss_rush = "Enter the Gungeon", + c_cry_dagger_war = "Dagger War", + c_cry_joker_poker = "Joker Poker", + c_cry_onlycard = "Solo Card", + c_cry_rng = "RNG", + c_cry_rush_hour = "Rush Hour I", + c_cry_rush_hour_ii = "Rush Hour II", + c_cry_rush_hour_iii = "Rush Hour III", + c_cry_sticker_sheet = "Sticker Sheet", + c_cry_sticker_sheet_plus = "Sticker Sheet+", + }, + dictionary = { + --Settings Menu + cry_set_features = "Features", + cry_set_music = "Music", + cry_set_enable_features = "Select features to enable (applies on game restart):", + cry_feat_achievements = "Achievements", + ["cry_feat_antimatter deck"] = "Antimatter Deck", + cry_feat_blinds = "Blinds", + cry_feat_challenges = "Challenges", + ["cry_feat_code cards"] = "Code Cards", + ["cry_feat_misc. decks"] = "Misc. Decks", + ["cry_feat_https module"] = "HTTPS Module", + ["cry_feat_timer mechanics"] = "Timer Mechanics", + ["cry_feat_enhanced decks"] = "Enhanced Decks", + ["cry_feat_epic jokers"] = "Epic Jokers", + ["cry_feat_exotic jokers"] = "Exotic Jokers", + ["cry_feat_m jokers"] = "M Jokers", + cry_feat_menu = "Custom Main Menu", + ["cry_feat_misc."] = "Misc.", + ["cry_feat_misc. jokers"] = "Misc. Jokers", + cry_feat_planets = "Planets", + cry_feat_jokerdisplay = "JokerDisplay (Does Nothing)", + cry_feat_tags = "Tags", + cry_feat_sleeves = "Sleeves", + cry_feat_spectrals = "Spectrals", + ["cry_feat_more stakes"] = "Stakes", + cry_feat_vouchers = "Vouchers", + cry_mus_jimball = "Jimball (Funkytown by Lipps Inc. - Copyrighted)", + cry_mus_code = "Code Cards (://LETS_BREAK_THE_GAME by HexaCryonic)", + cry_mus_exotic = "Exotic Jokers (Joker in Latin by AlexZGreat)", + cry_mus_high_score = "High Score (Final Boss [For Your Computer] by AlexZGreat)", + + k_cry_program_pack = "Program Pack", + k_cry_meme_pack = "Meme Pack", + + cry_critical_hit_ex = "Critical Hit!", + cry_critical_miss_ex = "Critical Miss!", + + cry_debuff_oldhouse = "No Full Houses", + cry_debuff_oldarm = "Must play 4 or fewer cards", + cry_debuff_oldpillar = "No Straights", + cry_debuff_oldflint = "No Flushes", + cry_debuff_oldmark = "No hands containing a Pair", + cry_debuff_obsidian_orb = "Applies abilities of all defeated bosses", + + k_code = "Code", + b_code_cards = "Code Cards", + b_pull = "PULL", + cry_hooked_ex = "Hooked!", + k_end_blind = "End Blind", + + cry_code_rank = "ENTER RANK", + cry_code_enh = "ENTER ENHANCEMENT", + cry_code_hand = "ENTER POKER HAND", + cry_code_enter_card = "ENTER A CARD", + cry_code_apply = "APPLY", + cry_code_apply_previous = "APPLY PREVIOUS", + cry_code_exploit = "EXPLOIT", + cry_code_exploit_previous = "EXPLOIT PREVIOUS", + cry_code_create = "CREATE", + cry_code_create_previous = "CREATE PREVIOUS", + cry_code_execute = "EXECUTE", + cry_code_cancel = "CANCEL", + + b_flip = "FLIP", + b_merge = "MERGE", + + cry_again_q = "Again?", + cry_curse = "Curse", + cry_curse_ex = "Curse!", + cry_sobbing = "Help me...", + cry_gaming = "Gaming", + cry_gaming_ex = "Gaming!", + cry_sus_ex = "Impostor!", + cry_jolly_ex = "Jolly Up!", + cry_m_minus = "m", + cry_m = "M", + cry_m_ex = "M!", + cry_minus_round = "-1 Round", + cry_plus_cryptid = "+1 Cryptid", + cry_no_triggers = "No triggers left!", + cry_unredeemed = "Unredeemed...", + cry_active = "Active", + cry_inactive = "Inactive", + + k_disable_music = "Disable Music", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic", + + cry_notif_jimball_1 = "Jimball", + cry_notif_jimball_2 = "Copyright Notice", + cry_notif_jimball_d1 = "Jimball plays the song \"Funkytown\",", + cry_notif_jimball_d2 = "which is copyrighted and can't be", + cry_notif_jimball_d3 = "used for streams and videos.", + }, + labels = { + food_jokers = "Food Jokers", + banana = "Banana", + code = "Code", + cry_rigged = "Rigged", + cry_hooked = "Hooked", + + cry_green_seal = "Green Seal", + cry_azure_seal = "Azure Seal", + + cry_astral = "Astral", + cry_blur = "Blurred", + cry_double_sided = "Double-Sided", + cry_glass = "Fragile", + cry_glitched = "Glitched", + cry_gold = "Golden", + cry_m = "Jolly", + cry_mosaic = "Mosaic", + cry_noisy = "Noisy", + cry_oversat = "Oversaturated", + + k_cry_epic = "Epic", + k_cry_epic = "Exotic" + }, + rnj_loc_txts = { + stats = { + plus_mult = { "{C:red}+#2#{} Mult" }, + plus_chips = { "{C:blue}+#2#{} Chips" }, + x_mult = { "{X:red,C:white} X#2#{} Mult" }, + x_chips = { "{X:blue,C:white} X#2#{} Chips" }, + h_size = { "{C:attention}+#2#{} Hand Size" }, + money = { "{C:money}+$#2#{} at payout" }, + }, + stats_inactive = { + plus_mult = { "{C:inactive}(Currently {C:red}+#1#{C:inactive} Mult)" }, + plus_chips = { "{C:inactive}(Currently {C:blue}+#1#{C:inactive} Chips)" }, + x_mult = { "{C:inactive}(Currently {X:red,C:white} X#1# {C:inactive} Mult)" }, + x_chips = { "{C:inactive}(Currently {X:blue,C:white} X#1# {C:inactive} Chips)" }, + h_size = { "{C:inactive}(Currently {C:attention}+#1#{C:inactive} Hand Size)" }, + money = { "{C:inactive}(Currently {C:money}+$#1#{C:inactive})" }, + }, + actions = { + make_joker = { "Create {C:attention}#2# Joker{}" }, + make_tarot = { "Create {C:attention}#2#{C:tarot} Tarot{} card" }, + make_planet = { "Create {C:attention}#2#{C:planet} Planet{} card" }, + make_spectral = { "Create {C:attention}#2#{C:spectral} Spectral{} card" }, + add_dollars = { "Earn {C:money}$#2#{}" }, + }, + contexts = { + open_booster = { "when a {C:attention}Booster{} is opened" }, + buying_card = { "when a card is bought" }, + selling_self = { "when this card is sold" }, + selling_card = { "when a card is sold" }, + reroll_shop = { "on reroll" }, + ending_shop = { "at the end of the {C:attention}shop{}" }, + skip_blind = { "when a {C:attention}blind{} is skipped" }, + skipping_booster = { "when any {C:attention}Booster Pack{} is skipped" }, + playing_card_added = { "every time a {C:attention}playing card{} is added to your deck" }, + first_hand_drawn = { "when round begins" }, + setting_blind = { "when {C:attention}Blind{} is selected" }, + remove_playing_cards = { "when a card is destroyed" }, + using_consumeable = { "when a {C:attention}consumable{} card is used" }, + debuffed_hand = { "if played {C:attention}hand{} is not allowed" }, + pre_discard = { "before each discard" }, + discard = { "for each discarded card" }, + end_of_round = { "at end of {C:attention}round{}" }, + individual_play = { "for each card scored" }, + individual_hand_score = { "for each card held in hand during scoring" }, + individual_hand_end = { "for each card held in hand at end of {C:attention}round{}" }, + repetition_play = { "Retrigger played cards" }, + repetition_hand = { "Retrigger held in hand cards" }, + other_joker = { "per {C:attention}Joker{}" }, + before = { "before each {C:attention}hand{}" }, + after = { "after each {C:attention}hand{}" }, + joker_main = {}, + }, + conds = { + buy_common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + buy_uncommon = { "if it is a {C:green}Uncommon{} {C:attention}Joker{}" }, + tarot = { "if card is a {C:tarot}Tarot{} card" }, + planet = { "if card is a {C:planet}Planet{} card" }, + spectral = { "if card is a {C:spectral}Spectral{} card" }, + joker = { "if card is a {C:attention}Joker{}" }, + suit = { "if card is a {V:1}#3#{}" }, + rank = { "if card is rank {C:attention}#3#{}" }, + face = { "if card is a {C:attention}face{} card" }, + boss = { "if {C:attention}blind{} is a {C:attention}Boss {C:attention}Blind{}" }, + non_boss = { "if {C:attention}blind{} is a {C:attention}Non-Boss {C:attention}Blind{}" }, + small = { "if {C:attention}blind{} is a {C:attention}Small {C:attention}Blind{}" }, + big = { "if {C:attention}blind{} is a {C:attention}Big {C:attention}Blind{}" }, + first = { "if it's the {C:attention}first {C:attention}hand{}" }, + last = { "if it's the {C:attention}last {C:attention}hand{}" }, + common = { "if it is a {C:blue}Common{} {C:attention}Joker{}" }, + uncommon = { "if it is an {C:green}Uncommon{} {C:attention}Joker{}" }, + rare = { "if it is a {C:red}Rare{} {C:attention}Joker{}" }, + poker_hand = { "if hand is a {C:attention}#3#{}" }, + or_more = { "if hand contains {C:attention}#3#{} or more cards" }, + or_less = { "if hand contains {C:attention}#3#{} or less cards" }, + hands_left = { "if #3# {C:blue}hands{} remaining at end of round" }, + discards_left = { "if #3# {C:red}discards{} remaining at end of round" }, + first_discard = { "if it's the {C:attention}first {C:attention}discard{}" }, + last_discard = { "if it's the {C:attention}last {C:attention}discard{}" }, + odds = { "with a {C:green}#4# {C:green}in {C:green}#3#{} chance" }, + }, + }, + v_dictionary = { + a_xchips = {"X#1# Chips"}, + a_powmult = {"^#1# Mult"}, + a_powchips = {"^#1# Chips"}, + a_powmultchips = {"^#1# Mult+Chips"}, + a_round = {"+#1# Round"}, + a_xchips_minus = {"-X#1# Chips"}, + a_powmult_minus = {"-^#1# Mult"}, + a_powchips_minus = {"-^#1# Chips"}, + a_powmultchips_minus = {"-^#1# Mult+Chips"}, + a_round_minus = {"-#1# Round"}, + + a_tag = {"#1# Tag"}, + a_tags = {"#1# Tags"}, + + cry_sticker_name = {"#1# Sticker"}, + cry_sticker_desc = { + "Used this Joker", + "to win on #2##1#", + "#2#Stake#3# difficulty" + }, + }, + v_text = { + ch_c_cry_all_perishable = {"All Jokers are {C:eternal}Perishable{}"}, + ch_c_cry_all_rental = {"All Jokers are {C:eternal}Rental{}"}, + ch_c_cry_all_pinned = {"All Jokers are {C:eternal}Pinned{}"}, + ch_c_cry_all_banana = {"All Jokers are {C:eternal}Banana{}"}, + ch_c_all_rnj = {"All Jokers are {C:attention}RNJoker{}"}, + ch_c_cry_sticker_sheet_plus = {"All purchasable items have all stickers"}, + ch_c_cry_rush_hour = {"All Boss Blinds are {C:attention}The Clock{} or {C:attention}Lavender Loop"}, + ch_c_cry_rush_hour_ii = {"All Blinds are {C:attention}Boss Blinds{}"}, + ch_c_cry_rush_hour_iii = {"{C:attention}The Clock{} and {C:attention}Lavender Loop{} scale {C:attention}twice{} as fast"}, + ch_c_cry_no_tags = {"Skipping is {C:attention}disabled{}"}, + ch_c_cry_no_vouchers = {"{C:attention}Vouchers{} no longer appear in the shop"}, + ch_c_cry_no_boosters = {"{C:attention}Booster Packs{} no longer appear in the shop"}, + ch_c_cry_no_rerolls = {"Rerolling is {C:attention}disabled{}"}, + ch_c_cry_no_consumables = {"{C:attention}Consumables{} no longer appear"} + }, + -- Thanks to many members of the community for contributing to all of these quips! + -- There's too many to credit so just go here: https://discord.com/channels/1116389027176787968/1209506360987877408/1237971471146553406 + -- And here: https://discord.com/channels/1116389027176787968/1219749193204371456/1240468252325318667 + very_fair_quips = { + { "L", "NO VOUCHERS", "FOR YOU" }, + { "BOZO", "DID YOU THINK I WOULD", "GIVE YOU A VOUCHER?" }, + { "NOPE!", "NO VOUCHERS HERE!", "(SLUMPAGE EDITION)" }, + { "SKILL ISSUE", "IMAGINE BEING GOOD ENOUGH", "FOR A VOUCHER" }, + { "JIMBO", "FROM MANAGEMENT", "FORGOT TO RESTOCK" }, + { "OOPS!", "NO VOUCHERS", "" }, + { "YOU JOKER,", "WHY ARE YOU LOOKING", "OVER HERE? LOL" }, + { "THE VOUCHER", "IS IN", "ANOTHER CASTLE" }, + { "$0", "BLANK VOUCHER", "(GET IT?)" }, + { "ERROR", "CANNOT DO ARITHMETIC ON A NIL VALUE", "(tier4vouchers.lua)" }, + { "100% OFF", "ON ALL VOUCHERS", "(SOMEONE ALREADY BOUGHT THEM)" }, + { "TRY AGAIN LATER", "HINT: YOU WON'T HAVE", "ENOUGH MONEY ANYWAYS" }, + { "HUH?", '"VOUCHER"?', "THAT'S NOT EVEN A WORD..." }, + { 'HOLD "R"', "TO RESTOCK", "ALL VOUCHERS" }, + { "DID YOU KNOW?", "PRESSING ALT+F4", "GIVES FREE VOUCHERS!" }, + { "SORRY,", "THERE ARE NO VOUCHERS", "DUE TO BUDGET CUTS" }, + { "CALL 1-600-JIMBO", "TO RATE YOUR", "VOUCHER EXPERIENCE" }, + { "DEFEAT", "ANTE 39 BOSS BLIND", "TO RESTOCK" }, + { "MAGIC TRICK", "I MADE THIS VOUCHER", "DISAPPEAR" }, + { "WHY IS A", "VOUCHER LIKE", "A WRITING DESK?" }, + { "WE HAVE RETRACTED", "YOUR VOUCHERS, THEY WOULD BE", "BETTER USED IN OTHER RUNS" }, + { "WHY DO THEY CALL IT VOUCHER", "WHEN MULT OUT THE HOT", "IN COLD EAT EAT THE CHIP" }, + { "SORRY", "THE VOUCHERS ARE EXPERIENCING", "VOUCHIFIA ABORTUS" }, + { "UNFORTUNATELY", "THE VOUCHRX REWRITE UPDATE", "HAS BEEN CANCELLED" }, + { "DEFEAT", "BOSS BLIND", "TO CHANGE NOTHING" }, + { "BIRDS ARE SINGING", "FLOWERS ARE BLOOMING", "KIDS LIKE YOU..." }, + { "WE ARE SORRY TO SAY", "ALL VOUCHERS HAVE BEEN RECALLED", "DUE TO SALMONELLA EXPOSURE" }, + { "VOUCHERS COULDN'T ARRIVE", "DUE TO SHOP LAYOUT BEING", "200% OVERBUDGET" }, + { "YOU LIKE", "BUYING VOUCHERS, DON'T YOU", "YOU'RE A VOUCHERBUYER" }, + { "VOUCHERS", "!E", "VOUCHER POOL" }, + { "THERE", "IS NO", "VOUCHER" }, + { "THERE IS", "NO SANTA", "AND THERE ARE NO VOUCHERS" }, + { "", "VOUCHERN'T", "" }, + { "YOU", "JUST LOST", "THE GAME" }, + { "CAN I OFFER YOU", "A NICE EGG", "IN THESE TRYING TIMES?" }, + { "GO TOUCH GRASS", "INSTEAD OF USING", "THIS DECK" }, + { "YOU COULD BE", "PLAYING ON BLUE DECK", "RIGHT NOW" }, + { "FREE EXOTICS", "GET THEM BEFORE ITS", "TOO LATE (sold out)" }, + { "PROVE THEM WRONG", "BUY BUYING AN INVISIBLE", "VOUCHER FOR $10" }, + { "", "no vouchers?", "" }, + { "see this ad?", "if you are, then it's working", "and you could have it for your own" }, + { "YOU'RE MISSING OUT ON", "AT LEAST 5 VOUCHERS RIGHT NOW", "tonktonktonktonktonk" }, + { "10", "20 NO VOUCHER XD", "30 GOTO 10" }, + { "VOUCHERS", "ARE A PREMIUM FEATURE", "$199.99 JOLLARS TO UNLOCK" }, + { "TRUE VOUCHERLESS!?!?", "ASCENDANT STAKE ONLY", "VERY FAIR DECK" }, + { "ENJOYING YOUR", "VOUCHER EXPERIENCE? GIVE US A", "FIVE STAR RATING ON JESTELP" }, + { "FREE VOUCHERS", "HOT VOUCHERS NEAR YOU", "GET VOUCHERS QUICK WITH THIS ONE TRICK" }, + { "INTRODUCING", "THE VERY FIRST TIER 0 VOUCHER!", "(coming to Cryptid 1.0 soon)" }, + { "A VOUCHER!", "IT'S JUST IMAGINARY", "WE IMAGINED YOU WOULD WANT IT, THAT IS" }, + { "TURN OFF ADBLOCKER", "WITHOUT ADS, WE WOULDN'T", "BE ABLE TO SELL YOU VOUCHERS" }, + { "IF YOU HAVE", "A PROBLEM WITH THIS", "EMAIL IT TO US AT NORESPONSE@JMAIL.COM" }, + { "NOT ENOUGH MONEY", "TO BUY THIS VOUCHER", "SO WHY WOULD WE PUT IT HERE?" }, + { "WANT A VOUCHER?", "WELL SHUT UP", "YOU CAN'T HAVE ANY LOL" }, + { "^$%& NO", "VOUCHERS ^%&% %&$^% FOR", "$%&%%$ %&$&*%$^ YOU" }, + { "A VOUCHER (TRUST)", "|\\/|", "|/\\|" }, + { + "... --- ...", + ".--. .-.. .- -.-- . .-. -.. . -.-. --- -.. . -.. -- --- .-. ... .", + "-.-. --- -.. . - --- ..-. .. -. -.. .- ...- --- ..- -.-. .... . .-.", + }, + { "RUN > NEW", "STARE AT NOTHING", "FOR AN HOUR OR TWO" }, + { "WE'RE VERY SORRY", "THE LAST GUY PANIC BOUGHT", "ALL THE VOUCHERS" }, + { "HOW IT FEELS", "TO BUY NO", "VOUCHERS" }, + { "JIMBO GOT A NAT 1", "AND DUMPED ALL THE", "VOUCHERS IN A DITCH" }, + { "ATTEMPT TO INDEX", "FIELD 'VOUCHER'", "(A NIL VALUE)" }, + { + "OH YOU REALLY THOUGHT THAT READING ALL THESE LINES WOULD BRING YOUR VOUCHERS BACK?", + "SORRY TO TELL YOU, BUT THIS DECK DOESN'T CONTAIN THE VOUCHERS YOU SEEK.", + "THIS ABNORMALLY LONG TEXT IS HERE AND DESIGNED TO WASTE YOUR TIME AND EFFORT WHILE YOU READ IT.", + }, + { "GO TO", "https://youtu.be/p7YXXieghto", "FOR FREE VOUCHERS" }, + } + } +} diff --git a/Cryptid/lovely.toml b/Cryptid/lovely.toml new file mode 100644 index 0000000..87afbef --- /dev/null +++ b/Cryptid/lovely.toml @@ -0,0 +1,739 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Make the splash screen more jolly +# Requires "Custom Main Menu" config to be enabled +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_joker'])" +position = "after" +payload = ''' +if Cryptid.enabled["Menu"] then + if Cryptid.enabled["M Jokers"] then + local mcard = {} + for k, _ in pairs(Cryptid.M_jokers) do + if G.P_CENTERS[k] then + mcard[#mcard + 1] = k + end + end + local option = math.random(#mcard) + local chosenoption = mcard[option] + if chosenoption == "j_cry_biggestm" or chosenoption == "j_cry_reverse" then --These don't render properly; replace these with loopy instead + SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_cry_loopy']) + else + SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[chosenoption]) + end + else + SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_jolly']) + end +end +''' +match_indent = true + +# Make cards in splash screen CCD cards +# Requires "Custom Main Menu" config to be enabled +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if math.random() > 0.8 then card.sprite_facing = 'back'; card.facing = 'back' end" +position = "before" +payload = ''' +if Cryptid.enabled["Menu"] then card:set_ability(get_random_consumable('cry_splash',{"no_grc"},nil,nil,true), true, nil) end +''' +match_indent = true + +# Show Glitched Edition to confirm Cryptid is Active +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "replace_card.states.visible = false" +position = "before" +payload = "replace_card:set_edition(G.P_CENTERS.e_cry_glitched and 'e_cry_glitched' or 'e_negative',true,true)" +match_indent = true + +# Patch related crash +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if not initial then G.GAME.blind:debuff_card(self) end" +position = "at" +payload = "if not initial and G.GAME and G.GAME.blind then G.GAME.blind:debuff_card(self) end" +match_indent = true + +# Draw midground layer +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.config.center.soul_pos and (self.config.center.discovered or self.bypass_discovery_center) then" +position = "after" +payload = ''' +if self.config.center.soul_pos and self.config.center.soul_pos.extra and (self.config.center.discovered or self.bypass_discovery_center) then + local scale_mod = 0.07-- + 0.02*math.cos(1.8*G.TIMERS.REAL) + 0.00*math.cos((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3 + local rotate_mod = 0--0.05*math.cos(1.219*G.TIMERS.REAL) + 0.00*math.cos((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2 + self.children.floating_sprite2:draw_shader('dissolve',0, nil, nil, self.children.center,scale_mod, rotate_mod,nil, 0.1--[[ + 0.03*math.cos(1.8*G.TIMERS.REAL)--]],nil, 0.6) + self.children.floating_sprite2:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod) +end +''' +match_indent = true + + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''if k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end''' +position = "at" +payload = '''if k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k ~= 'floating_sprite2' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end''' +match_indent = true + + +# Custom variables in info queue +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end)" +position = "after" +payload = "if _c.specific_vars then specific_vars = _c.specific_vars end" +match_indent = true + +# Fix not all cards returning to hand on big hands +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "{card_limit = 500, type = 'discard'})" +position = "at" +payload = "{card_limit = 1e308, type = 'discard'})" +match_indent = true + +# When hand size exceeds deck size, space the cards as if the hand size was equal to the deck size +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "function CardArea:align_cards()" +position = "after" +payload = ''' + if self.config.type == 'hand' then + self.config.temp_limit = math.min(self.config.card_limit, #G.playing_cards) + end +''' +match_indent = true + +# Crash fix +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "if not G.SAVED_GAME.VERSION or G.SAVED_GAME.VERSION < '0.9.2' then" +position = "at" +payload = "if not G.SAVED_GAME or not G.SAVED_GAME.VERSION or G.SAVED_GAME.VERSION < '0.9.2' then" +match_indent = true + +# Register banned bosses for rush hour +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "set_profile_progress()" +position = "before" +payload = ''' +for i = 1, #G.CHALLENGES do + if (G.CHALLENGES[i].id == 'c_cry_rush_hour' or G.CHALLENGES[i].id == 'c_cry_rush_hour_ii' or G.CHALLENGES[i].id == 'c_cry_rush_hour_iii') and #G.CHALLENGES[i].restrictions.banned_other == 0 then + for k, v in pairs(G.P_BLINDS) do + if k ~= "bl_cry_clock" and k ~= "bl_cry_lavender_loop" and v.boss then + G.CHALLENGES[i].restrictions.banned_other[#G.CHALLENGES[i].restrictions.banned_other+1] = {id = k, type = 'blind'} + end + end + end +end +''' +match_indent = true + +# Apply booster pack edition and stickers to contents - by Jen Walter +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "card.T.x = self.T.x" +position = "before" +payload = ''' +local edi = self.edition or {} +if edi.type and not self.ability.name:find('Standard') then + if card.ability.name ~= "cry-meteor" + and card.ability.name ~= "cry-exoplanet" + and card.ability.name ~= "cry-stardust" then + card:set_edition({[edi.type] = true}) + end +end +if self.ability.eternal then + card.ability.eternal = self.ability.eternal +end +if self.ability.perishable then + card.ability.perishable = self.ability.perishable +end +if self.ability.rental then + card.ability.rental = self.ability.rental +end +if self.pinned then + card.pinned = self.pinned +end +if self.ability.banana then + card.ability.banana = self.ability.banana +end +''' +match_indent = true + +# catch edition code in standard pack to avoid reapplying edition (HORRIBLE) +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''local edition \= poll_edition\('standard_edition'\.\.G.GAME\.round_resets\.ante, edition_rate, true\)\n\s+card\:set_edition\(edition\)''' +position = "at" +payload = ''' +local edi = self.edition or {} +if edi.type and not (G.GAME.modifiers.cry_force_edition and G.GAME.modifiers.cry_force_edition ~= 'random') then + card:set_edition({[edi.type] = true}) +elseif not G.GAME.modifiers.cry_force_random_edition then + local edition = poll_edition('standard_edition'..G.GAME.round_resets.ante, edition_rate, true) + card:set_edition(edition) +end +''' + +# Edition + sticker forcing on vouchers (editions are just funny cost increases) +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "G.shop_vouchers:emplace(card)" +position = "before" +payload = ''' +if G.GAME.current_round.cry_voucher_edition then + card:set_edition(G.GAME.current_round.cry_voucher_edition, true, true) +end +if G.GAME.current_round.cry_voucher_stickers then + if G.GAME.current_round.cry_voucher_stickers.eternal == true then -- this is dumb but i'm not sure how to call functions from a string + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.current_round.cry_voucher_stickers.perishable == true then + card.ability.perishable = true + end + if G.GAME.current_round.cry_voucher_stickers.rental == true then + card:set_rental(true) + card.ability.rental = true + end + if G.GAME.current_round.cry_voucher_stickers.pinned == true then + card.pinned = true + end + if G.GAME.current_round.cry_voucher_stickers.banana == true then + card.ability.banana = true + end +end +''' +match_indent = true + +# don't forget voucher tags +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = "G.shop_vouchers:emplace(card)" +position = "before" +payload = ''' +if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true) +elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true) +end + +if G.GAME.modifiers.cry_force_sticker == 'eternal' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_eternal(true) + card.ability.eternal = true +end +if G.GAME.modifiers.cry_force_sticker == 'perishable' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_perishable(true) + card.ability.perishable = true +end +if G.GAME.modifiers.cry_force_sticker == 'rental' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_rental(true) + card.ability.rental = true +end +if G.GAME.modifiers.cry_force_sticker == 'pinned' or G.GAME.modifiers.cry_sticker_sheet_plus then + card.pinned = true +end +if G.GAME.modifiers.cry_force_sticker == 'banana' or G.GAME.modifiers.cry_sticker_sheet_plus then + card.ability.banana = true +end +if G.GAME.modifiers.cry_sticker_sheet_plus then + for k, v in pairs(SMODS.Stickers) do + if v.apply and not v.no_sticker_sheet then v:apply(card, true) end + end +end +''' +match_indent = true + +# show owned vouchers +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "if G.GAME.used_vouchers[v.key] then" +position = "after" +payload = ''' + if not G.GAME.cry_owned_vouchers[v.key] then + G.GAME.cry_owned_vouchers[v.key] = G.GAME.used_vouchers[v.key] + end +end +if G.GAME.cry_owned_vouchers[v.key] then +''' +match_indent = true + +# show in voucher menu +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "voucher_areas[#voucher_areas]:emplace(card)" +position = "before" +payload = ''' +if G.GAME.voucher_edition_index[card.ability.name] then -- i just made it a function so i can look at it less + local edition = cry_edition_to_table(G.GAME.voucher_edition_index[card.ability.name]) + if edition then + card:set_edition(edition, true, true) + end +end + +if G.GAME.voucher_sticker_index.eternal[card.ability.name] then + card:set_eternal(true) + card.ability.eternal = true +end +if G.GAME.voucher_sticker_index.perishable[card.ability.name] then + card:set_perishable(true) + card.ability.perish_tally = G.GAME.voucher_sticker_index.perishable[card.ability.name] + card.ability.perishable = true + if G.GAME.voucher_sticker_index.perishable[card.ability.name] == 0 then + card.debuff = true + end +end +if G.GAME.voucher_sticker_index.rental[card.ability.name] then + card:set_rental(true) + card.ability.rental = true +end +if G.GAME.voucher_sticker_index.pinned[card.ability.name] then + card.pinned = true +end +if G.GAME.voucher_sticker_index.banana[card.ability.name] then + card.ability.banana = true +end +card.ability.extra = G.GAME.cry_voucher_centers[card.config.center_key].config.extra +if card.ability.extra_disp then card.ability.extra_disp = G.GAME.cry_voucher_centers[card.config.center_key].config.extra_disp end +''' +match_indent = true + +# sticker tagging +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if center_table.name == 'Overstock' or center_table.name == 'Overstock Plus' then" +position = "before" +payload = ''' +if not G.GAME.voucher_edition_index then G.GAME.voucher_edition_index = {} end +if self and self.edition then + G.GAME.voucher_edition_index[center_table.name] = self.edition.type +end +if not G.GAME.voucher_sticker_index then G.GAME.voucher_sticker_index = {eternal = {}, perishable = {}, rental = {}, pinned = {}, banana = {}} end +if self and self.ability and self.ability.eternal and self.ability.eternal == true then + G.GAME.voucher_sticker_index.eternal[center_table.name] = true +end +if self and self.ability and self.ability.perishable and self.ability.perishable == true then + G.GAME.voucher_sticker_index.perishable[center_table.name] = G.GAME.cry_voucher_perishable_rounds +end +if self and self.ability and self.ability.rental and self.ability.rental == true then + G.GAME.voucher_sticker_index.rental[center_table.name] = true +end +if self and self.pinned and self.pinned == true then + G.GAME.voucher_sticker_index.pinned[center_table.name] = true +end +if self and self.ability and self.ability.banana and self.ability.banana == true then + G.GAME.voucher_sticker_index.banana[center_table.name] = true +end +''' +match_indent = true + +# apply end of round stuff +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if G.GAME.round_resets.ante == G.GAME.win_ante and G.GAME.blind:get_type() == 'Boss' then" +position = "before" +payload = ''' +if G.GAME.voucher_sticker_index then + if G.GAME.voucher_sticker_index.perishable then + for k, v in pairs(G.GAME.voucher_sticker_index.perishable) do + if v > 1 then + G.GAME.voucher_sticker_index.perishable[k] = v - 1 + end + if v == 1 then + G.GAME.voucher_sticker_index.perishable[k] = v - 1 + for kk, vv in pairs(G.P_CENTERS) do + if k == vv.name then + cry_debuff_voucher(kk) + G.GAME.used_vouchers.vv = nil + G.GAME.used_vouchers[kk] = nil + break + end + end + end + end + end + if G.GAME.voucher_sticker_index.rental then + local cumulative_rental = 0 + for k, v in pairs(G.GAME.voucher_sticker_index.rental) do + cumulative_rental = cumulative_rental + G.GAME.cry_voucher_rental_rate + end + if cumulative_rental > 0 then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + blocking = false, + blockable = false, + func = (function() + ease_dollars(-cumulative_rental) + return true + end)})) + end + end + if G.GAME.voucher_sticker_index.banana then -- i'm pretty sure unredeem breaks if spectrals are disabled? unsure though + local voucher_queue = {} + local current_round_voucher=G.GAME.current_round.voucher + for k, v in pairs(G.GAME.voucher_sticker_index.banana) do + if (pseudorandom('byebyevoucher') < G.GAME.probabilities.normal/G.GAME.cry_voucher_banana_odds) then + area = G.play + local unredeemed_voucher = '' + for kk, vv in pairs(G.P_CENTERS) do + if k == vv.name then + unredeemed_voucher = kk + break + end + end + local card = create_card('Voucher', area, nil, nil, nil, nil, unredeemed_voucher) + if G.GAME.voucher_edition_index[card.ability.name] then -- i made this bullshit a function + local edition = cry_edition_to_table(G.GAME.voucher_edition_index[card.ability.name]) + if edition then + card:set_edition(edition, true, true) + end + end + if G.GAME.voucher_sticker_index.eternal[card.ability.name] then + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.voucher_sticker_index.perishable[card.ability.name] then + card:set_perishable(true) + card.ability.perish_tally = G.GAME.voucher_sticker_index.perishable[card.ability.name] + card.ability.perishable = true + if G.GAME.voucher_sticker_index.perishable[card.ability.name] == 0 then + card.debuff = true + end + end + if G.GAME.voucher_sticker_index.rental[card.ability.name] then + card:set_rental(true) + card.ability.rental = true + end + if G.GAME.voucher_sticker_index.pinned[card.ability.name] then + card.pinned = true + end + if G.GAME.voucher_sticker_index.banana[card.ability.name] then + card.ability.banana = true + end + card:start_materialize() + area:emplace(card) + card.cost=0 + card.shop_voucher=false + voucher_queue[#voucher_queue+1] = card + end + end + for k, v in pairs(voucher_queue) do + v:unredeem() + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0, + func = function() + v:start_dissolve() + return true + end})) + end + G.GAME.current_round.voucher=current_round_voucher + end +end +''' +match_indent = true + +# Affect booster packs +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "G.shop_booster:emplace(card)" +position = "before" +payload = ''' +if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) +elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) +end + +local eternal_perishable_poll = pseudorandom('cry_bpet'..(key_append or '')..G.GAME.round_resets.ante) +if (G.GAME.modifiers.cry_force_sticker == 'eternal') or (G.GAME.modifiers.cry_sticker_sheet_plus) or (G.GAME.modifiers.cry_any_stickers and (G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.8)) then + card:set_eternal(true) + card.ability.eternal = true +end +if G.GAME.modifiers.enable_perishables_in_shop and G.GAME.modifiers.cry_any_stickers then -- i don't feel like messing with this, whatever + if not G.GAME.modifiers.cry_eternal_perishable_compat and ((eternal_perishable_poll > 0.6) and (eternal_perishable_poll <= 0.8)) then + card:set_perishable(true) + card.ability.perishable = true + end + if G.GAME.modifiers.cry_eternal_perishable_compat and pseudorandom('cry_bpper'..(key_append or '')..G.GAME.round_resets.ante) > 0.8 then + card:set_perishable(true) + card.ability.perishable = true + end +end +if (G.GAME.modifiers.cry_force_sticker == 'perishable') or (G.GAME.modifiers.cry_sticker_sheet_plus) then + card:set_perishable(true) + card.ability.perishable = true +end +if (G.GAME.modifiers.cry_force_sticker == 'rental') or (G.GAME.modifiers.cry_sticker_sheet_plus) or (G.GAME.modifiers.cry_any_stickers and (G.GAME.modifiers.enable_rentals_in_shop and pseudorandom('cry_bpssjr'..(key_append or '')..G.GAME.round_resets.ante) > 0.8)) then -- i should really just make this a function? so messy + card.ability.rental = true -- do not set_rental here to prevent cost from decreasing +end +if (G.GAME.modifiers.cry_force_sticker == 'pinned') or (G.GAME.modifiers.cry_sticker_sheet_plus) or (G.GAME.modifiers.cry_any_stickers and (G.GAME.modifiers.cry_enable_pinned_in_shop and pseudorandom('cry_bppin'..(key_append or '')..G.GAME.round_resets.ante) > 0.8)) then + card.pinned = true +end +if G.GAME.modifiers.cry_force_sticker == 'banana' or G.GAME.modifiers.cry_sticker_sheet_plus then + card.ability.banana = true +end +if not G.GAME.modifiers.cry_eternal_perishable_compat and G.GAME.modifiers.enable_banana and G.GAME.modifiers.cry_any_stickers and (pseudorandom('cry_bpbanana'..(key_append or '')..G.GAME.round_resets.ante) > 0.8) and (eternal_perishable_poll <= 0.8) then + card.ability.banana = true +end +if G.GAME.modifiers.cry_eternal_perishable_compat and G.GAME.modifiers.enable_banana and G.GAME.modifiers.cry_any_stickers and (pseudorandom('cry_bpbanana'..(key_append or '')..G.GAME.round_resets.ante) > 0.8) then + card.ability.banana = true +end +if G.GAME.modifiers.cry_sticker_sheet_plus then + for k, v in pairs(SMODS.Stickers) do + if v.apply and not v.no_sticker_sheet then v:apply(card, true) end + end +end +''' +match_indent = true + +# world's first actually good multi-patch +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = "card:start_materialize()" +position = "before" +payload = ''' +if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) +elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) +end +''' +match_indent = true + +# Prevent Jokers from spitting empty messages +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "text = extra.message or text" +position = "after" +payload = "if not text or text == '' then return end" +match_indent = true + +# Add default pool value for Consumeables +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = 'else _pool[#_pool + 1] = "j_joker"' +position = "before" +payload = '''elseif _type == 'Consumeables' then _pool[#_pool + 1] = "c_ceres"''' +match_indent = true + +# make the cat tag meow (can probably do this without injecting?) +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = '''play_sound('tarot2', math.random()*0.1 + 0.55, 0.09)''' +position = "before" +payload = '''if self.key == 'tag_cry_cat' then +local rand = math.random(4) +play_sound('cry_meow'..rand, 1.26, 0.25) +end +''' +match_indent = true + +# hi it's me toneblock and i'm being stupid again! (Game:update inject) +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''if G.FILE_HANDLER and G.FILE_HANDLER and G.FILE_HANDLER.update_queued and (''' +position = "before" +payload = ''' +if not GLOBAL_cry_member_count_delay then GLOBAL_cry_member_count_delay = 0 end +if (GLOBAL_cry_member_count_delay > 5) or not GLOBAL_cry_member_count then -- it doesn't need to update this frequently? but it also doesn't need to be higher tbh... + if update_cry_member_count then update_cry_member_count() end -- i honestly hate nil checks like this, wish there was a shorthand + GLOBAL_cry_member_count_delay = 0 +else + GLOBAL_cry_member_count_delay = GLOBAL_cry_member_count_delay + dt +end +''' +match_indent = true + +# call update_cry_member_count() whenever the collection is opened to ensure it updates properly on title collection if it can (better than running it on loc_vars) +# it's not computationally intense at all, so whatever +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''G.FUNCS.your_collection = function(e)''' +position = "after" +payload = ''' +if update_cry_member_count then update_cry_member_count() end +''' +match_indent = true + +# notice if https is disabled (by default) +# also does some other things since this is patching in the same spot +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if _c.set == 'Other' then" +position = "before" +payload = ''' +if _c.name == 'cry-membershipcard' or _c.name == 'cry-membershipcardtwo' then + if not Cryptid.enabled["HTTPS Module"] then + if G.localization.descriptions.Other.cry_https_disabled then + main_end = {} + localize{type = 'other', key = 'cry_https_disabled', nodes = main_end, vars = {}} + main_end = main_end[1] + end + end +end +if _c.name == 'cry-translucent Joker' then + if G.jokers and G.jokers.cards then + for k, v in ipairs(G.jokers.cards) do + if (v.edition and v.edition.negative) and (G.localization.descriptions.Other.remove_negative)then + main_end = {} + localize{type = 'other', key = 'remove_negative', nodes = main_end, vars = {}} + main_end = main_end[1] + break + end + end + end +end +if _c.name == 'cry-blurred Joker' then + if (SMODS.Mods["sdm0sstuff"] or {}).can_load then + if G.localization.descriptions.Other.blurred_sdm0 then + main_end = {} + localize{type = 'other', key = 'blurred_sdm0', nodes = main_end, vars = {}} + main_end = main_end[1] + end + end +end +''' +match_indent = true + +# hand size forgiveness if playing negative or antimatter deck (someone please fix) +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "function Game:update_draw_to_hand(dt)" +position = "after" +payload = ''' +if G.GAME.selected_back and (G.GAME.selected_back.name == 'cry--Negative Deck' or G.GAME.selected_back.name == 'cry-Antimatter') and G.hand.config.card_limit <= 0 then -- 'cry--Negative Deck'... sure + G.hand.config.card_limit = 1 +end +''' +match_indent = true + +# don't draw old perishable texture +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''if self.ability.perishable then''' +position = "at" +payload = '''if self.ability.perishable and not layer then''' +match_indent = true + +# init Cryptid global through lovely +# so other mods can add things to memepack pool +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = '''function love.load()''' +position = "before" +payload = ''' +Cryptid = {} +Cryptid.memepack = {} +Cryptid.aliases = {} +Cryptid.food = {} +Cryptid.M_jokers = {} +Cryptid.Megavouchers = {} +''' +match_indent = true + +# Adds cry_creating_card event +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = ''' + check_for_unlock({type = 'have_edition'}) +end +''' +position = "after" +payload = ''' +for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({cry_creating_card = true, card = card}) +end +''' +match_indent = true + +# Adds cry_debuff_immune card modifier +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'if self.ability and self.ability.perma_debuff then self.debuff = true end' +position = "after" +payload = ''' +if self.cry_debuff_immune then + self.debuff = false +end +''' +match_indent = true + +# Removes cry_debuff_immune at the end of the round +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = 'for i = 1, #G.jokers.cards do' +position = "before" +payload = ''' +for i = 1, #G.playing_cards do + local CARD = G.playing_cards[i] + CARD.cry_debuff_immune = false +end +''' +match_indent = true + +# Joker Sell List +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'self:calculate_joker{selling_self = true}' +position = "before" +payload = ''' +if self.config.center.set == 'Joker' then + if G.GAME.jokers_sold then + local contained = false + for i = 1, #G.GAME.jokers_sold do + if self.config.center.key == G.GAME.jokers_sold[i] then contained = true end + end + if not contained then table.insert(G.GAME.jokers_sold, self.config.center.key) end + else + G.GAME.jokers_sold = {self.config.center.key} + end +end +''' +match_indent = true diff --git a/Cryptid/lovely/Achievements.toml b/Cryptid/lovely/Achievements.toml new file mode 100644 index 0000000..939e745 --- /dev/null +++ b/Cryptid/lovely/Achievements.toml @@ -0,0 +1,38 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +## Cryptid Achievements + +# Check to earn some achievements on startup +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''check_for_unlock({type = 'blind_discoveries'})''' +position = "after" +payload = ''' +if change_context ~= "splash" then + if not (G.ACHIEVEMENTS and G.ACHIEVEMENTS['ach_cry_used_crash'] and G.ACHIEVEMENTS['ach_cry_used_crash'].earned) then check_for_unlock({type = 'ach_cry_used_crash'}) end + if not (G.ACHIEVEMENTS and G.ACHIEVEMENTS['ach_cry_traffic_jam'] and G.ACHIEVEMENTS['ach_cry_traffic_jam'].earned) then check_for_unlock({type = 'win_challenge_startup'}) end + if not (G.ACHIEVEMENTS and G.ACHIEVEMENTS['ach_cry_perfectly_balanced'] and G.ACHIEVEMENTS['ach_cry_perfectly_balanced'].earned) then check_for_unlock({type = 'win_stake_startup'}) end +end''' +match_indent = true + +# Cryptid the Cryptid check +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''new_cards[#new_cards+1] = _card''' +position = "before" +payload = '''if _card.config.center.key == "c_cryptid" then check_for_unlock({type = "cryptid_the_cryptid"}) end''' +match_indent = true + +# WHAT HAVE YOU DONE check +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''local sliced_card = G.jokers.cards[my_pos+1]''' +position = "after" +payload = '''if sliced_card.config.center.rarity == "cry_exotic" then check_for_unlock({type = "what_have_you_done"}) end''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Ascended.toml b/Cryptid/lovely/Ascended.toml new file mode 100644 index 0000000..d11b794 --- /dev/null +++ b/Cryptid/lovely/Ascended.toml @@ -0,0 +1,194 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# gonna use a separate file for this because it'll be a lot +# golden text +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = ''' +{n=G.UIT.O, config={id = 'hand_name', func = 'hand_text_UI_set',object = DynaText({string = {{ref_table = G.GAME.current_round.current_hand, ref_value = "handname_text"}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, float = true, scale = scale*1.4})}}, +''' +position = "after" +payload = ''' +{n=G.UIT.O, config={id = 'cry_asc', func = 'cry_asc_UI_set',object = DynaText({string = {{ref_table = G.GAME.current_round.current_hand, ref_value = "cry_asc_num_text"}}, colours = {G.C.GOLD}, shadow = true, float = true, scale = scale*1})}}, +''' +match_indent = true + +# state events moment +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = ''' + delay = G.GAME.current_round.current_hand.handname ~= disp_text and 0.4 or 0}, {handname=disp_text, level=G.GAME.hands[text].level, mult = G.GAME.hands[text].mult, chips = G.GAME.hands[text].chips}) +''' +position = "at" +payload = ''' + delay = G.GAME.current_round.current_hand.handname ~= disp_text and 0.4 or 0}, {handname=disp_text, level=G.GAME.hands[text].level, mult = cry_ascend(G.GAME.hands[text].mult), chips = cry_ascend(G.GAME.hands[text].chips)}) +''' +match_indent = true + +# increment +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = ''' +G.GAME.hands[text].played_this_round = G.GAME.hands[text].played_this_round + 1 +''' +position = "before" +payload = ''' +if G.GAME.current_round.current_hand.cry_asc_num > 0 then G.GAME.cry_asc_played = G.GAME.cry_asc_played and G.GAME.cry_asc_played+1 or 1 end +''' +match_indent = true + +# even worse +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = ''' + mult = mod_mult(G.GAME.hands[text].mult) +''' +position = "at" +payload = ''' + mult = mod_mult(cry_ascend(G.GAME.hands[text].mult)) +''' +match_indent = true + +# again +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = ''' + hand_chips = mod_chips(G.GAME.hands[text].chips) +''' +position = "at" +payload = ''' + hand_chips = mod_chips(cry_ascend(G.GAME.hands[text].chips)) +''' +match_indent = true + +# b +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = ''' + update_hand_text({delay = 0}, {chips = G.GAME.hands[hand].chips, StatusText = true}) +''' +position = "at" +payload = ''' +update_hand_text({delay = 0}, {chips = cry_ascend(G.GAME.hands[hand].chips), StatusText = true}) +''' +match_indent = true + +# same old +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = ''' + update_hand_text({delay = 0}, {mult = G.GAME.hands[hand].mult, StatusText = true}) +''' +position = "at" +payload = ''' +update_hand_text({delay = 0}, {mult = cry_ascend(G.GAME.hands[hand].mult), StatusText = true}) +''' +match_indent = true + +# the removal (this is slightly invasive, should switch to regex) +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = ''' +check_and_set_high_score('hand', hand_chips*mult) +''' +position = "before" +payload = ''' + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() G.GAME.current_round.current_hand.cry_asc_num = 0;G.GAME.current_round.current_hand.cry_asc_num_text = '';return true end) + })) +''' +match_indent = true + +# the rare cardarea inject +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = ''' +update_hand_text({immediate = true, nopulse = nil, delay = 0}, {handname=disp_text, level=G.GAME.hands[text].level, mult = G.GAME.hands[text].mult, chips = G.GAME.hands[text].chips}) +''' +position = "at" +payload = ''' +update_hand_text({immediate = true, nopulse = nil, delay = 0}, {handname=disp_text, level=G.GAME.hands[text].level, mult = cry_ascend(G.GAME.hands[text].mult), chips = cry_ascend(G.GAME.hands[text].chips)}) +''' +match_indent = true + +# hyperspace splasher +[[patches]] +[patches.pattern] +target = "state_events.lua" +pattern = ''' +if next(find_joker('Splash')) then +''' +position = "at" +payload = ''' +if next(find_joker('Splash')) or G.GAME.used_vouchers.v_cry_hyperspacetether then +''' +match_indent = true + +# initialise variables +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = ''' + chip_total_text = '', +''' +position = "after" +payload = ''' + cry_asc_num = 0, + cry_asc_num_text = '', +''' +match_indent = true + +# i hope this is worth it +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = ''' +G.ARGS.score_intensity.required_score = G.GAME.blind and G.GAME.blind.chips or 0 +''' +position = "after" +payload = ''' +if G.cry_flame_override and G.cry_flame_override['duration'] > 0 then + G.cry_flame_override['duration'] = math.max(0, G.cry_flame_override['duration'] - dt) +end +''' +match_indent = true + +# overwrite the value for better pulsing +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = ''' +_F.real_intensity = math.max(0, _F.real_intensity + _F.intensity_vel) +''' +position = "after" +payload = ''' + +_F.real_intensity = (G.cry_flame_override and G.cry_flame_override['duration'] > 0) and ((_F.real_intensity + G.cry_flame_override['intensity'])/2) or _F.real_intensity +''' +match_indent = true + +# again (this is messy, don't really know how these lines even work) +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = ''' +_F.change = (_F.change or 0)*(1 - 4.*G.real_dt) + ( 4.*G.real_dt)*(_F.real_intensity < _F.intensity - 0.0 and 1 or 0)*_F.real_intensity +''' +position = "after" +payload = ''' +_F.change = (G.cry_flame_override and G.cry_flame_override['duration'] > 0) and ((_F.change + G.cry_flame_override['intensity'])/2) or _F.change +''' +match_indent = true diff --git a/Cryptid/lovely/Blinds.toml b/Cryptid/lovely/Blinds.toml new file mode 100644 index 0000000..ec88e8f --- /dev/null +++ b/Cryptid/lovely/Blinds.toml @@ -0,0 +1,53 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# The Tax effect +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "func = (function() update_hand_text({delay = 0, immediate = true}, {mult = 0, chips = 0, chip_total = math.floor(hand_chips*mult), level = '', handname = ''});play_sound('button', 0.9, 0.6);return true end)" +position = "at" +payload = "func = (function() update_hand_text({delay = 0, immediate = true}, {mult = 0, chips = 0, chip_total = G.GAME.blind.cry_cap_score and G.GAME.blind:cry_cap_score(math.floor(hand_chips*mult)) or math.floor(hand_chips*mult), level = '', handname = ''});play_sound('button', 0.9, 0.6);return true end)" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "ease_to = G.GAME.chips + math.floor(hand_chips*mult)," +position = "at" +payload = "ease_to = G.GAME.chips + (G.GAME.blind.cry_cap_score and G.GAME.blind:cry_cap_score(math.floor(hand_chips*mult)) or math.floor(hand_chips*mult))," +match_indent = true + +# Bunco (Magenta Dagger) and Cryptid (The Tax) compat +[[patches]] +[patches.pattern] +target = 'functions/state_events.lua' +pattern = "ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1)," +position = 'at' +match_indent = true +payload = '''ease_to = G.GAME.chips + (G.GAME.blind.cry_cap_score and G.GAME.blind:cry_cap_score(math.floor(hand_chips*mult)) or math.floor(hand_chips*mult)) * (e and e.antiscore and -1 or 1),''' + +# Fix a crash related to undebuffing Jokers at end of round +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if not G.P_CENTERS['e_'..(self.edition.type)].discovered then" +position = "at" +payload = "if self.edition.type and G.P_CENTERS['e_'..(self.edition.type)] and not G.P_CENTERS['e_'..(self.edition.type)].discovered then" +match_indent = true + +# Blocks hands with more than 5 cards with Psychic +[[patches]] +[patches.pattern] +target = "blind.lua" +pattern = "if self.debuff.h_size_ge and #cards < self.debuff.h_size_ge then" +position = "before" +payload = ''' + if self.name == "The Psychic" and #cards > 5 then + self.triggered = true + return true + end +''' +match_indent = true diff --git a/Cryptid/lovely/CCD.toml b/Cryptid/lovely/CCD.toml new file mode 100644 index 0000000..47d4631 --- /dev/null +++ b/Cryptid/lovely/CCD.toml @@ -0,0 +1,57 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# This is sacrilegious +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "self.GAME.starting_deck_size = #G.playing_cards" +position = "before" +payload = ''' +if G.GAME.modifiers.cry_ccd then + for k, v in pairs(G.playing_cards) do + v:set_ability(get_random_consumable('cry_ccd',{"no_doe", "no_grc"}, nil, nil, true), true, nil) + end +end +''' +match_indent = true + +# Aura use conditions +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if G.hand and (#G.hand.highlighted == 1) and G.hand.highlighted[1] and (not G.hand.highlighted[1].edition) then return true end" +position = "at" +payload = ''' +if self.area ~= G.hand then + return G.hand and (#G.hand.highlighted == 1) and G.hand.highlighted[1] and (not G.hand.highlighted[1].edition) +else + local idx = 1 + if G.hand.highlighted[1] == self then + local idx = 2 + end + return (#G.hand.highlighted == 2) and (not G.hand.highlighted[idx].edition) +end +''' +match_indent = true + +# Prevent counting CCD consumables for pack uses +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "if area == G.consumeables then" +position = "at" +payload = "if area == G.consumeables or area == G.hand then" +match_indent = true + +# Fix bugs from removing CCD +# This really shouldn't be in the card drawing code, but it doesn't really matter since that's where it crashes anyway lol +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if not self.config.center.discovered and (self.ability.consumeable or self.config.center.unlocked) and not self.config.center.demo and not self.bypass_discovery_center then" +position = "before" +payload = "if self.ability.set == 'Enhanced' then self.ability.consumeable = nil end" +match_indent = true diff --git a/Cryptid/lovely/CatMerge.toml b/Cryptid/lovely/CatMerge.toml new file mode 100644 index 0000000..146e9f8 --- /dev/null +++ b/Cryptid/lovely/CatMerge.toml @@ -0,0 +1,108 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# what in ze fuck am i cooking +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = '''tag_sprite.states.collide.can = true''' +position = "after" +payload = ''' +if self.key == 'tag_cry_cat' then tag_sprite.states.click.can = true; tag_sprite.states.drag.can = true end +''' +match_indent = true + +# m +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = '''tag_sprite.stop_hover = function(_self) _self.hovering = false; Node.stop_hover(_self); _self.hover_tilt = 0 end''' +position = "after" +payload = ''' +tag_sprite.click = function(_self) + if self.key == 'tag_cry_cat' and self.HUD_tag then + for i = 1, #G.GAME.tags do + local other_cat = G.GAME.tags[i] + if other_cat.key == 'tag_cry_cat' then + if not self.ability.level then self.ability.level = 1 end + if not other_cat.ability.level then other_cat.ability.level = 1 end -- setting ability just doesn't seem to be working... so you get this + if (self.ability.level == other_cat.ability.level) and (other_cat ~= self) and not cry_too_fast_kitty then + cry_too_fast_kitty = true + local perc = (other_cat.ability.level + 1)/10 + if perc > 1 then perc = 1 end + G.E_MANAGER:add_event(Event({ + delay = 0.0, + trigger = 'immediate', + func = (function() + attention_text({ + text = ""..other_cat.ability.level, + colour = G.C.WHITE, + scale = 1, + hold = 0.3/G.SETTINGS.GAMESPEED, + cover = other_cat.HUD_tag, + cover_colour = G.C.DARK_EDITION, + align = 'cm', + }) + play_sound('generic1', 0.8 + perc/2, 0.6) + play_sound('multhit1', 0.9 + perc/2, 0.4) + return true + end) + })) + G.E_MANAGER:add_event(Event({ + delay = 0.0, + trigger = 'immediate', + func = (function() + attention_text({ + text = "-", + colour = G.C.WHITE, + scale = 1, + hold = 0.3/G.SETTINGS.GAMESPEED, + cover = self.HUD_tag, + cover_colour = G.C.RED, + align = 'cm', + }) + return true + end) + })) + G.E_MANAGER:add_event(Event({ + func = (function() + self.HUD_tag.states.visible = false + return true + end) + })) + G.E_MANAGER:add_event(Event({ -- i have no idea what this does but i'm not messing with it + func = func + })) + + other_cat.ability.level = other_cat.ability.level + 1 + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.7, + func = (function() + self:remove() + cry_too_fast_kitty = nil + return true + end) + })) + break + end + end + end + end +end +''' +match_indent = true + +# copied from rework patch +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = '''elseif name_to_check == 'Economy Tag' then loc_vars = {self.config.max}''' +position = "after" +payload = ''' +elseif name_to_check == "cry-Cat Tag" then loc_vars = {self.ability.level or 1} +''' +match_indent = true diff --git a/Cryptid/lovely/Challenges.toml b/Cryptid/lovely/Challenges.toml new file mode 100644 index 0000000..857cddc --- /dev/null +++ b/Cryptid/lovely/Challenges.toml @@ -0,0 +1,31 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Rush Hour - remove tags +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "if type == 'Small' then" +position = "at" +payload = "if type == 'Small' and not G.GAME.modifiers.cry_no_tags then" +match_indent = true + +# Rush Hour - remove tags +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "elseif type == 'Big' then" +position = "at" +payload = "elseif type == 'Big' and not G.GAME.modifiers.cry_no_tags then" +match_indent = true + +# Rush Hour - remove tags +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "elseif not run_info then" +position = "at" +payload = "elseif type == 'Boss' and not run_info then" +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Code.toml b/Cryptid/lovely/Code.toml new file mode 100644 index 0000000..0a65605 --- /dev/null +++ b/Cryptid/lovely/Code.toml @@ -0,0 +1,245 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Code UI disables hold R shortcut +[[patches]] +[patches.pattern] +target = "engine/controller.lua" +pattern = 'if key == "r" and not G.SETTINGS.paused then' +position = "at" +payload = 'if key == "r" and not G.SETTINGS.paused and not (G.GAME and G.GAME.USING_CODE) then' +match_indent = true + + +# Payload - cash out UI +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "elseif config.name == 'interest' then" +position = "before" +payload = ''' +elseif config.name == 'interest_payload' then +table.insert(left_text, {n=G.UIT.T, config={text = num_dollars, scale = 0.8*scale, colour = G.C.SECONDARY_SET.Code, shadow = true, juice = true}}) +table.insert(left_text,{n=G.UIT.O, config={object = DynaText({string = {" "..localize{type = 'variable', key = 'interest', vars = {G.GAME.interest_amount*config.payload, 5, G.GAME.interest_amount*config.payload*G.GAME.interest_cap/5}}}, colours = {G.C.SECONDARY_SET.Code}, shadow = true, pop_in = 0, scale = 0.4*scale, silent = true})}}) +''' +match_indent = true + +# Payload - cash out +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if G.GAME.dollars >= 5 and not G.GAME.modifiers.no_interest then" +position = "at" +payload = ''' +if G.GAME.dollars >= 5 and not G.GAME.modifiers.no_interest and G.GAME.cry_payload then + add_round_eval_row({bonus = true, payload = G.GAME.cry_payload, name='interest_payload', pitch = pitch, dollars = G.GAME.interest_amount*G.GAME.cry_payload*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5)}) + pitch = pitch + 0.06 + if not G.GAME.seeded and not G.GAME.challenge then + if G.GAME.interest_amount*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5) == G.GAME.interest_amount*G.GAME.interest_cap/5 then + G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak = G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak + 1 + else + G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak = 0 + end + end + check_for_unlock({type = 'interest_streak'}) + dollars = dollars + G.GAME.interest_amount*G.GAME.cry_payload*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5) + G.GAME.cry_payload = nil +elseif G.GAME.dollars >= 5 and not G.GAME.modifiers.no_interest then''' +match_indent = true + +# Revert - fix a crash +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "if area and area.cards[1] then" +position = "at" +payload = "if area and area.cards and area.cards[1] then" +match_indent = true + +# Crash - use glitched shader +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "G.SHADERS['CRT']:send('glitch_intensity', 0)--0.1*G.SETTINGS.GRAPHICS.crt/100 + (G.screenwipe_amt) + 1)" +position = "at" +payload = "G.SHADERS['CRT']:send('glitch_intensity', glitched_intensity or 0)" +match_indent = true + +# Semicolon - don't lose +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''G.RESET_JIGGLES = true''' +position = 'after' +match_indent = true +payload = ''' +if G.GAME.current_round.semicolon then + game_over = false +end +''' + +# Semicolon - end screen text +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = '''localize\('ph_mr_bones'\)..' '\}, colours = \{G.C.FILTER''' +position = 'at' +# match_indent = true +line_prepend = '' +payload = '''(G.GAME.current_round.semicolon and ";" or localize('ph_mr_bones'))..' '}, colours = {(G.GAME.current_round.semicolon and G.C.SET.Code or G.C.FILTER)''' + +# Semicolon - polished UI +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''{n=G.UIT.T, config={text = localize('b_cash_out')..": ", scale = 1, colour = G.C.UI.TEXT_LIGHT, shadow = true}},''' +position = "at" +payload = '''{n=G.UIT.T, config={text = G.GAME.current_round.semicolon and localize('k_end_blind') or (localize('b_cash_out')..": "), scale = 1, colour = G.C.UI.TEXT_LIGHT, shadow = true}},''' +match_indent = true + +# Semicolon - polished UI +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''{n=G.UIT.T, config={text = localize('$')..config.dollars, scale = 1.2*scale, colour = G.C.WHITE, shadow = true, juice = true}}''' +position = "at" +payload = '''{n=G.UIT.T, config={text = not G.GAME.current_round.semicolon and localize('$')..config.dollars or ';', scale = 1.2*scale, colour = G.C.WHITE, shadow = true, juice = true}}''' +match_indent = true + +# Semicolon - polished UI +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''{n=G.UIT.R, config={id = 'cash_out_button', align = "cm", padding = 0.1, minw = 7, r = 0.15, colour = G.C.ORANGE, shadow = true, hover = true, one_press = true, button = 'cash_out', focus_args = {snap_to = true}}, nodes={''' +position = "at" +payload = '''{n=G.UIT.R, config={id = 'cash_out_button', align = "cm", padding = 0.1, minw = 7, r = 0.15, colour = G.GAME.current_round.semicolon and G.C.SET.Code or G.C.ORANGE, shadow = true, hover = true, one_press = true, button = 'cash_out', focus_args = {snap_to = true}}, nodes={''' +match_indent = true + +# Semicolon - reset value at start of round +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "G.GAME.blind:set_blind(G.GAME.round_resets.blind)" +position = "after" +payload = ''' +G.GAME.current_round.semicolon = false +''' +match_indent = true + +# Delete - placeholder booster, in the case that all are deleted +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = '''return center\nend\n\nfunction get_current_pool\(_type, _rarity, _legendary, _append\)''' +position = "before" +payload = '''if not center then center = G.P_CENTERS['p_buffoon_normal_1'] end ''' + +# Run - don't clear shop +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''if self.shop then self.shop:remove(); self.shop = nil end''' +position = "at" +payload = '''if self.shop and not G.GAME.USING_CODE then self.shop:remove(); self.shop = nil end''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''ease_background_colour_blind(G.STATES.SHOP)''' +position = "at" +payload = '''if not G.GAME.USING_RUN then ease_background_colour_blind(G.STATES.SHOP) end''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''if self.STATE == self.STATES.SELECTING_HAND then''' +position = "before" +payload = '''if G.GAME.USING_RUN then self.STATE = self.STATES.SHOP end''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''G.shop.alignment.offset.y = -5.3''' +position = "at" +payload = '''if not G.shop then return true end +G.shop.alignment.offset.y = -5.3''' +match_indent = true + +# Increase highlight limit for consumables +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "{card_limit = self.GAME.starting_params.consumable_slots, type = 'joker', highlight_limit = 1})" +position = "at" +payload = "{card_limit = self.GAME.starting_params.consumable_slots, type = 'joker', highlight_limit = 1e100})" +match_indent = true + +# Increase highlight limit for jokers +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "{card_limit = self.GAME.starting_params.joker_slots, type = 'joker', highlight_limit = 1})" +position = "at" +payload = "{card_limit = self.GAME.starting_params.joker_slots, type = 'joker', highlight_limit = 1e100})" +match_indent = true + +# Satellite Uplink +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''card = create_card("Planet", G.pack_cards, nil, nil, true, true, nil, 'pl1')''' +position = "at" +payload = ''' +if G.GAME.used_vouchers.v_cry_satellite_uplink and pseudorandom('cry_satellite_uplink') > 0.8 then + card = create_card("Code", G.pack_cards, nil, nil, true, true, nil, 'pl2') +else + card = create_card("Planet", G.pack_cards, nil, nil, true, true, nil, 'pl1') +end +''' +match_indent = true + +# Exploit - reset variables +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''function end_round()''' +position = "after" +payload = ''' +G.GAME.cry_exploit_override = nil +''' +match_indent = true + +# Rework Tag - UI +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = '''elseif name_to_check == 'Economy Tag' then loc_vars = {self.config.max}''' +position = "after" +payload = ''' +elseif name_to_check == "cry-Rework Tag" then loc_vars = { + self.ability and self.ability.rework_edition and localize{type = "name_text", set = "Edition", key = self.ability.rework_edition} or "["..string.lower(localize("k_edition")).."]", + self.ability and self.ability.rework_key and localize{type = "name_text", set = "Joker", key = self.ability.rework_key} or "["..string.lower(localize("k_joker")).."]", + } +''' +match_indent = true + +# Double Tag makes exact copy of rework tag +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = 'add_tag(Tag(_context.tag.key))' +position = "at" +payload = ''' + local tag = Tag(_context.tag.key) + if _context.tag.key == "tag_cry_rework" then + tag.ability.rework_edition = _context.tag.ability.rework_edition + tag.ability.rework_key = _context.tag.ability.rework_key + end + add_tag(tag) +''' +match_indent = true diff --git a/Cryptid/lovely/Conveyor.toml b/Cryptid/lovely/Conveyor.toml new file mode 100644 index 0000000..40421ec --- /dev/null +++ b/Cryptid/lovely/Conveyor.toml @@ -0,0 +1,29 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Don't sort cards +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end)" +position = "at" +payload = "if not G.GAME.modifiers.cry_conveyor then table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end) end" +match_indent = true + +# Start of round effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "G.GAME.blind:set_blind(G.GAME.round_resets.blind)" +position = "after" +payload = ''' +if G.GAME.modifiers.cry_conveyor and #G.jokers.cards>0 then + local duplicated_joker = copy_card(G.jokers.cards[#G.jokers.cards]) + duplicated_joker:add_to_deck() + G.jokers:emplace(duplicated_joker) + G.jokers.cards[1]:start_dissolve() +end +''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Cube.toml b/Cryptid/lovely/Cube.toml new file mode 100644 index 0000000..1875da7 --- /dev/null +++ b/Cryptid/lovely/Cube.toml @@ -0,0 +1,27 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Joker Lock +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "elseif v.enhancement_gate then" +position = "before" +payload = ''' +elseif v.source_gate then + if v.source_gate ~= _append then + add = nil + else + add = true + end +elseif v.joker_gate then + add = nil + for kk, vv in pairs(G.jokers.cards) do + if vv.ability.name == v.joker_gate then + add = true + end + end +''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Enhanced.toml b/Cryptid/lovely/Enhanced.toml new file mode 100644 index 0000000..29d075e --- /dev/null +++ b/Cryptid/lovely/Enhanced.toml @@ -0,0 +1,74 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Show edition on Edition Decks +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self.children.back:draw(overlay)" +position = "after" +payload = ''' +local currentBack = self.params.viewed_back and G.GAME.viewed_back or G.GAME.selected_back +if currentBack.effect.config.cry_force_edition and not currentBack.effect.config.cry_antimatter then + if currentBack.effect.config.cry_force_edition_shader then + self.children.back:draw_shader(currentBack.effect.config.cry_force_edition_shader , nil, self.ARGS.send_to_shader, true) + else + self.children.back:draw_shader(currentBack.effect.config.cry_force_edition , nil, self.ARGS.send_to_shader, true) + end +end +if currentBack.effect.config.cry_force_seal and not currentBack.effect.config.hide_seal and not currentBack.effect.config.cry_antimatter then + G.shared_seals[currentBack.effect.config.cry_force_seal]:draw_shader('dissolve', nil, nil, true, self.children.center) + if currentBack.effect.config.cry_force_seal == 'Gold' then G.shared_seals[currentBack.effect.config.cry_force_seal]:draw_shader('voucher', nil, self.ARGS.send_to_shader, true, self.children.center) end +end +if currentBack.effect.config.cry_force_sticker and not currentBack.effect.config.cry_antimatter then + for k, v in pairs(SMODS.Stickers) do + if currentBack.effect.config.cry_force_sticker == v.key then + if v and v.draw and type(v.draw) == 'function' then + v:draw(self) + else + G.shared_stickers[v.key].role.draw_major = self + G.shared_stickers[v.key]:draw_shader('dissolve', nil, nil, true, self.children.center) + G.shared_stickers[v.key]:draw_shader('voucher', nil, self.ARGS.send_to_shader, true, self.children.center) + end + end + end +end +if currentBack.effect.config.cry_antimatter or currentBack.effect.config.cry_force_edition == 'negative' then + self.children.back:draw_shader('negative', nil, self.ARGS.send_to_shader, true) + self.children.center:draw_shader('negative_shine', nil, self.ARGS.send_to_shader, true) +end +''' +match_indent = true + +# Antimatter Deck +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self.children.back:draw_shader('dissolve')" +position = "at" +payload = ''' +local currentBack = self.params.viewed_back and G.GAME.viewed_back or G.GAME.selected_back +if currentBack and currentBack.effect.config.cry_antimatter or currentBack.effect.config.cry_force_edition == 'negative' then + self.children.back:draw_shader('negative', nil, self.ARGS.send_to_shader) + self.children.center:draw_shader('negative_shine', nil, self.ARGS.send_to_shader) +else + self.children.back:draw_shader('dissolve') +end +''' +match_indent = true + +# Created cards match suits of suit deck +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "card.playing_card = G.playing_card" +position = "after" +payload = ''' +if G.GAME.modifiers.cry_force_suit then card:change_suit(G.GAME.modifiers.cry_force_suit) end +if G.GAME.modifiers.cry_force_enhancement then card:set_ability(G.P_CENTERS[G.GAME.modifiers.cry_force_enhancement]) end +if G.GAME.modifiers.cry_force_edition then card:set_edition({[G.GAME.modifiers.cry_force_edition]=true},true,true) end +if G.GAME.modifiers.cry_force_seal then card:set_seal(G.GAME.modifiers.cry_force_seal) end +''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Equilibrium.toml b/Cryptid/lovely/Equilibrium.toml new file mode 100644 index 0000000..2e0f771 --- /dev/null +++ b/Cryptid/lovely/Equilibrium.toml @@ -0,0 +1,30 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# non-pack scaling in pack slots +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "G.shop_booster.T.y, G.CARD_W*1.27, G.CARD_H*1.27, G.P_CARDS.empty, G.P_CENTERS[G.GAME.current_round.used_packs[i]], {bypass_discovery_center = true, bypass_discovery_ui = true})" +position = "at" +payload = "G.shop_booster.T.y, G.CARD_W*(G.P_CENTERS[G.GAME.current_round.used_packs[i]].set == 'Booster' and 1.27 or 1), G.CARD_H*(G.P_CENTERS[G.GAME.current_round.used_packs[i]].set == 'Booster' and 1.27 or 1), G.P_CARDS.empty, G.P_CENTERS[G.GAME.current_round.used_packs[i]], {bypass_discovery_center = true, bypass_discovery_ui = true})" +match_indent = true + +# scaling issues when reclaiming Overstock +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "G.GAME.shop.joker_max*1.02*G.CARD_W," +position = "at" +payload = "math.min(G.GAME.shop.joker_max,4)*1.02*G.CARD_W," +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "G.shop_jokers.T.w = G.GAME.shop.joker_max*1.01*G.CARD_W" +position = "at" +payload = "G.shop_jokers.T.w = math.min(G.GAME.shop.joker_max,4)*1.02*G.CARD_W" +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Error.toml b/Cryptid/lovely/Error.toml new file mode 100644 index 0000000..36fa4b0 --- /dev/null +++ b/Cryptid/lovely/Error.toml @@ -0,0 +1,57 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Misprint-like description +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "elseif self.ability.name == 'Misprint' then" +position = "before" +payload = ''' +elseif self.ability.name == 'cry-Error' then + if G.GAME and G.GAME.pseudorandom and G.STAGE == G.STAGES.RUN then + cry_error_msgs[#cry_error_msgs].string = "%%" .. predict_card_for_shop() + else + cry_error_msgs[#cry_error_msgs].string = "%%J6" + end + main_start = { + {n=G.UIT.O, config={object = DynaText({string = cry_error_operators, colours = {G.C.DARK_EDITION,},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.30, scale = 0.32, min_cycle_time = 0})}}, + {n=G.UIT.O, config={object = DynaText({string = cry_error_numbers, colours = {G.C.DARK_EDITION,},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.33, scale = 0.32, min_cycle_time = 0})}}, + {n=G.UIT.O, config={object = DynaText({string = cry_error_msgs, + colours = {G.C.UI.TEXT_DARK},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.4011, scale = 0.32, min_cycle_time = 0})}}, + } +''' +match_indent = true + +# Patch get_current_pool to predict for ERROR desc +# There's also a consumable rarity patch I should do but don't feel like overriding SMODS right now +## Note this breaks one of the regexs for SMODS.Rarity +# [[patches]] +# [patches.pattern] +# target = "functions/common_events.lua" +# pattern = "local rarity = _rarity or pseudorandom('rarity'..G.GAME.round_resets.ante..(_append or ''))" +# position = "at" +# payload = "local rarity = _rarity or pseudorandom(_G[gcparea == 'ERROR' and 'predict_pseudoseed' or 'pseudoseed']('rarity'..G.GAME.round_resets.ante..(_append or '')))" +# match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "local main_start, main_end = nil,nil" +position = "after" +payload = ''' +if self.ability.name == 'cry-Machine Code' then + --"Create a random // glitched consumable" + main_start = { + randomchar(codechars6), + randomchar(codechars6), + randomchar(codechars6), + randomchar(codechars6), + randomchar(codechars6), + randomchar(codechars6), + } +end +''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Exponentia.toml b/Cryptid/lovely/Exponentia.toml new file mode 100644 index 0000000..f4b0b61 --- /dev/null +++ b/Cryptid/lovely/Exponentia.toml @@ -0,0 +1,116 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Increment Exponentia on Joker XMult +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "card_eval_status_text(_card, 'jokers', nil, percent, nil, effects.jokers)" +position = "after" +payload = ''' +if effects.jokers.Xmult_mod and effects.jokers.Xmult_mod ~= 1 and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end +end +''' +match_indent = true + +# Increment Exponentia on Joker-on-Joker XMult +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if extras.mult or extras.hand_chips then card_eval_status_text(v, 'jokers', nil, percent, nil, effect) end" +position = "after" +payload = ''' +if effect.Xmult_mod and effect.Xmult_mod ~= 1 and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end +end +''' +match_indent = true + + +# Increment Exponentia on Misc XMult +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if extras.mult or extras.hand_chips then card_eval_status_text(v, 'jokers', nil, percent, nil, effect) end" +position = "after" +payload = ''' +if effects.Xmult_mod and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end +end +''' +match_indent = true + + +# Increment Exponentia on Enhancement XMult +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "card_eval_status_text(scoring_hand[i], 'x_mult', effects[ii].x_mult, percent)" +position = "after" +payload = ''' +if next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end +end +''' +match_indent = true + + +# Increment Exponentia on Held-in-Hand XMult +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "card_eval_status_text(G.hand.cards[i], 'x_mult', effects[ii].x_mult, percent)" +position = "after" +payload = ''' +if next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end +end +''' +match_indent = true + + +# Increment Exponentia on Edition XMult +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "edition = true})" +position = "after" +payload = ''' +if (effects and effects[ii] and effects[ii].edition and effects[ii].edition.x_mult_mod or edition_effects and edition_effects.jokers and edition_effects.jokers.x_mult_mod) and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end +end +''' +match_indent = true diff --git a/Cryptid/lovely/Gateway.toml b/Cryptid/lovely/Gateway.toml new file mode 100644 index 0000000..3cff6b7 --- /dev/null +++ b/Cryptid/lovely/Gateway.toml @@ -0,0 +1,28 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# 3-layer drawing for Gateway +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.config.center.soul_pos and (self.config.center.discovered or self.bypass_discovery_center) then" +position = "before" +payload = ''' +if self.ability.name == 'cry-Gateway' and (self.config.center.discovered or self.bypass_discovery_center) then + local scale_mod2 = 0.07-- + 0.02*math.cos(1.8*G.TIMERS.REAL) + 0.00*math.cos((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3 + local rotate_mod2 = 0--0.05*math.cos(1.219*G.TIMERS.REAL) + 0.00*math.cos((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2 + self.children.floating_sprite2:draw_shader('dissolve',0, nil, nil, self.children.center,scale_mod2, rotate_mod2,nil, 0.1--[[ + 0.03*math.cos(1.8*G.TIMERS.REAL)--]],nil, 0.6) + self.children.floating_sprite2:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod2, rotate_mod2) + + local scale_mod = 0.05 + 0.05*math.sin(1.8*G.TIMERS.REAL) + 0.07*math.sin((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3 + local rotate_mod = 0.1*math.sin(1.219*G.TIMERS.REAL) + 0.07*math.sin((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2 + + self.children.floating_sprite.role.draw_major = self + self.children.floating_sprite:draw_shader('dissolve',0, nil, nil, self.children.center,scale_mod, rotate_mod,nil, 0.1 + 0.03*math.sin(1.8*G.TIMERS.REAL),nil, 0.6) + self.children.floating_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod) + +end +''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Misc.toml b/Cryptid/lovely/Misc.toml new file mode 100644 index 0000000..a2b94ee --- /dev/null +++ b/Cryptid/lovely/Misc.toml @@ -0,0 +1,491 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Shine on Oversaturated to make it more noticable +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "if (self.edition and self.edition.negative) or (self.ability.name == 'Antimatter' and (self.config.center.discovered or self.bypass_discovery_center)) then" +position = 'at' +match_indent = true +payload = "if (self.edition and (self.edition.negative or self.edition.cry_oversat)) or (self.ability.name == 'Antimatter' and (self.config.center.discovered or self.bypass_discovery_center)) then" + +# Detect if edition comes from copy_card +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = '''new_card:set_edition(other.edition or {}, nil, true)''' +position = 'before' +payload = ''' +new_card.from_copy = true +''' +match_indent = true + +# Used to check for Monster from copy_card and update values +# more or less the same patch above but this also applies if strip_edition is true +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if not strip_edition then" +position = 'before' +payload = ''' +new_card.checkmonster = true +''' +match_indent = true + +# Joker BigNum value support +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Campfire' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then" +position = "at" +payload = ''' +if self.ability.name == 'Campfire' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and to_big(self.ability.x_mult) > to_big(1) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Hit the Road' and self.ability.x_mult > 1 then" +position = "at" +payload = ''' +if self.ability.name == 'Hit the Road' and to_big(self.ability.x_mult) > to_big(1) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.x_mult > 1 then" +position = "at" +payload = ''' +if to_big(self.ability.x_mult) > to_big(1) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name ~= 'Seeing Double' and self.ability.x_mult > 1 and (self.ability.type == '' or next(context.poker_hands[self.ability.type])) then" +position = "at" +payload = ''' +if self.ability.name ~= 'Seeing Double' and to_big(self.ability.x_mult) > to_big(1) and (self.ability.type == '' or next(context.poker_hands[self.ability.type])) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Caino' and self.ability.caino_xmult > 1 then" +position = "at" +payload = ''' +if self.ability.name == 'Caino' and to_big(self.ability.caino_xmult) > to_big(1) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.t_mult > 0 and next(context.poker_hands[self.ability.type]) then" +position = "at" +payload = ''' +if to_big(self.ability.t_mult) > to_big(0) and next(context.poker_hands[self.ability.type]) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.t_chips > 0 and next(context.poker_hands[self.ability.type]) then" +position = "at" +payload = ''' +if to_big(self.ability.t_chips) > to_big(0) and next(context.poker_hands[self.ability.type]) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Ceremonial Dagger' and self.ability.mult > 0 then" +position = "at" +payload = ''' +if self.ability.name == 'Ceremonial Dagger' and to_big(self.ability.mult) > to_big(0) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Castle' and (self.ability.extra.chips > 0) then" +position = "at" +payload = ''' +if self.ability.name == 'Castle' and (to_big(self.ability.extra.chips) > to_big(0)) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Swashbuckler' and self.ability.mult > 0 then" +position = "at" +payload = ''' +if self.ability.name == 'Swashbuckler' and to_big(self.ability.mult) > to_big(0) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Spare Trousers' and self.ability.mult > 0 then" +position = "at" +payload = ''' +if self.ability.name == 'Spare Trousers' and to_big(self.ability.mult) > to_big(0) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Ride the Bus' and self.ability.mult > 0 then" +position = "at" +payload = ''' +if self.ability.name == 'Ride the Bus' and to_big(self.ability.mult) > to_big(0) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Flash Card' and self.ability.mult > 0 then" +position = "at" +payload = ''' +if self.ability.name == 'Flash Card' and to_big(self.ability.mult) > to_big(0) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Popcorn' and self.ability.mult > 0 then" +position = "at" +payload = ''' +if self.ability.name == 'Popcorn' and to_big(self.ability.mult) > to_big(0) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Green Joker' and self.ability.mult > 0 then" +position = "at" +payload = ''' +if self.ability.name == 'Green Joker' and to_big(self.ability.mult) > to_big(0) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Red Card' and self.ability.mult > 0 then" +position = "at" +payload = ''' +if self.ability.name == 'Red Card' and to_big(self.ability.mult) > to_big(0) then +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "function localize(args, misc_cat)" +position = "after" +payload = ''' +if args and args.vars then + local reset = {} + for i, j in pairs(args.vars) do + if type(j) == 'table' then + if (j.new and type(j.new) == "function") and ((j.m and j.e) or (j.array and j.sign and (type(j.array) == "table"))) then + reset[i] = number_format(j) + end + end + end + for i, j in pairs(reset) do + args.vars[i] = j + end +end +''' +match_indent = true + +# Compat UI for Old Blueprint and Gemini +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "elseif self.ability.name == 'Blueprint' then" +position = "at" +payload = '''elseif self.ability.name == 'Blueprint' or self.ability.name == 'cry-oldblueprint' then''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "elseif self.ability.name == 'Brainstorm' then" +position = "at" +payload = '''elseif self.ability.name == 'Brainstorm' or self.config.center.key == 'j_cry_gemino' then''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Blueprint' or self.ability.name == 'Brainstorm' then" +position = "at" +payload = ''' +if self.config.center.key == 'j_cry_gemino' then +other_joker = G.jokers.cards[1] +if other_joker and other_joker ~= self and not (Card.no(other_joker, "immutable", true)) then + self.ability.blueprint_compat = 'compatible' +else + self.ability.blueprint_compat = 'incompatible' +end end +if self.ability.name == 'Blueprint' or self.ability.name == 'cry-oldblueprint' or self.ability.name == 'Brainstorm' then''' +match_indent = true + +# calculate wheel fail (for Wheel of Hope) +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' or pseudorandom('wheel_of_fortune') < G.GAME.probabilities.normal/self.ability.extra then" +position = "after" +payload = ''' +if self.ability.name == 'The Wheel of Fortune' then self.cry_wheel_success = true end +''' +match_indent = false + +# unscoring context (for Green Seal) +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "for i=1, #G.hand.cards do" +position = "before" +payload = ''' +if scoring_hand then + local unscoring_hand = {} + for i = 1, #G.play.cards do + local is_scoring = false + for j = 1, #scoring_hand do + if G.play.cards[i] == scoring_hand[j] then + is_scoring = true + end + end + if not is_scoring then + unscoring_hand[#unscoring_hand+1] = G.play.cards[i] + end + end + for i = 1, #unscoring_hand do + unscoring_hand[i]:calculate_seal{unscoring = true} + end +end +''' +match_indent = true + +# no "Again!" text if a card will shatter +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''if reps[j] ~= 1 then''' +position = "at" +payload = '''if reps[j] ~= 1 and (not scoring_hand or not scoring_hand[i] or not scoring_hand[i].will_shatter) then''' +match_indent = true + +# properly remove destroyed playing cards from deck +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''if destroyed then''' +position = "before" +payload = '''if scoring_hand[i].will_shatter then destroyed = true end''' +match_indent = true + +# sfx fixes with destroyed playing cards from fragile edition +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''if cards_destroyed[i].ability.name == 'Glass Card' then''' +position = "before" +payload = '''if cards_destroyed[i].will_shatter then return true end''' +match_indent = true + +# fix random crashes +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''local edition_effects = eval_card(_card, {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, edition = true})''' +position = "after" +payload = '''if not edition_effects then edition_effects = {} end''' +match_indent = true + +# m +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''if self.ability.consumeable or self.ability.set == 'Joker' or (self.area and self.area == G.pack_cards) then''' +position = "at" +payload = '''if true then''' +match_indent = true + +# Energia - detect blind skips from tag +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''add_tag(_tag.config.ref_table)''' +position = "at" +payload = '''add_tag(_tag.config.ref_table, true)''' +match_indent = true + +# Energia - don't add tags from save load +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''add_tag(_tag)''' +position = "at" +payload = '''add_tag(_tag, nil, true)''' +match_indent = true + +# Beta Deck - merge slots +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''self.consumeables = CardArea(''' +position = "before" +payload = '''if not G.GAME.modifiers.cry_beta then''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''self.discard = CardArea(''' +position = "before" +payload = '''else +self.jokers = CardArea( + 0, 0, + CAI.joker_W+CAI.consumeable_W, + CAI.joker_H, + {card_limit = self.GAME.starting_params.joker_slots+self.GAME.starting_params.consumable_slots-1, type = 'joker', highlight_limit = 1e100}) +self.consumeables = self.jokers +end''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''G.consumeables.T.y = 0''' +position = "after" +payload = '''G.jokers.T.x = G.hand.T.x - 0.1 +G.jokers.T.y = 0''' +match_indent = true + +# Make Perkeo only copy consumables +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''if G.consumeables.cards[1] then''' +position = "at" +payload = '''local eligibleJokers = {} +for i = 1, #G.consumeables.cards do + if G.consumeables.cards[i].ability.consumeable then + eligibleJokers[#eligibleJokers + 1] = G.consumeables.cards[i] + end +end +if #eligibleJokers > 0 then''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''local card = copy_card(pseudorandom_element(G.consumeables.cards, pseudoseed('perkeo')), nil)''' +position = "at" +payload = '''local card = copy_card(pseudorandom_element(eligibleJokers, pseudoseed('perkeo')), nil)''' +match_indent = true + +# More Beta Deck fixes +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''for i=1, #G.jokers.cards + #G.consumeables.cards do''' +position = "at" +payload = ''' +local numcards = #G.jokers.cards + #G.consumeables.cards +if G.GAME.modifiers.cry_beta then numcards = #G.jokers.cards end +for i=1, numcards do''' +match_indent = true + +# Poker Hand display can have enhancements +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local card = Card(0,0, 0.5*G.CARD_W, 0.5*G.CARD_H, G.P_CARDS[v[1]], G.P_CENTERS.c_base)''' +position = "at" +payload = '''local card = Card(0,0, 0.5*G.CARD_W, 0.5*G.CARD_H, G.P_CARDS[v[1]], G.P_CENTERS[v[3] or 'c_base'])''' +match_indent = true + +# Adds G.GAME.modifiers.cry_forced_draw_amount +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = 'local hand_space = e or math.min(#G.deck.cards, G.hand.config.card_limit - #G.hand.cards)' +position = "after" +payload = ''' +if G.GAME.modifiers.cry_forced_draw_amount and (G.GAME.current_round.hands_played > 0 or G.GAME.current_round.discards_used > 0) then + hand_space = math.min(#G.deck.cards, G.GAME.modifiers.cry_forced_draw_amount) +end +''' +match_indent = true + +# adds oldbp blueprint corruption +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = 'if _pool_size == 0 then' +position = "before" +payload = ''' +if G.GAME.oldbpfactor and G.GAME.oldbpfactor >= 2 then + if _type == 'Joker' and (_rarity == nil or type(_rarity) ~= "string") and not _legendary and not (G.GAME.used_jokers["j_blueprint"] and not next(find_joker("Showman"))) then + for i = 1, math.floor(G.GAME.oldbpfactor - 1) do + _pool[#_pool + 1] = "j_blueprint" + end + end +end +''' +match_indent = true + +# end of shop decrement +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = 'G.SHOP_SIGN.alignment.offset.y = -15' +position = "after" +payload = ''' +if G.GAME.oldbpfactor then G.GAME.oldbpfactor = math.max(G.GAME.oldbpfactor - G.GAME.oldbpfactor/8, 1) end +''' +match_indent = true + +# Make Loopy reset here so it retriggers everything before resetting regardless of position +# Makes Old Blueprint self-destruct after triggering other end of round effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''if G.GAME.round_resets.temp_reroll_cost then G.GAME.round_resets.temp_reroll_cost = nil; calculate_reroll_cost(true) end''' +position = "after" +payload = ''' +for _, v in pairs(find_joker("cry-loopy")) do + if v.ability.extra.retrigger ~= 0 then + v.ability.extra.retrigger = 0 + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize("k_reset"), colour = G.C.GREEN}) + end +end +for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({end_of_round2 = true}) +end +''' +match_indent = true diff --git a/Cryptid/lovely/Misprint.toml b/Cryptid/lovely/Misprint.toml new file mode 100644 index 0000000..a1563b9 --- /dev/null +++ b/Cryptid/lovely/Misprint.toml @@ -0,0 +1,518 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Randomize poker hands after RNG is set up +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "self.GAME.pseudorandom.hashed_seed = pseudohash(self.GAME.pseudorandom.seed)" +position = "after" +payload = ''' +if G.GAME.modifiers.cry_misprint_min and not args.savetext then + for k, v in pairs(G.GAME.hands) do + v.chips = to_big(cry_format(v.chips * cry_log_random(pseudoseed('cry_misprint'),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2g")) + v.mult = to_big(cry_format(v.mult * cry_log_random(pseudoseed('cry_misprint'),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2g")) + v.l_chips = cry_format(v.l_chips * cry_log_random(pseudoseed('cry_misprint'),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2g") + v.l_mult = cry_format(v.l_mult * cry_log_random(pseudoseed('cry_misprint'),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2g") + v.s_chips = v.chips + v.s_mult = v.mult + end +end +''' +match_indent = true + +# Packs +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "create_shop_card_ui(card, 'Booster', G.shop_booster)" +position = "before" +payload = ''' +cry_misprintize(card) +''' +match_indent = true + +# Prevent pack softlocks +# Off by One Error effect +# Booster Tag effect +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "G.GAME.pack_choices = self.config.center.config.choose or 1" +position = "after" +payload = ''' +if G.GAME.modifiers.cry_misprint_min then + G.GAME.pack_size = self.ability.extra + if G.GAME.pack_size < 1 then G.GAME.pack_size = 1 end + self.ability.extra = G.GAME.pack_size + G.GAME.pack_choices = math.min(math.floor(G.GAME.pack_size), self.ability.choose) + --G.GAME.pack_choices = math.min(math.floor(G.GAME.pack_size),cry_format(G.GAME.pack_choices * cry_log_random(pseudoseed('cry_misprint_p'..G.GAME.round_resets.ante),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2f")) +end +if G.GAME.cry_oboe then + self.ability.extra = self.ability.extra + G.GAME.cry_oboe + G.GAME.pack_choices = G.GAME.pack_choices + G.GAME.cry_oboe + G.GAME.cry_oboe = nil + G.GAME.pack_size = self.ability.extra +end +if G.GAME.boostertag then + self.ability.extra = self.ability.extra * 2 + G.GAME.pack_choices = G.GAME.pack_choices * 2 + G.GAME.boostertag = nil + G.GAME.pack_size = self.ability.extra +end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name:find('Arcana') then" +position = "before" +payload = ''' +if self.ability.extra < 1 then self.ability.extra = 1 end +''' +match_indent = true + +# Vouchers +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "create_shop_card_ui(card, 'Voucher', G.shop_vouchers)" +position = "before" +payload = ''' +cry_misprintize(card) +if G.GAME.events.ev_cry_choco2 then + card.misprint_cost_fac = (card.misprint_cost_fac or 1) * 2 + card:set_cost() +end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = "create_shop_card_ui(card, 'Voucher', G.shop_vouchers)" +position = "before" +payload = ''' +cry_misprintize(card) +if G.GAME.events.ev_cry_choco2 then + card.misprint_cost_fac = (card.misprint_cost_fac or 1) * 2 + card:set_cost() +end +''' +match_indent = true + +# Fractional Ante Bugs +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "G.GAME.round_resets.ante = G.GAME.round_resets.ante + mod" +position = "after" +payload = "G.GAME.round_resets.ante = Big and (to_number(math.floor(to_big(G.GAME.round_resets.ante)))) or math.floor(G.GAME.round_resets.ante)" +match_indent = true + +# UI Bugs +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "_size*G.CARD_W," +position = "at" +payload = "math.max(1,math.min(_size,5))*G.CARD_W," +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "_size*G.CARD_W*1.1," +position = "at" +payload = "math.max(1,math.min(_size,5))*G.CARD_W*1.1," +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "_size*G.CARD_W*1.1 + 0.5," +position = "at" +payload = "math.max(1,math.min(_size,5))*G.CARD_W*1.1 + 0.5," +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "{card_limit = _size, type = 'consumeable', highlight_limit = 1}" +position = "at" +payload = "{card_limit = math.max(1,_size), type = 'consumeable', highlight_limit = 1}" +match_indent = true + +# Death and a CCD Patch +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.consumeable.mod_num >= #G.hand.highlighted and #G.hand.highlighted >= (self.ability.consumeable.min_highlighted or 1) then" +position = "at" +payload = "if (self.ability.consumeable.mod_num - ((G.GAME.modifiers.cry_consumable_reduce and (self.ability.name ~= 'Death')) and (self.ability.consumeable.mod_num > 1) and 1 or 0)) >= #G.hand.highlighted + (self.area == G.hand and -1 or 0) and #G.hand.highlighted + (self.area == G.hand and -1 or 0) >= 1 then" +match_indent = true + +# mod_num has no limit +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self.ability.consumeable.mod_num = math.min(5, self.ability.consumeable.max_highlighted)" +position = "at" +payload = "self.ability.consumeable.mod_num = self.ability.consumeable.max_highlighted" +match_indent = true + +# Infinite Deck +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "self.config.highlighted_limit = config.highlight_limit or 5" +position = "at" +payload = "self.config.highlighted_limit = config.highlight_limit or G.GAME.modifiers.cry_highlight_limit or 5" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "if #G.hand.highlighted <= 0 or G.GAME.blind.block_play or #G.hand.highlighted > 5 then" +position = "at" +payload = "if #G.hand.highlighted <= (G.GAME.blind and G.GAME.blind.name == 'cry-Sapphire Stamp' and not G.GAME.blind.disabled and 1 or 0) or G.GAME.blind.block_play then" +match_indent = true + +# Hieroglyph +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "ease_ante(-center_table.extra)" +position = "at" +payload = "ease_ante(math.floor(-center_table.extra))" +match_indent = true + +# Get Cryptid to display and work with card selections +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "elseif _c.name == 'Cryptid' then loc_vars = {_c.config.extra}" +position = "at" +payload = '''elseif _c.name == 'Cryptid' then loc_vars = {_c.config.extra, _c.config.max_highlighted} +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'local _card = copy_card(G.hand.highlighted[1], nil, nil, G.playing_card)' +position = "at" +payload = ''' +for q = 1, #G.hand.highlighted do +local _card = copy_card(G.hand.highlighted[q], nil, nil, G.playing_card) +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "new_cards[#new_cards+1] = _card" +position = "after" +payload = "end" +match_indent = true + +# Get seal spectrals to display and work with card selections +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = ''' +(?[\t ]*)elseif _c\.set == 'Spectral' then ''' +position = 'after' +# match_indent = true +line_prepend = '$indent ' +payload = ''' + +if _c.name == 'Talisman' or _c.name == 'Medium' or _c.name == 'Trance' or _c.name == 'Deja Vu' then + loc_vars = {_c.config.max_highlighted} +end +''' + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'local conv_card = G.hand.highlighted[1]' +position = "at" +payload = ''' +for q = 1, #G.hand.highlighted do +local conv_card = G.hand.highlighted[q] +G.E_MANAGER:add_event(Event({func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true end })) + +G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1,func = function() + conv_card:set_seal(self.ability.extra, nil, true) + return true end })) +end +delay(0.5) +G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end })) +end--[[ +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Aura' then" +position = "before" +payload = "--]]" +match_indent = true + +# Fractional pricing +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self.cost = math.max(1, math.floor((self.base_cost + self.extra_cost + 0.5)*(100-G.GAME.discount_percent)/100))" +position = "after" +payload = ''' + if self.ability.set == 'Joker' then + self.cost = cry_format(self.cost * G.GAME.cry_shop_joker_price_modifier,'%.2f') end + if self.misprint_cost_fac then + self.cost = cry_format(self.cost * self.misprint_cost_fac,'%.2f') + if not G.GAME.modifiers.cry_misprint_min then self.cost = math.floor(self.cost) end end +''' +match_indent = true + +# welcome to hell, enjoy your stay (initialise center copy) +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "G.GAME.chips_text = ''" +position = "after" +payload = ''' +G.GAME.cry_voucher_centers = {} +for k, v in pairs(G.P_CENTERS) do + if v.set == 'Voucher' then + G.GAME.cry_voucher_centers[k] = {config = {}} + G.GAME.cry_voucher_centers[k].config = copy_table(v.config) + end +end +''' +match_indent = true + +# when voucher is redeemed, save ability to center copy +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.shop_voucher then G.GAME.current_round.voucher = nil end" +position = "after" +payload = ''' +G.GAME.cry_voucher_centers[self.config.center_key].config.extra = self.ability.extra +if self.ability.extra_disp then G.GAME.cry_voucher_centers[self.config.center_key].config.extra_disp = self.ability.extra_disp end +''' +match_indent = true + +# extra override +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "extra = center and center.config.extra or self and self.ability.extra" +position = "at" +payload = ''' +extra = self and self.config and self.config.center_key and G.GAME and G.GAME.cry_voucher_centers and G.GAME.cry_voucher_centers[self.config.center_key] and G.GAME.cry_voucher_centers[self.config.center_key].config.extra +''' +match_indent = true + +# the d in disp stands for disparity (match internal values to display values) +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if center_table.name == 'Overstock' or center_table.name == 'Overstock Plus' then" +position = "before" +payload = ''' +if not center_table.extra then center_table.extra = center and center.config.extra end -- catch +if self and self.ability and self.ability.extra_disp then + local up = false + if center_table.name == 'Tarot Tycoon' or center_table.name == 'Planet Tycoon' then + up = true + end + local og_extra = 9.6/4 + local og_disp = 2 + if up == true then + og_extra = 32/4 + og_disp = 4 + end + local misprint_diff = self.ability.extra_disp / og_disp + G.GAME.cry_voucher_centers[self.config.center_key].config.extra = og_extra*misprint_diff + center_table.extra = og_extra*misprint_diff +end +''' +match_indent = true + +# I LOVE HARDCODING +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "G.hand:change_size(1)" +position = "at" +payload = ''' +G.hand:change_size(center_table.extra) +''' +match_indent = true + +# YOU LOVE HARDCODING +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "change_shop_size(1)" +position = "at" +payload = ''' +change_shop_size(center_table.extra) +''' +match_indent = true + +# WE ALL LOVE HARDCODING +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''if center_table\.name \=\= 'Antimatter' then\n\s+G\.E_MANAGER\:add_event\(Event\(\{func = function\(\)\n\s+if G\.jokers then \n\s+G\.jokers\.config\.card_limit \= G\.jokers\.config\.card_limit \+ 1\n\s+end\n\s+return true end \}\)\)\n\s+end''' +position = "at" +payload = ''' + if center_table.name == 'Antimatter' then + G.E_MANAGER:add_event(Event({func = function() + if G.jokers then + G.jokers.config.card_limit = G.jokers.config.card_limit + center_table.extra + end + return true end })) + end +''' + +# AAAA +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''if center_table\.name \=\= 'Crystal Ball' then\n\s+G\.E_MANAGER:add_event\(Event\(\{func \= function\(\)\n\s+G\.consumeables\.config\.card_limit \= G.consumeables\.config\.card_limit \+ 1\n\s+return true end }\)\)\n\s+end''' +position = "at" +payload = ''' + if center_table.name == 'Crystal Ball' then + G.E_MANAGER:add_event(Event({func = function() + G.consumeables.config.card_limit = G.consumeables.config.card_limit + center_table.extra + return true end })) + end +''' + +# anyway how's your day going? +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''elseif _c.name == "Clearance Sale" or _c.name == "Liquidation" then loc_vars = {_c.config.extra}''' +position = "after" +payload = ''' +elseif _c.name == "Crystal Ball" or _c.name == "Omen Globe" then loc_vars = {_c.config.extra} +''' +match_indent = true + +# doing pretty good myself +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''if _c.name == "Overstock" or _c.name == 'Overstock Plus' then''' +position = "at" +payload = ''' +if _c.name == "Overstock" or _c.name == "Overstock Plus" then loc_vars = {_c.config.extra} +''' +match_indent = true + +# spent the whole day reviving misprinted vouchers +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''elseif _c.name == "Blank" or _c.name == "Antimatter" then''' +position = "at" +payload = ''' +elseif _c.name == "Blank" or _c.name == "Antimatter" then loc_vars = {_c.config.extra} +''' +match_indent = true + +# tbf, more like half +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''message = localize\{type = 'variable', key = 'a_xmult', vars = \{G.P_CENTERS.v_observatory.config.extra}},\n\s+Xmult_mod = G.P_CENTERS.v_observatory.config.extra''' +position = "at" +payload = ''' + message = localize{type = 'variable', key = 'a_xmult', vars = {G.GAME.cry_voucher_centers['v_observatory'].config.extra}}, + Xmult_mod = G.GAME.cry_voucher_centers['v_observatory'].config.extra +''' + +# but whatever, not like i was doing anything anyway +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''UIBox_button({label = {localize('b_reroll_boss'), localize('$')..'10'}, button = "reroll_boss", func = 'reroll_boss_button'}) or nil''' +position = "at" +payload = ''' +UIBox_button({label = {localize('b_reroll_boss'), localize('$')..cry_cheapest_boss_reroll()}, button = "reroll_boss", func = 'reroll_boss_button'}) or nil +''' +match_indent = true + +# worth it in the end ig, it was bugging me +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''if ((G.GAME.dollars-G.GAME.bankrupt_at) - 10 >= 0) and''' +position = "at" +payload = ''' +if ((G.GAME.dollars-G.GAME.bankrupt_at) - cry_cheapest_boss_reroll() >= 0) and +''' +match_indent = true + +# see you around +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''if not G.from_boss_tag then ease_dollars(-10) end''' +position = "at" +payload = ''' +if not G.from_boss_tag then ease_dollars(-cry_cheapest_boss_reroll()) end +''' +match_indent = true + +# hi i'm back and i'm fixing this thing that breaks sometimes for no reason (it really shouldn't) +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''v_overstock_norm = {order = 1, discovered = false, unlocked = true , available = true, cost = 10, name = "Overstock", pos = {x=0,y=0}, set = "Voucher", config = {}},''' +position = "at" +payload = ''' +v_overstock_norm = {order = 1, discovered = false, unlocked = true , available = true, cost = 10, name = "Overstock", pos = {x=0,y=0}, set = "Voucher", config = {extra = 1}}, +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''v_crystal_ball= {order = 9, discovered = false, unlocked = true , available = true, cost = 10, name = "Crystal Ball", pos = {x=2,y=2}, set = "Voucher", config = {extra = 3}},''' +position = "at" +payload = ''' +v_crystal_ball= {order = 9, discovered = false, unlocked = true , available = true, cost = 10, name = "Crystal Ball", pos = {x=2,y=2}, set = "Voucher", config = {extra = 1}}, +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''v_overstock_plus= {order = 2, discovered = false, unlocked = false, available = true, cost = 10, name = "Overstock Plus", pos = {x=0,y=1}, set = "Voucher", config = {}, requires = {'v_overstock_norm'},unlock_condition = {type = 'c_shop_dollars_spent', extra = 2500}},''' +position = "at" +payload = ''' +v_overstock_plus= {order = 2, discovered = false, unlocked = false, available = true, cost = 10, name = "Overstock Plus", pos = {x=0,y=1}, set = "Voucher", config = {extra = 1}, requires = {'v_overstock_norm'},unlock_condition = {type = 'c_shop_dollars_spent', extra = 2500}}, +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''v_antimatter= {order = 24, discovered = false, unlocked = false, available = true, cost = 10, name = "Antimatter", pos = {x=7,y=1}, set = "Voucher", config = {extra = 15}, requires = {'v_blank'},unlock_condition = {type = 'blank_redeems', extra = 10}},''' +position = "at" +payload = ''' +v_antimatter= {order = 24, discovered = false, unlocked = false, available = true, cost = 10, name = "Antimatter", pos = {x=7,y=1}, set = "Voucher", config = {extra = 1}, requires = {'v_blank'},unlock_condition = {type = 'blank_redeems', extra = 10}}, +''' +match_indent = true diff --git a/Cryptid/lovely/Planets.toml b/Cryptid/lovely/Planets.toml new file mode 100644 index 0000000..7521952 --- /dev/null +++ b/Cryptid/lovely/Planets.toml @@ -0,0 +1,27 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Lapio softlock mechanic +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if (not v.config.softlock or G.GAME.hands[v.config.hand_type].played > 0) then" +position = "at" +payload = ''' +local softlocked = true +if not v.config.softlock then + softlocked = false +elseif v.config.hand_type then + softlocked = G.GAME.hands[v.config.hand_type].played == 0 +elseif v.config.hand_types then + for _, h in pairs(v.config.hand_types) do + if G.GAME.hands[h].played > 0 then + softlocked = false + end + end +end +if not softlocked then +''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/ResizedJokers.toml b/Cryptid/lovely/ResizedJokers.toml new file mode 100644 index 0000000..544523c --- /dev/null +++ b/Cryptid/lovely/ResizedJokers.toml @@ -0,0 +1,126 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Wee Fibonacci rendering +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'if center.name == "Wee Joker" and (center.discovered or self.bypass_discovery_center) then' +position = "before" +payload = ''' +if (center.name == "cry-Wee Fibonacci" or center.name == "cry-reverse") and (center.discovered or self.bypass_discovery_center) then + H = H*0.7 + W = W*0.7 + self.T.h = H + self.T.w = W +end +if center.name == "cry-biggestm" and (center.discovered or self.bypass_discovery_center) then + H = H*1.7 + W = W*1.7 + self.T.h = H + self.T.w = W +end +if center.name == "cry-Booster Joker" and (center.discovered or self.bypass_discovery_center) then + H = H*1.17 + W = W*1.17 + self.T.h = H + self.T.w = W +end +if center.name == "cry-Cube" and (center.discovered or self.bypass_discovery_center) then + H = H*0.1 + W = W*0.1 + self.T.h = H + self.T.w = W +end +if center.name == "cry-Potion" and (center.discovered or self.bypass_discovery_center) then + H = W + self.T.h = H + H = H*35/69 + W = W*35/69 + self.T.h = H + self.T.w = W +end +if center.name == "cry-Jimball" and (center.discovered or self.bypass_discovery_center) then + H = W + self.T.h = H + H = H*57/69 + W = W*57/69 + self.T.h = H + self.T.w = W +end +if center.name == "cry-magnet" and (center.discovered or self.bypass_discovery_center) then + H = W + self.T.h = H + H = H*35/71 + W = W*35/71 + self.T.h = H + self.T.w = W +end +''' +match_indent = true + + +# Wee Fibonacci rendering +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if _center.name == 'Photograph' and (_center.discovered or self.bypass_discovery_center) then" +position = "before" +payload = ''' +if _center.name == "cry-Cube" and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.y*0.1 + self.children.center.scale.x = self.children.center.scale.x*0.1 +end +if _center.name == 'cry-Jimball' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.x + self.children.center.scale.y = self.children.center.scale.y*57/69 + self.children.center.scale.x = self.children.center.scale.x*57/69 +end +if _center.name == 'cry-Potion' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.x + self.children.center.scale.y = self.children.center.scale.y*35/69 + self.children.center.scale.x = self.children.center.scale.x*35/69 +end +if _center.name == 'cry-magnet' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.x + self.children.center.scale.y = self.children.center.scale.y*35/71 + self.children.center.scale.x = self.children.center.scale.x*35/71 +end +''' +match_indent = true + +# Cube rendering +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'elseif self.config.center.name == "Wee Joker" then' +position = "before" +payload = ''' +elseif self.config.center.name == "cry-Wee Fibonacci" or self.config.center.name == "cry-reverse" then + self.T.h = H*scale*0.7*scale + self.T.w = W*scale*0.7*scale +elseif self.config.center.name == "cry-biggestm" then + self.T.h = H*scale*1.7*scale + self.T.w = W*scale*1.7*scale +elseif self.config.center.name == "cry-Booster Joker" then + self.T.h = H*scale*1.17*scale + self.T.w = W*scale*1.17*scale +elseif self.config.center.name == "cry-Cube" then + self.T.h = H*scale*0.1*scale + self.T.w = W*scale*0.1*scale +elseif self.config.center.name == "cry-Jimball" then + H = W + self.T.h = H*scale*57/69*scale + self.T.w = W*scale*57/69*scale +elseif self.config.center.name == "cry-Potion" then + H = W + self.T.h = H*scale*35/69*scale + self.T.w = W*scale*35/69*scale +elseif self.config.center.name == "cry-magnet" then + H = W + self.T.h = H*scale*35/71*scale + self.T.w = W*scale*35/71*scale +''' +match_indent = true diff --git a/Cryptid/lovely/Seals.toml b/Cryptid/lovely/Seals.toml new file mode 100644 index 0000000..eed6ca9 --- /dev/null +++ b/Cryptid/lovely/Seals.toml @@ -0,0 +1,20 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 10 + + +# Call Card:calculate_seal() with destroying_card context +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = '(?[\t ]*)if destroyed then \n' +position = 'before' +line_prepend = '$indent' +payload = ''' +if scoring_hand[i]:calculate_seal({destroying_card = scoring_hand[i], full_hand = G.play.cards}) and not scoring_hand[i].ability.eternal then + destroyed = true +end + +''' + diff --git a/Cryptid/lovely/Spooky.toml b/Cryptid/lovely/Spooky.toml new file mode 100644 index 0000000..e30eccf --- /dev/null +++ b/Cryptid/lovely/Spooky.toml @@ -0,0 +1,43 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 10 + +# Haunted House - useless skips +[[patches]] +[patches.regex] +target = "functions/button_callbacks.lua" +pattern = 'if _tag then' +position = 'at' +line_prepend = '$indent' +payload = '''if _tag and not G.GAME.events.ev_cry_choco2 then''' + +# Please Take One jank +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''ease_background_colour_blind(G.STATES.ROUND_EVAL)''' +position = "after" +payload = '''if G.GAME.events.ev_cry_choco6 and G.round_eval then return true end''' +match_indent = true + +# Revered Antique - create legendary +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''if G.load_shop_booster then''' +position = "before" +payload = ''' +if G.GAME.events.ev_cry_choco10 and not G.load_shop_vouchers then + local card = create_card('Joker', G.jokers, true, nil, nil, nil, nil, 'cry_antique') + cry_misprintize(card) + card.misprint_cost_fac = 50/card.cost + card:set_cost() + create_shop_card_ui(card, 'Voucher', G.shop_vouchers) + card:start_materialize() + card.ability.cry_antique = true + G.shop_vouchers.config.card_limit = G.shop_vouchers.config.card_limit + 1 + G.shop_vouchers:emplace(card) +end +''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/Stakes.toml b/Cryptid/lovely/Stakes.toml new file mode 100644 index 0000000..041d084 --- /dev/null +++ b/Cryptid/lovely/Stakes.toml @@ -0,0 +1,485 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + + +# Yellow Stake - perishable and rental effects on consumable +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if G.GAME.round_resets.ante == G.GAME.win_ante and G.GAME.blind:get_type() == 'Boss' then" +position = "before" +payload = ''' +local i = 1 +while i <= #G.jokers.cards do + local gone = G.jokers.cards[i]:calculate_banana() + if not gone then i = i + 1 end +end +i = 1 +while i <= #G.consumeables.cards do + G.consumeables.cards[i]:cry_calculate_consumeable_rental() + G.consumeables.cards[i]:cry_calculate_consumeable_perishable() + local gone = nil + if not gone then i = i + 1 end +end +''' +match_indent = true + +# Yellow Stake - perishable and rental effects on cards held in hand +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local effects = {G.hand.cards[i]:get_end_of_round_effect()}" +position = "after" +payload = ''' +G.hand.cards[i]:calculate_rental() +G.hand.cards[i]:calculate_perishable() +''' +match_indent = true + +# Yellow Stake - perishable and rental effects on cards in deck and discard pile +# Double Down +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "G.FUNCS.draw_from_hand_to_discard()" +position = "before" +payload = ''' +local i = 1 +while i <= #G.hand.cards do + local gone = G.hand.cards[i]:calculate_banana() + if not gone then i = i + 1 end +end +for i = 1, #G.discard.cards do + G.discard.cards[i]:calculate_perishable() +end +i = 1 +while i <= #G.deck.cards do + G.deck.cards[i]:calculate_perishable() + local gone = G.deck.cards[i]:calculate_banana() + if not gone then i = i + 1 end +end +if G.GAME.used_vouchers.v_cry_double_down then + local function update_dbl(area) + for i = 1, #area.cards do + if area.cards[i].dbl_side then + --tweak to do deck effects with on the flip side + cry_misprintize(area.cards[i].dbl_side, {min = 1.5, max = 1.5}, nil, true) + card_eval_status_text(area.cards[i], "extra", nil, nil, nil, { message = localize("k_upgrade_ex") }) + end + end + end + update_dbl(G.jokers) + update_dbl(G.consumeables) + update_dbl(G.hand) + update_dbl(G.discard) + update_dbl(G.deck) +end +''' +match_indent = true + +# Yellow Stake - Hanged Man can't be used on Eternal cards, Death can't remove Eternal +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'The Hermit' or self.ability.consumeable.hand_type or self.ability.name == 'Temperance' or self.ability.name == 'Black Hole' then" +position = "before" +payload = ''' +if self.ability.name == "The Hanged Man" then + for i = 1, #G.hand.highlighted do + if G.hand.highlighted[i].ability.eternal then return false end + end +end +if self.ability.name == "Death" then + local rightmost = G.hand.highlighted[1] + for i=1, #G.hand.highlighted-1 do if G.hand.highlighted[i].T.x > rightmost.T.x then rightmost = G.hand.highlighted[i] end end + for i=1, #G.hand.highlighted do if G.hand.highlighted[i].ability.eternal and rightmost ~= G.hand.highlighted[i] then return false end end +end +''' +match_indent = true + + +# Yellow Stake - Immolate can't be used on Eternal cards +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "for k, v in ipairs(G.hand.cards) do temp_hand[#temp_hand+1] = v end" +position = "at" +payload = ''' +for k, v in ipairs(G.hand.cards) do + if not v.ability.eternal then + temp_hand[#temp_hand+1] = v + end +end +''' +match_indent = true + + +# Yellow Stake - Death can't modify Eternal cards (redundant, but may help if Death can be used on more cards) +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if G.hand.highlighted[i] ~= rightmost then" +position = "at" +payload = "if G.hand.highlighted[i] ~= rightmost and not G.hand.highlighted[i].ability.eternal then" +match_indent = true + + +# Yellow Stake - Trading Card can't destroy Eternals +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "G.GAME.current_round.discards_used <= 0 and #context.full_hand == 1 then" +position = "at" +payload = "G.GAME.current_round.discards_used <= 0 and #context.full_hand == 1 and not context.other_card.ability.eternal then" +match_indent = true + + +# Yellow Stake - Sixth Sense can't destroy Eternals +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Sixth Sense' and #context.full_hand == 1 and context.full_hand[1]:get_id() == 6 and G.GAME.current_round.hands_played == 0 then" +position = "at" +payload = "if self.ability.name == 'Sixth Sense' and #context.full_hand == 1 and context.full_hand[1]:get_id() == 6 and not context.full_hand[1].ability.eternal and G.GAME.current_round.hands_played == 0 then" +match_indent = true + + +# Yellow Stake - Glass can't destroy Eternals +# Glass Stake - Any card can shatter +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if scoring_hand[i].ability.name == 'Glass Card' and not scoring_hand[i].debuff and pseudorandom('glass') < G.GAME.probabilities.normal/scoring_hand[i].ability.extra then" +position = "at" +payload = "if ((scoring_hand[i].ability.name == 'Glass Card' and not scoring_hand[i].debuff and pseudorandom('glass') < G.GAME.probabilities.normal/scoring_hand[i].ability.extra) or (G.GAME.modifiers.cry_shatter_rate and pseudorandom('cry_shatter') < 1/G.GAME.modifiers.cry_shatter_rate)) and not scoring_hand[i].ability.eternal then" +match_indent = true + + +# Yellow Stake - enhancement tarots don't remove stickers +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "perma_bonus = self.ability and self.ability.perma_bonus or 0," +position = "after" +payload = ''' +eternal = self.ability and self.ability.eternal, +perishable = self.ability and self.ability.perishable, +perish_tally = self.ability and self.ability.perish_tally, +rental = self.ability and self.ability.rental +''' +match_indent = true + + +# Amber Stake - edit number of booster packs +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "for i = 1, 2 do" +position = "at" +payload = "for i = 1, G.GAME.modifiers.cry_no_boosters and 0 or G.GAME.modifiers.cry_booster_packs or 2 do" +match_indent = true + + +# Quartz Stake - pinned effect applies in every type of slot +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 < b.T.x + b.T.w/2 end)" +position = "at" +payload = "table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end)" +match_indent = true + + +# Quartz Stake - render pinned sticker +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "elseif self.sprite_facing == 'back' then" +position = "before" +payload = ''' +if self.pinned then + G.shared_stickers['pinned'].role.draw_major = self + G.shared_stickers['pinned']:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_stickers['pinned']:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) +end +''' +match_indent = true + + +# Ruby Stake - big blind bosses +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "self.GAME.round_resets.blind_choices.Boss = get_new_boss()" +position = "before" +payload = ''' +if G.GAME.modifiers.cry_big_boss_rate and pseudorandom('cry_big_boss') < G.GAME.modifiers.cry_big_boss_rate then + self.GAME.round_resets.blind_choices.Big = get_new_boss() +elseif G.GAME.modifiers.cry_rush_hour_ii then + self.GAME.round_resets.blind_choices.Small = get_new_boss() + self.GAME.round_resets.blind_choices.Big = get_new_boss() +else + self.GAME.round_resets.blind_choices.Big = 'bl_big' +end +''' +match_indent = true + + +# Ruby Stake - big blind bosses +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "G.GAME.round_resets.blind_choices.Boss = get_new_boss()" +position = "before" +payload = ''' +if G.GAME.modifiers.cry_big_boss_rate and pseudorandom('cry_big_boss') < G.GAME.modifiers.cry_big_boss_rate then + G.GAME.round_resets.blind_choices.Big = get_new_boss() +elseif G.GAME.modifiers.cry_rush_hour_ii then + G.GAME.round_resets.blind_choices.Small = get_new_boss() + G.GAME.round_resets.blind_choices.Big = get_new_boss() +else + G.GAME.round_resets.blind_choices.Big = 'bl_big' +end +''' +match_indent = true + + +# Ruby Stake - big blind doesn't increase ante +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if G.GAME.blind:get_type() == 'Boss' then" +position = "at" +payload = "if G.GAME.blind_on_deck == 'Boss' then" +match_indent = true + +# Ruby Stake - smaller showdown blinds don't win +# Win on any ante above win_ante +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if G.GAME.round_resets.ante == G.GAME.win_ante and G.GAME.blind:get_type() == 'Boss' then" +position = "at" +payload = "if G.GAME.round_resets.ante >= G.GAME.win_ante and G.GAME.blind_on_deck == 'Boss' then" +match_indent = true + + +# Rush Hour - mark small blind as defeated +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if G.GAME.round_resets.blind == G.P_BLINDS.bl_small then" +position = "at" +payload = "if G.GAME.blind_on_deck == 'Small' then" +match_indent = true + +# Ruby Stake - mark big blind as defeated +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "elseif G.GAME.round_resets.blind == G.P_BLINDS.bl_big then" +position = "at" +payload = "elseif G.GAME.blind_on_deck == 'Big' then" +match_indent = true + + +# Sapphire Stake - ante tax +# The Joke boss effect +# Save game state for Revert +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "delay(0.4); ease_ante(1); delay(0.4); check_for_unlock({type = 'ante_up', ante = G.GAME.round_resets.ante + 1})" +position = "at" +payload = "delay(0.4); ease_ante(G.GAME.blind and G.GAME.blind:cry_calc_ante_gain() or 1); cry_apply_ante_tax(); delay(0.4); check_for_unlock({type = 'ante_up', ante = G.GAME.round_resets.ante + 1})" +match_indent = true + + +# Emerald Stake - Permanently flipped cards +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "if card.facing == 'back' and self.config.type ~= 'discard' and self.config.type ~= 'deck' and not stay_flipped then" +position = "at" +payload = '''if card.cry_flipped then card.facing = 'back'; card.sprite_facing = 'back' end +if not (card.cry_flipped and (self == G.shop_jokers or self == G.shop_vouchers or self == G.shop_booster)) and card.facing == 'back' and self.config.type ~= 'discard' and self.config.type ~= 'deck' and not stay_flipped then''' +match_indent = true + + +# Emerald Stake - flipped packs +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "create_shop_card_ui(card, 'Booster', G.shop_booster)" +position = "before" +payload = ''' +if G.GAME.modifiers.cry_enable_flipped_in_shop and pseudorandom('cry_flip_pack'..G.GAME.round_resets.ante) > 0.7 then + card.cry_flipped = true +end''' +match_indent = true + + +# Emerald Stake - flipped vouchers +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "create_shop_card_ui(card, 'Voucher', G.shop_vouchers)" +position = "before" +payload = ''' +if G.GAME.modifiers.cry_enable_flipped_in_shop and pseudorandom('cry_flip_vouch'..G.GAME.round_resets.ante) > 0.7 then + card.cry_flipped = true +end''' +match_indent = true + + +# Platinum Stake - start with big blind ready to be selected +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "G.GAME.blind_on_deck = 'Small'" +position = "at" +payload = "G.GAME.blind_on_deck = G.GAME.modifiers.cry_no_small_blind and 'Big' or 'Small'" +match_indent = true + + +# Platinum Stake - start with big blind ready to be selected +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "G.GAME.blind_on_deck = 'Small'" +position = "at" +payload = "G.GAME.blind_on_deck = G.GAME.modifiers.cry_no_small_blind and 'Big' or 'Small'" +match_indent = true + + +# Platinum Stake - hide Small Blind +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "G.GAME.round_resets.blind_states.Small = 'Upcoming'" +position = "at" +payload = "G.GAME.round_resets.blind_states.Small = G.GAME.modifiers.cry_no_small_blind and 'Hide' or 'Upcoming'" +match_indent = true + +# Ember Stake - grant no money on sell +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then''' +position = "at" +payload = '''elseif v.boss.showdown and (((G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2) or G.GAME.modifiers.cry_big_showdown ) then''' +match_indent = true + +# Ember Stake - give no money for selling +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''ease_dollars(self.sell_cost)''' +position = "at" +payload = '''if not G.GAME.modifiers.cry_no_sell_value then ease_dollars(self.sell_cost) end''' +match_indent = true + +# Ember Stake - don't play coin sound +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''play_sound('coin2')''' +position = "at" +payload = '''if not G.GAME.modifiers.cry_no_sell_value then play_sound('coin2') end''' +match_indent = true + +# Ember Stake - red dissolve for swag points +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''self:start_dissolve({G.C.GOLD})''' +position = "at" +payload = '''if G.GAME.modifiers.cry_no_sell_value then self:start_dissolve({G.C.RED}) else self:start_dissolve({G.C.GOLD}) end''' +match_indent = true + +# Ember Stake - remove sell price visually +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''self.sell_cost_label = self.facing == 'back' and '?' or self.sell_cost''' +position = "at" +payload = '''self.sell_cost_label = (self.facing == 'back' and '?') or (G.GAME.modifiers.cry_no_sell_value and 0) or self.sell_cost''' +match_indent = true + +# Dawn Stake - change maximum allowed highlights (i have no idea what this code is meant to be doing?? whatever it is, it doesn't seem to be working -toneblock) +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''local cfg = (card and card.ability) or _c['config']''' +position = "after" +payload = '''if cfg and G.GAME.modifiers.cry_consumable_reduce and cfg.max_highlighted and (cfg.max_highlighted > 1) then + local new_table = {} + for i0, j0 in pairs(cfg) do + new_table[i0] = j0 + end + new_table.max_highlighted = new_table.max_highlighted - 1 + cfg = new_table +end''' +match_indent = true + +# Horizon Stake - create random card at start of blind +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = '''G.GAME.blind:set_blind(G.GAME.round_resets.blind)''' +position = "after" +payload = '''if G.GAME.modifiers.cry_card_each_round then + G.E_MANAGER:add_event(Event({ + func = function() + local front = pseudorandom_element(G.P_CARDS, pseudoseed('cry_horizon')) + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local edition = G.P_CENTERS.c_base + local card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, front, G.P_CENTERS.c_base, {playing_card = G.playing_card}) + card:start_materialize() + if G.GAME.selected_back.effect.config.cry_force_edition and G.GAME.selected_back.effect.config.cry_force_edition ~= "random" then + local edition = {} + edition[G.GAME.selected_back.effect.config.cry_force_edition] = true + card:set_edition(edition, true, true); + end + G.play:emplace(card) + table.insert(G.playing_cards, card) + playing_card_joker_effects({true}) + return true + end})) + G.E_MANAGER:add_event(Event({ + func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + return true + end})) + draw_card(G.play,G.deck, 90,'up', nil) +end''' +match_indent = true + +# Blossom Stake - showdown blinds before the winning ante +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then''' +position = "at" +payload = '''elseif v.boss.showdown and (((G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2) or G.GAME.modifiers.cry_big_showdown ) then''' +match_indent = true + +# inject into vanilla calculate_perishable to prevent nil index bug (i don't know where it fails so i'm just patching every part of it...) +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''if self.ability.perishable and self.ability.perish_tally > 0 then''' +position = "before" +payload = '''if self.ability.perishable and not self.ability.perish_tally then self.ability.perish_tally = G.GAME.perishable_rounds end''' +match_indent = true + +# again in set_debuff +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''if self.ability.perishable and self.ability.perish_tally <= 0 then''' +position = "before" +payload = '''if self.ability.perishable and not self.ability.perish_tally then self.ability.perish_tally = G.GAME.perishable_rounds end''' +match_indent = true diff --git a/Cryptid/lovely/Sticker.toml b/Cryptid/lovely/Sticker.toml new file mode 100644 index 0000000..02eefdd --- /dev/null +++ b/Cryptid/lovely/Sticker.toml @@ -0,0 +1,312 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# ok it seems i still have to use the dumb fix for pinned, smods please overwrite +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''if self\.pinned then badges\[\#badges \+ 1\] \= 'pinned_left' end''' +position = "at" +payload = ''' +if self.pinned then + if self.ability.set == 'Booster' then + badges[#badges + 1] = 'cry_pinned_booster' + elseif self.ability.set == 'Voucher' then + badges[#badges + 1] = 'cry_pinned_voucher' + elseif self.ability.consumeable then + badges[#badges + 1] = 'cry_pinned_consumeable' + else + badges[#badges + 1] = 'pinned_left' + end +end +''' + +# sigh +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "if AUT.badges then" +position = "before" +payload = ''' +local function is_bad_badge(string) + local bad_badges = {'cry_pinned_booster', 'cry_pinned_voucher', 'cry_pinned_consumeable'} + for i = 1, #bad_badges do + if string == bad_badges[i] then return true end + end + return false +end +''' +match_indent = true + +# lmao wtf is this shit +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''badges[#badges + 1] = create_badge(localize(v, "labels"), get_badge_colour(v))''' +position = "at" +payload = ''' +if not is_bad_badge(v) then badges[#badges + 1] = create_badge(localize(v, "labels"), get_badge_colour(v)) end +''' +match_indent = true + +# this no longer just moves mod badges... it DUPES them, so now it's commented out (fml...) +# pinned badges should now no longer exist for non-jokers due to whatever the fuck is happening here +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''if AUT.info then''' +position = "before" +payload = ''' +-- if AUT.badges then +-- for k, v in ipairs(AUT.badges) do +-- local replaced = false +-- if v == 'cry_pinned_booster' or v == 'cry_pinned_voucher' or v == 'cry_pinned_consumeable' then replaced = true; v = 'pinned_left' end +-- if replaced == true then badges[#badges + 1] = create_badge(localize(v, "labels"), get_badge_colour(v)) end +-- end +--end +''' +match_indent = true + +# fucking hell +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if v == 'negative_consumable' then info_queue[#info_queue+1] = {key = 'e_negative_consumable', set = 'Edition', config = {extra = 1}} end" +position = "after" +payload = ''' +if v == 'cry_pinned_booster' then info_queue[#info_queue+1] = {key = 'cry_pinned_booster', set = 'Other'} end +if v == 'cry_pinned_voucher' then info_queue[#info_queue+1] = {key = 'cry_pinned_voucher', set = 'Other'} end +if v == 'cry_pinned_consumeable' then info_queue[#info_queue+1] = {key = 'cry_pinned_consumeable', set = 'Other'} end +''' +match_indent = true + +# initiate variables +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "rental_rate = 3," +position = "after" +payload = ''' +cry_voucher_perishable_rounds = 8, +cry_voucher_rental_rate = 2, +cry_consumeable_rental_rate = 2, +cry_voucher_banana_odds = 12, +cry_consumeable_banana_odds = 4, +cry_pinned_consumeables = 0, +cry_shop_joker_price_modifier = 1, +''' +match_indent = true + +# do more than just get voucher key +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "self.GAME.current_round.voucher = G.SETTINGS.tutorial_progress and G.SETTINGS.tutorial_progress.forced_voucher or get_next_voucher_key()" +position = "after" +payload = ''' +self.GAME.current_round.cry_voucher_edition = cry_get_next_voucher_edition() +self.GAME.current_round.cry_voucher_stickers = cry_get_next_voucher_stickers() +''' +match_indent = true + +# again, also check for pinned +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "G.GAME.current_round.voucher = get_next_voucher_key()" +position = "at" +payload = ''' +if G.GAME.current_round.cry_voucher_stickers.pinned == false then + G.GAME.current_round.voucher = get_next_voucher_key() + G.GAME.current_round.cry_voucher_edition = cry_get_next_voucher_edition() + G.GAME.current_round.cry_voucher_stickers = cry_get_next_voucher_stickers() +end +''' +match_indent = true + +# remove stickers if voucher is redeemed +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self:apply_to_run()" +position = "before" +payload = ''' +G.GAME.current_round.cry_voucher_edition = nil +G.GAME.current_round.cry_voucher_stickers = {eternal = false, perishable = false, rental = false, pinned = false, banana = false} +''' +match_indent = true + +# this is dumb but it saves overwrites + mod compat +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if not G.GAME.used_vouchers[v.key] then" +position = "at" +payload = ''' +if not G.GAME.cry_owned_vouchers[v.key] then +''' +match_indent = true + +# again +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if not G.GAME.used_vouchers[vv] then" +position = "at" +payload = ''' +if not G.GAME.cry_owned_vouchers[vv] then +''' +match_indent = true + +# add on redeem +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "G.GAME.used_vouchers[self.config.center_key] = true" +position = "before" +payload = ''' +G.GAME.cry_owned_vouchers[self.config.center_key] = true +''' +match_indent = true + +# initialise +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "used_vouchers = {}," +position = "after" +payload = ''' +cry_owned_vouchers = {}, +''' +match_indent = true + +# for challenge starting vouchers +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "G.GAME.used_vouchers[v.id] = true" +position = "after" +payload = ''' +G.GAME.cry_owned_vouchers[v.id] = true +''' +match_indent = true + +# for deck starting vouchers (no idea why there's a space here?) +[[patches]] +[patches.pattern] +target = "back.lua" +pattern = "G.GAME.used_vouchers[v ] = true" +position = "after" +payload = ''' +G.GAME.cry_owned_vouchers[v ] = true +''' +match_indent = true + +# for deck starting vouchers pt.2 apparently (is this part of the code even used? seems redundant) +[[patches]] +[patches.pattern] +target = "back.lua" +pattern = "G.GAME.used_vouchers[self.effect.config.voucher] = true" +position = "after" +payload = ''' +G.GAME.cry_owned_vouchers[self.effect.config.voucher] = true +''' +match_indent = true + +# consumeable sticker checking +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "local used_tarot = copier or self" +position = "after" +payload = ''' +if self.ability.rental then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + blocking = false, + blockable = false, + func = (function() + ease_dollars(-G.GAME.cry_consumeable_rental_rate) + return true + end)})) +end +local gone = false +if self.ability.banana then + if not self.ability.extinct then + if (pseudorandom('oops_it_banana') < G.GAME.probabilities.normal/G.GAME.cry_consumeable_banana_odds) then + local gone = true + self.ability.extinct = true + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + if self.area then self.area:remove_card(self) end + self:remove() + self = nil + return true; end})) + return true + end + })) + card_eval_status_text(self, 'jokers', nil, nil, nil, {message = localize('k_extinct_ex'), delay = 0.1}) + return true + end + end +end +if gone == false then +''' +match_indent = true + +# end the wrap +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "function Card:can_use_consumeable(any_state, skip_check)" +position = "before" +payload = ''' +end +''' +match_indent = true + +# check for pinned +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if G.STATE ~= G.STATES.HAND_PLAYED and G.STATE ~= G.STATES.DRAW_TO_HAND and G.STATE ~= G.STATES.PLAY_TAROT or any_state then" +position = "before" +payload = ''' +if G.GAME.cry_pinned_consumeables > 0 and not self.pinned then + return false +end +''' +match_indent = true + +# pinned consumeable remove, counterpart is in cryptid's create_card +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.queue_negative_removal then" +position = "before" +payload = ''' +if self.ability.consumeable and self.pinned and (G.GAME.cry_pinned_consumeables > 0) then + G.GAME.cry_pinned_consumeables = G.GAME.cry_pinned_consumeables - 1 +end +''' +match_indent = true + +# rental jank +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.rental then self.cost = 1 end" +position = "at" +payload = ''' +if self.ability.rental and (not (self.ability.set == "Planet" and #find_joker('Astronomer') > 0) and self.ability.set ~= "Booster") then self.cost = 1 end +''' +match_indent = true diff --git a/Cryptid/lovely/Universum.toml b/Cryptid/lovely/Universum.toml new file mode 100644 index 0000000..73165b3 --- /dev/null +++ b/Cryptid/lovely/Universum.toml @@ -0,0 +1,44 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Level up modifier +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "amount = amount or 1" +position = "after" +payload = "if not next(find_joker('cry-Universum')) then" +match_indent = true + + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].s_mult + G.GAME.hands[hand].l_mult*(G.GAME.hands[hand].level - 1), 1)" +position = "at" +payload = "G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].mult + G.GAME.hands[hand].l_mult*amount, 1)" +match_indent = true + + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].s_chips + G.GAME.hands[hand].l_chips*(G.GAME.hands[hand].level - 1), 0)" +position = "at" +payload = ''' +G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].chips + G.GAME.hands[hand].l_chips*amount, 1) +else + universum_mod = 1 + for i = 1, #G.jokers.cards do + local effects = G.jokers.cards[i]:calculate_joker({cry_universum = true, callback = function(card, effects) + universum_mod = universum_mod * (effects and effects.mod or 1) + end}) + end + G.GAME.hands[hand].level = math.max(0, G.GAME.hands[hand].level + amount) + G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].mult * (universum_mod)^amount, 1) + G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].chips * (universum_mod)^amount, 1) +end +''' +match_indent = true \ No newline at end of file diff --git a/Cryptid/lovely/VeryFair.toml b/Cryptid/lovely/VeryFair.toml new file mode 100644 index 0000000..0b88063 --- /dev/null +++ b/Cryptid/lovely/VeryFair.toml @@ -0,0 +1,70 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Remove voucher at start of run (also for Crimson Stake) +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "self.GAME.current_round.voucher = G.SETTINGS.tutorial_progress and G.SETTINGS.tutorial_progress.forced_voucher or get_next_voucher_key()" +position = "at" +payload = ''' +if not self.GAME.modifiers.cry_no_vouchers then + if not G.GAME.modifiers.cry_voucher_restock_antes or G.GAME.round_resets.ante % G.GAME.modifiers.cry_voucher_restock_antes == 0 then + self.GAME.current_round.voucher = G.SETTINGS.tutorial_progress and G.SETTINGS.tutorial_progress.forced_voucher or get_next_voucher_key() + end +else + very_fair_quip = pseudorandom_element(G.localization.misc.very_fair_quips, pseudoseed("cry_very_fair")) +end +''' +match_indent = true + + +# Remove voucher when defeating boss (also for Crimson Stake) +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "G.GAME.current_round.voucher = get_next_voucher_key()" +position = "at" +payload = ''' +if not G.GAME.modifiers.cry_no_vouchers then + if not G.GAME.modifiers.cry_voucher_restock_antes or G.GAME.round_resets.ante % G.GAME.modifiers.cry_voucher_restock_antes == 0 then + G.GAME.current_round.voucher = get_next_voucher_key() + end +else + very_fair_quip = pseudorandom_element(G.localization.misc.very_fair_quips, pseudoseed("cry_very_fair")) +end +''' +match_indent = true + + +# Change empty voucher text on Very Fair Deck +# This ain't localized, but that's a vanilla issue anyway +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "{n=G.UIT.T, config={text = 'DEFEAT', scale = 0.6, colour = G.C.WHITE}}" +position = "at" +payload = "{n=G.UIT.T, config={text = G.GAME.modifiers.cry_no_vouchers and (very_fair_quip[1] or '') or 'DEFEAT', scale = 0.6, colour = G.C.WHITE}}" +match_indent = true + + +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "{n=G.UIT.T, config={text = 'BOSS BLIND', scale = 0.4, colour = G.C.WHITE}}" +position = "at" +payload = "{n=G.UIT.T, config={text = G.GAME.modifiers.cry_no_vouchers and (very_fair_quip[2] or '') or G.GAME.modifiers.cry_voucher_restock_antes and G.GAME.round_resets.ante % G.GAME.modifiers.cry_voucher_restock_antes == 0 and 'TWO BOSS BLINDS' or 'BOSS BLIND', scale = 0.4, colour = G.C.WHITE}}" +match_indent = true + + +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "{n=G.UIT.T, config={text = 'TO RESTOCK', scale = 0.4, colour = G.C.WHITE}}" +position = "at" +payload = "{n=G.UIT.T, config={text = G.GAME.modifiers.cry_no_vouchers and (very_fair_quip[3] or '') or 'TO RESTOCK', scale = 0.4, colour = G.C.WHITE}}" +match_indent = true + + diff --git a/DebugPlus/README.MD b/DebugPlus/README.MD new file mode 100644 index 0000000..868e4c5 --- /dev/null +++ b/DebugPlus/README.MD @@ -0,0 +1,18 @@ +# DebugPlus +DebugPlus is a Balatro mod that adds a bunch of additional debug tools on top of Balatro's built in ones. Below are some of the highlights: + +- Console for logging and commands +- Win blind button +- Support for spawning vouchers, boosters, tags and blinds +- Save states +- A comically oversized spoonful of [new keybinds](keys.txt) + +## Installation +DebugPlus requires [lovely](https://github.com/ethangreen-dev/lovely-injector) to run. You can then download either the [latest release](https://github.com/WilsontheWolf/DebugPlus/releases), or [the latest code](https://github.com/WilsontheWolf/DebugPlus/archive/refs/heads/master.zip) directly into your `Mods` folder. + +You can hold tab to open the debug menu and press / to open the console. + +DebugPlus has a configuration UI which you can access from Mods > DebugPlus if Steamodded is installed, or Options > Settings > DebugPlus otherwise. + +## Development +For more information on the DebugPlus features and apis for mod developers see the [dev docs](docs/dev.md). diff --git a/DebugPlus/api.lua b/DebugPlus/api.lua new file mode 100644 index 0000000..aa9f4aa --- /dev/null +++ b/DebugPlus/api.lua @@ -0,0 +1,51 @@ +local console = require("debugplus.console") +local logger = require("debugplus.logger") + +local global = {} +local modIDs = { + debugplus = { + internal = true + } +} +-- API versions: +-- 1: Initial release + +function global.isVersionCompatible(version) + if version == 1 then + return true + end + return false +end + +local function createLogger(name) + return { + log = logger.createLogFn(name, "INFO"), + debug = logger.createLogFn(name, "DEBUG"), + info = logger.createLogFn(name, "INFO"), + warn = logger.createLogFn(name, "WARN"), + error = logger.createLogFn(name, "ERROR"), + } +end + +local function createCommandRegister(id) + return function (options) + return console.registerCommand(id, options) + end +end + +function global.registerID(name) + if not name then + return false, "Name not provided" + end + local id = string.lower(name) + if modIDs[id] then + return false, "ID " .. id .." already exists" + end + local ret = { + logger = createLogger(name), + addCommand = createCommandRegister(id) + } + return ret +end + +return global diff --git a/DebugPlus/assets/1x/modicon.png b/DebugPlus/assets/1x/modicon.png new file mode 100644 index 0000000..91a694a Binary files /dev/null and b/DebugPlus/assets/1x/modicon.png differ diff --git a/DebugPlus/assets/2x/modicon.png b/DebugPlus/assets/2x/modicon.png new file mode 100644 index 0000000..dc681f0 Binary files /dev/null and b/DebugPlus/assets/2x/modicon.png differ diff --git a/DebugPlus/config.lua b/DebugPlus/config.lua new file mode 100644 index 0000000..5de4c20 --- /dev/null +++ b/DebugPlus/config.lua @@ -0,0 +1,427 @@ +if string.match(debug.getinfo(1).source, '=%[SMODS %w+ ".+"]') then + error("Please update your steamodded thanks") +end + +local util = require("debugplus.util") +local loggerSucc, logger = pcall(require, "debugplus.logger") +local global = {} + +if not loggerSucc then -- To handle older lovely versions, where I can't properly load my deps. + return { + getValue = function() -- Can't error myself because it's not propagated, so I error in the first function that is called. + error("DebugPlus couldn't load a required component. Please make sure your lovely is up to date.\nYou can grab the latest lovely at: https://github.com/ethangreen-dev/lovely-injector/releases\n\n".. (logger or "No further info")) + end + } +end + + +local configDefinition = { + debugMode = { + label = "Debug Mode", + type = "toggle", + default = true, + info = {"Toggles everything in DebugPlus except the console."}, + onUpdate = function(v) _RELEASE_MODE = not v end + }, + ctrlKeybinds = { + label = util.ctrlText .. " for Keybinds", + type = "toggle", + default = true, + info = {"Requires you hold ".. util.ctrlText .. " when pressing the built in keybinds."} + }, + logLevel = { + label = "Log Level", + type = "select", + default = "INFO", + values = {"ERROR", "WARN", "INFO", "DEBUG"}, + info = { + "Only shows you logs of a certain level. This setting ignore command logs.", + "Will show all logs for the selected level and higher." + }, + -- Most of the time I wouldn't define onUpdate here for something in another module, + -- but I need to avoid circular dependencies and want the logger here. + onUpdate = function(v) + for k, v in pairs(logger.levelMeta) do + v.shouldShow = false + end + + logger.levelMeta.ERROR.shouldShow = true + if v == "ERROR" then return logger.handleLogsChange() end + logger.levelMeta.WARN.shouldShow = true + if v == "WARN" then return logger.handleLogsChange() end + logger.levelMeta.INFO.shouldShow = true + if v == "INFO" then return logger.handleLogsChange() end + logger.levelMeta.DEBUG.shouldShow = true + logger.handleLogsChange() + end + }, + showNewLogs = { + label = "Show New Logs", + type = "toggle", + default = true, + info = { + "Show a message when something is logged. Can also press shift + / to temporarily toggle." + } + }, + onlyCommands = { + label = "Only Show Commands", + type = "toggle", + default = false, + info = {"Do not show any logs, other than ones from commands or from you pressing debug keybinds."} + }, + showHUD = { + label = "Show Debug HUD", + type = "toggle", + default = true, + info = {"Shows some debug information on the top left of the screen."} + }, + -- Hidden config for rn. Even though they are hidden at the default logging level, + -- they are so frequent is starts clearing normal logs if left run for a bit. + -- This option was added so if someone does want them, they can be reenabled. + enableLongDT = { + label = "Enable Long DT Messages", + type = "toggle", + default = false, + info = {} + }, + processTables = { + label = "Automatically Expand Printed Tables", + type = "toggle", + default = true, + info = {"When a table is printed, expand it's contents (like in the eval command) instead of just strigifying it."} + }, + stringifyPrint = { + label = "Process Arguments Before Logging", + type = "toggle", + default = true, + info = { + "When this is enabled and something is printed to the lovely console/log DebugPlus will handle the processing of the args before logging them.", + "This allows the 'Automatically Expand Printed Tables' option to also show up in those logs." + } + }, + hyjackErrorHandler = { + label = "Console In Crash Handler", + type = "toggle", + default = true, + info = { + "When this is toggled, DebugPlus's console will be accessible in the error handler.", + "Requires Steamodded (or another tool to replace the error handler) to function", + "Requires a restart for the toggle to take effect" + } + } +} + +global.configDefinition = configDefinition + +local configPages = { -- TODO: implement paging, maybe only when I need to + { + name = "Console", + "showNewLogs", + "onlyCommands", + "logLevel", + "processTables", + "stringifyPrint", + "hyjackErrorHandler", + }, + { + name = "Misc", + "debugMode", + "ctrlKeybinds", + "showHUD", + } +} + +for k,v in pairs(configDefinition) do + v.key = k +end + +local testValues = {} +local configTypes +local configMemory + +local function parseConfigValue(val) + val = util.trim(val) + if val == "true" then + return true + end + if val == "false" then + return false + end + if val:sub(1, 1) == '"' and val:sub(#val) == '"' then + return val:sub(2, #val - 1):gsub("\\(.?)", { + ["\\"] = "\\", + n = "\n", + r = "\r" + }) + end + if tonumber(val) then + return tonumber(val) + end + return { + type = "raw", + val = val + } +end + +local function stringifyConfigValue(val) + if val == true then + return "true" + end + if val == false then + return "false" + end + if type(val) == "string" then + return '"' .. val:gsub("\\", "\\\\"):gsub("\n", "\\n"):gsub("\r", "\\r") .. '"' + end + if type(val) == "number" then + return string.format("%g", val) + end + if val.type == "raw" then + return val.val + end +end + +local function parseConfigFile(data) + local t = {} + for str in string.gmatch(data, "([^\n\r]+)") do + local name, val = str:match("(%w+)%s*=%s*(.+)") + if not name then + logger.errorLog("Failed to parse line:", str) + else + t[name] = parseConfigValue(val) + end + end + return t +end + +local function stringifyConfigFile(data) + local str = "" + for k, v in pairs(data) do + local val = stringifyConfigValue(v) + if val then + str = str .. k .. "=" .. val .. "\n" + end + end + return str +end + +local function loadSaveFromFile() + local content = love.filesystem.read("config/DebugPlus.jkr") + if not content then + return {} + end + local success, res = pcall(parseConfigFile, content) + if success and type(res) == "table" then + return res + end + logger.errorLog("Loading save err", res) + return {} +end + + +local function generateSaveFileTable() + if not configMemory then return loadSaveFromFile() end + local fin = {} + for k, v in pairs(configMemory) do + fin[k] = v.store + end + return fin +end + +local function updateSaveFile() + local conf = generateSaveFileTable() + love.filesystem.createDirectory("config") + local success, res = pcall(stringifyConfigFile, conf) + if success then + love.filesystem.write("config/DebugPlus.jkr", res) + else + logger.errorLog("Failure saving config", res) + end +end + + +function global.setValue(key, value) + local def = configDefinition[key] + if not def then return end + if configTypes[def.type] and configTypes[def.type].validate then + if not configTypes[def.type].validate(value, def) then + logger.errorLog('Value for saving key ' .. key .. ' failed to validate') + return + end + end + local mem = configMemory[key] + mem.store = value + mem.value = value + if def.onUpdate then + def.onUpdate(value) + end + updateSaveFile() +end + +function global.clearValue(key) + local def = configDefinition[key] + if not def then return end + local mem = configMemory[key] + mem.store = nil + mem.value = def.default + if def.onUpdate then + def.onUpdate(value) + end + updateSaveFile() +end + +function global.getValue(key) + local def = configDefinition[key] + if not def then return end + return configMemory[key].value +end + +configTypes = { + toggle = { + validate = function(data, def) + return type(data) == "boolean" + end, + render = function(def) + return create_toggle({ + label = def.label, + ref_table = configMemory[def.key], + ref_value = "value", + callback = function(v) global.setValue(def.key, v) end, + info = def.info + }) + end + }, + select = { + validate = function(data, def) + return util.hasValue(def.values, data) + end, + render = function(def) + local curr = util.hasValue(def.values, configMemory[def.key].value) or 1 + return create_option_cycle({ + options = def.values, + current_option = curr, + scale = 0.8, + opt_callback = "DP_conf_select_callback", + label = def.label, + info = def.info, + dp_key = def.key + }) + end + }, +} + +local function getDefaultsObject() + local config = {} + for k, v in pairs(configDefinition) do + config[k] = v.default + end + return config +end + + +local function generateMemory() + local defaults = getDefaultsObject() + local loaded = loadSaveFromFile() + + configMemory = {} + + for k, v in pairs(loaded) do + local store = v + local value = nil + local def = configDefinition[k] + if def then + if configTypes[def.type] and configTypes[def.type].validate then + if configTypes[def.type].validate(v, def) then + value = v + else + logger.errorLog('Value for saved key ' .. k .. ' failed to validate') + value = def.default + end + else + value = v + end + if def.onUpdate then + def.onUpdate(value) + end + end + configMemory[k] = { + store = store, + value = value, + } + end + + for k, v in pairs(defaults) do + if configMemory[k] then + goto continue + end + configMemory[k] = { + store = nil, + value = v, + } + ::continue:: + end +end + +function global.generateConfigTab(arg) + local index = arg.index or 1 + function G.FUNCS.DP_conf_select_callback(e) + global.setValue(e.cycle_config.dp_key, e.to_val) + end + local nodes = {} + for k,v in ipairs(configPages[index]) do + local def = configDefinition[v] + table.insert(nodes, configTypes[def.type].render(def)) + end + return { + -- ROOT NODE + n = G.UIT.ROOT, + config = {r = 0.1, minw = 7, minh = 5, align = "cm", padding = arg.source == "lovely" and .05 or .5, colour = arg.source == "lovely" and G.C.CLEAR or G.C.BLACK}, + nodes = { + { + -- COLUMN NODE TO ALIGN EVERYTHING INSIDE VERTICALLY + n = G.UIT.C, + config = {align = "tm", padding = 0.1}, + nodes = nodes + } + } + } +end + +function global.generateConfigTabs(source) + local tab = {} + for i,v in ipairs(configPages) do + table.insert(tab, { + label = v.name, + tab_definition_function = global.generateConfigTab, + tab_definition_function_args = {source = source, index = i } + }) + end + return tab +end + +function global.fakeConfigTab() + local tabs = global.generateConfigTabs("lovely") + tabs[1].chosen = true + G.FUNCS.overlay_menu({ + definition = create_UIBox_generic_options({ + back_func = "settings", + contents = {create_tabs({ + snap_to_nav = true, + -- colour = {.65, .36, 1, 1}, + tabs = tabs, + tab_h = 7.05, + tab_alignment = 'tm', + })} + }) + }) + return {} +end + +generateMemory() + +-- if debug.getinfo(1).source:match("@.*") then -- For when running under watch +-- logger.log("DebugPlus config in watch") +-- return global.generateConfigTab() -- For watch config_tab +-- end + +return global diff --git a/DebugPlus/console.lua b/DebugPlus/console.lua new file mode 100644 index 0000000..24cdf7e --- /dev/null +++ b/DebugPlus/console.lua @@ -0,0 +1,726 @@ +local util = require("debugplus.util") +local utf8 = require("utf8") +local watcher = require("debugplus.watcher") +local config = require("debugplus.config") +local logger = require("debugplus.logger") + +local global = {} + +local showTime = 5 -- Amount of time new console messages show up +local fadeTime = 1 -- Amount of time it takes for a message to fade +local consoleOpen = false +local openNextFrame = false +local gameKeyRepeat = love.keyboard.hasKeyRepeat() +local showNewLogs = config.getValue("showNewLogs") +local firstConsoleRender = nil +local history = {} +local currentHistory = nil +local commands = nil +local controller = nil +local logOffset = 0 + +commands = {{ + name = "echo", + source = "debugplus", + shortDesc = "Repeat's what you say", + desc = "Mostly just a testing command. Outputs what you input.", + exec = function(args, rawArgs, dp) + return rawArgs + end +}, { + name = "help", + source = "debugplus", + shortDesc = "Get command info", + desc = "Get's help about commands. When run without args, lists all commands and their short descriptions. When run with a command name, shows info about that command.", + exec = function(args, rawArgs, dp) + local toLookup = args[1] + if not toLookup then + local out = "Help:\nBelow is a list of commands.\n" + for k, v in ipairs(commands) do + out = out .. v.name .. ": " .. v.shortDesc .. "\n" + end + out = out .. "\nFor more information about a specific command, run 'help '" + return out + end + local cmdName = string.lower(string.gsub(toLookup, "^(%S+).*", "%1")) + local cmd + for i, c in ipairs(commands) do + if c.source .. ":" .. c.name == cmdName then + cmd = c + break + end + if c.name == cmdName then + cmd = c + break + end + end + if not cmd then + return '"' .. cmdName .. '" could not be found. To see a list of all commands, run "help" without any args', + "ERROR" + end + return cmd.name .. ":\n" .. cmd.desc .. "\n\nThis command can be run by typing '" .. cmd.name .. "' or '" .. + cmd.source .. ":" .. cmd.name .. "'." + end +}, { + name = "eval", + source = "debugplus", + shortDesc = "Evaluate lua code", + desc = "Execute's lua code. This code has access to all the globals that the game has, as well as a dp object, with some DebugPlus specific stuff.", + exec = function(args, rawArgs, dp) + local env = {} + for k, v in pairs(_G) do + env[k] = v + end + env.dp = dp + local func, err = load("return " .. rawArgs, "DebugPlus Eval", "t", env) + if not func then + func, err = load(rawArgs, "DebugPlus Eval", "t", env) + end + if not func then + return "Syntax Error: " .. err, "ERROR" + end + local success, res = pcall(func) + if not success then + return "Error: " .. res, "ERROR" + end + return util.stringifyTable(res) + end +}, { + name = "money", + source = "debugplus", + shortDesc = "Set or add money", + desc = "Set or add to your money. Usage:\nmoney set [amount] - Set your money to the given amount\nmoney add [amount] - Adds the given amount to your money.", + exec = function(args, rawArgs, dp) + if G.STAGE ~= G.STAGES.RUN then + return "This command must be run during a run.", "ERROR" + end + local subCmd = args[1] + local amount = tonumber(args[2]) + if subCmd == "set" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.dollars = amount + elseif subCmd == "add" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.dollars = G.GAME.dollars + amount + else + return "Please choose whether you want to add or set. For more info, run 'help money'" + end + return "Money is now $" .. G.GAME.dollars + end +}, { + name = "round", + source = "debugplus", + shortDesc = "Set or add to your round", + desc = "Set or add to your round. Usage:\nround set [amount] - Set the current round to the given amount\nround add [amount] - Adds the given number of rounds.", + exec = function(args, rawArgs, dp) + if G.STAGE ~= G.STAGES.RUN then + return "This command must be run during a run.", "ERROR" + end + local subCmd = args[1] + local amount = tonumber(args[2]) + if subCmd == "set" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.round = amount + elseif subCmd == "add" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.round = G.GAME.round + amount + else + return "Please choose whether you want to add or set. For more info, run 'help round'" + end + return "Round is now " .. G.GAME.round + end +}, { + name = "ante", + source = "debugplus", + shortDesc = "Set or add to your ante", + desc = "Set or add to your ante. Usage:\nante set [amount] - Set the current ante to the given amount\nante add [amount] - Adds the given number of antes.", + exec = function(args, rawArgs, dp) + if G.STAGE ~= G.STAGES.RUN then + return "This command must be run during a run.", "ERROR" + end + local subCmd = args[1] + local amount = tonumber(args[2]) + if subCmd == "set" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.round_resets.ante = amount + elseif subCmd == "add" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.round_resets.ante = G.GAME.round_resets.ante + amount + else + return "Please choose whether you want to add or set. For more info, run 'help ante'" + end + return "Ante is now " .. G.GAME.round_resets.ante + end +}, { + name = "discards", + source = "debugplus", + shortDesc = "Set or add to your hand", + desc = "Set or add to your hand. Usage:\ndiscards set [amount] - Set the current hand to the given amount\ndiscards add [amount] - Adds the given number of discards.", + exec = function(args, rawArgs, dp) + if G.STAGE ~= G.STAGES.RUN then + return "This command must be run during a run.", "ERROR" + end + local subCmd = args[1] + local amount = tonumber(args[2]) + if subCmd == "set" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.current_round.discards_left = amount + elseif subCmd == "add" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.current_round.discards_left = G.GAME.current_round.discards_left + amount + else + return "Please choose whether you want to add or set. For more info, run 'help hand'" + end + return "Discards are now " .. G.GAME.current_round.discards_left + end +}, { + name = "hands", + source = "debugplus", + shortDesc = "Set or add to your hand", + desc = "Set or add to your hand. Usage:\nhands set [amount] - Set the current hand to the given amount\nhands add [amount] - Adds the given number of hands.", + exec = function(args, rawArgs, dp) + if G.STAGE ~= G.STAGES.RUN then + return "This command must be run during a run.", "ERROR" + end + local subCmd = args[1] + local amount = tonumber(args[2]) + if subCmd == "set" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.current_round.hands_left = amount + elseif subCmd == "add" then + if not amount then + return "Please provide a valid number to set/add.", "ERROR" + end + G.GAME.current_round.hands_left = G.GAME.current_round.hands_left + amount + else + return "Please choose whether you want to add or set. For more info, run 'help hand'" + end + return "Hands are now " .. G.GAME.current_round.hands_left + end +}, { + name = "watch", + source = "debugplus", + shortDesc = "Watch and execute a file when it changes.", + desc = "Watch and execute a file when it changes. Usage:\nwatch stop - Stop's watching files.\n".. watcher.subCommandDesc .."Files should be a relative path to a file in the save directory (e.g. `Mods/Example/test.lua`)", + exec = function(args, rawArgs, dp) + local subCmd = args[1] + local file = args[2] + if subCmd == "stop" then + watcher.stopWatching() + return "I will stop watching for file changes." + elseif watcher.types[subCmd] then + local succ, err = watcher.startWatching(file, subCmd) + if not succ then return err, "ERROR" end + return "Started watching " .. file + else + return "Please provide a valid sub command. For more info, run 'help watch'" + end + end +}, { + name = "tutorial", + source = "debugplus", + shortDesc = "Modify the tutorial state.", + desc = "Modify the tutorial state. Usage:\ntutorial finish - Finish the tutorial.\ntutorial reset - Reset the tutorial progress to a fresh state.\ntutorial new - Starts a new tutorial run (like hitting play for the first time)", + exec = function(args, rawArgs, dp) + local subCmd = args[1] + if subCmd == "finish" then + if G.OVERLAY_TUTORIAL then + G.FUNCS.skip_tutorial_section() + end + G.SETTINGS.tutorial_complete = true + G.SETTINGS.tutorial_progress = nil + return "Tutorial finished." + elseif subCmd == "reset" then + G.SETTINGS.tutorial_complete = false + G.SETTINGS.tutorial_progress = { + forced_shop = {'j_joker', 'c_empress'}, + forced_voucher = 'v_grabber', + forced_tags = {'tag_handy', 'tag_garbage'}, + hold_parts = {}, + completed_parts = {} + } + return "Tutorial reset." + elseif subCmd == "new" then + G.FUNCS.start_tutorial() + return "Starting a new run." + else + return "Please provide a valid sub command. For more info, run 'help tutorial'" + end + end +}, { + name = "resetshop", + source = "debugplus", + shortDesc = "Reset the shop.", + desc = "Resets the shop.", + exec = function(args, rawArgs, dp) + if G.STATE ~= G.STATES.SHOP then + return "This command can only be run in a shop.", 'ERROR' + end + G.shop:remove() + G.shop = nil + G.SHOP_SIGN:remove() + G.SHOP_SIGN = nil + G.GAME.current_round.used_packs = nil + G.STATE_COMPLETE = false + G:update_shop() + return "Reset shop." + end +}, { + name = "value", + source = "debugplus", + shortDesc = "Get and modify highlighted card values", + desc = "Retrives or modifies the values of the currently hovered card. Usage:\nvalue get - Gets all detected values on the hovered card.\nvalue set [keys] [value] - Modifies a value of hovered card. The format of keys should match the 'get' command.\nvalue set_center [keys] [value] - Modifies a value on the center of the hovered card. This will modify future versions of the card.", + exec = function (args, rawArgs, dp) + local unmodified_vals = { + bonus = 0, + perma_bonus = 0, + extra_value = 0, + p_dollars = 0, + h_mult = 0, + h_x_mult = 0, + h_dollars = 0, + h_size = 0, + d_size = 0, + hands_played_at_create = 0, + mult = 0, + x_mult = 1, + e_mult = 0, + ee_mult = 0, + eee_mult = 0, + x_chips = 0, + e_chips = 0, + ee_chips = 0, + eee_chips = 0, + t_mult = 0, + t_chips = 0, + } + local ignore_vals = { + name = true, + set = true, + order = true, + consumeable = true + } + if dp.hovered:is(Card) then + if args[1] == "get" then + local values = "Values:" + for k, v in pairs(dp.hovered.ability) do + if (not ignore_vals[k]) and (not unmodified_vals[k] or unmodified_vals[k] ~= dp.hovered.ability[k]) then + if k == "hyper_chips" or k == "hyper_mult" then + if dp.hovered.ability[k][1] ~= 0 or dp.hovered.ability[k][2] ~= 0 then + values = values .. "\n" .. tostring(k) .. " " .. tostring(dp.hovered.ability[k][1]) .. " " .. tostring(dp.hovered.ability[k][2]) + end + elseif type(dp.hovered.ability[k]) == "table" then + for kk, vv in pairs(dp.hovered.ability[k]) do + values = values .. "\n" .. tostring(k) .. " " .. tostring(kk) .. " " .. tostring(vv) + end + elseif dp.hovered.ability[k] ~= "" then + values = values .. "\n" .. tostring(k) .. " " .. tostring(dp.hovered.ability[k]) + end + end + end + return values + elseif args[1] == "set" or args[1] == "set_center" then + local root = dp.hovered.ability + if args[1] == "set_center" then + root = dp.hovered.config.center.config + end + local rootC + if dp.hovered.ability.consumeable then + rootC = root.consumeable + end + if #args < 2 then + return "Please provide a key to set", "ERROR" + end + if #args < 3 then + return "Please provide a value to set", "ERROR" + end + for i = 2, #args-2 do + root = root[args[i]] + if rootC then rootC = rootC[args[i]] end + end + if tonumber(args[#args]) then --number + root[args[#args-1]] = tonumber(args[#args]) + if rootC then rootC[args[#args-1]] = tonumber(args[#args]) end + elseif args[#args] == "true" then --bool + root[args[#args-1]] = true + if rootC then rootC[args[#args-1]] = true end + elseif args[#args] == "false" then + root[args[#args-1]] = false + if rootC then rootC[args[#args-1]] = false end + else + root[args[#args-1]] = args[#args] + if rootC then rootC[args[#args-1]] = args[#args] end + end + return "Value set successfully." + else + return "Invalid argument. Use 'get' or 'set' or 'set_center'.", "ERROR" + end + else + return "This command only works while hovering over a card. Rerun it while hovering over a card.", "ERROR" + end + end +}} +local inputText = "" + +local function runCommand() + inputText = util.trim(inputText) + if inputText == "" then + return + end + + logger.handleLog({1, 0, 1}, "INFO", "> " .. inputText) + if history[1] ~= inputText then + table.insert(history, 1, inputText) + end + + local cmdName = string.lower(string.gsub(inputText, "^(%S+).*", "%1")) + local rawArgs = string.gsub(inputText, "^%S+%s*(.*)", "%1") + local args = {} + for w in string.gmatch(rawArgs, "%S+") do + table.insert(args, w) + end + + inputText = "" + consoleOpen = false + love.keyboard.setKeyRepeat(gameKeyRepeat) + + local cmd + for i, c in ipairs(commands) do + if c.source .. ":" .. c.name == cmdName then + cmd = c + break + end + if c.name == cmdName then + cmd = c + break + end + end + if not cmd then + return logger.handleLog({1, 0, 0}, "ERROR", "< ERROR: Command '" .. cmdName .. "' not found.") + end + local dp = { + hovered = G.CONTROLLER.hovering.target, + handleLog = logger.handleLog + } + local success, result, loglevel, colourOverride = pcall(cmd.exec, args, rawArgs, dp) + if not success then + return logger.handleLog({1, 0, 0}, "ERROR", "< An error occurred processing the command:", result) + end + local level = loglevel or "INFO" + if not logger.levelMeta[level] then + level = "INFO" + logger.handleLogAdvanced({ + level = "WARN", + }, "[DebugPlus] Command ".. cmdName.. " returned an invalid log level. Defaulting to INFO.") + end + local colour = colourOverride or logger.levelMeta[level].colour + if success and success ~= "" then + return logger.handleLog(colour, level, "<", result) + else + return logger.handleLog(colour, level, "< Command exited without a response.") + end +end + +function global.consoleHandleKey(key) + if not consoleOpen then + if key == '/' or key == 'kp/' then + if util.isShiftDown() then + showNewLogs = not showNewLogs + else + openNextFrame = true -- This is to prevent the keyboard handler from typing this key + end + end + return true + end + + if key == "escape" then + consoleOpen = false + love.keyboard.setKeyRepeat(gameKeyRepeat) + inputText = "" + end + -- This bit stolen from https://love2d.org/wiki/love.textinput + if key == "backspace" then + -- get the byte offset to the last UTF-8 character in the string. + local byteoffset = utf8.offset(inputText, -1) + + if byteoffset then + -- remove the last UTF-8 character. + -- string.sub operates on bytes rather than UTF-8 characters, so we couldn't do string.sub(text, 1, -2). + inputText = string.sub(inputText, 1, byteoffset - 1) + end + end + + if key == "return" then + if util.isShiftDown() then + inputText = inputText .. "\n" + else + runCommand() + end + end + + if key == "v" and util.isCtrlDown() then + inputText = inputText .. love.system.getClipboardText() + end + + if key == "up" then + if currentHistory.index >= #history then + return + end + if currentHistory.index == 0 then + currentHistory.val = inputText + end + currentHistory.index = currentHistory.index + 1 + inputText = history[currentHistory.index] + end + + if key == "down" then + if currentHistory.index <= 0 then + return + end + currentHistory.index = currentHistory.index - 1 + if currentHistory.index == 0 then + inputText = currentHistory.val + else + inputText = history[currentHistory.index] + end + end + +end + +local orig_textinput = love.textinput +local function textinput(t) + if orig_textinput then + orig_textinput(t) + end -- That way if another mod uses this, I don't clobber it's implementation + if not consoleOpen then + return + end + inputText = inputText .. t +end +love.textinput = textinput + +local orig_wheelmoved = love.wheelmoved +local function wheelmoved(x, y) + if orig_wheelmoved then + orig_wheelmoved(x, y) + end + if not consoleOpen then + return + end + logOffset = math.min(math.max(logOffset + y, 0), #logger.logs - 1) +end +love.wheelmoved = wheelmoved + +local function calcHeight(text, width) + local font = love.graphics.getFont() + local rw, lines = font:getWrap(text, width) + local lineHeight = font:getHeight() + + return #lines * lineHeight, rw, lineHeight +end + +local function hyjackErrorHandler() + local orig = love.errorhandler + if not orig then -- Vanilla + return -- Doesn't work with love.errhand (need love.errorhandler) + end + local function safeCall(func, ...) + local succ, res = pcall(func, ...) + if not succ then print("ERROR", res) + else return res end + end + function love.errorhandler(msg) + local ret = orig(msg) + orig_wheelmoved = nil + orig_textinput = nil + consoleOpen = false + inputText = "" + love.keyboard.setKeyRepeat(gameKeyRepeat) + local justCrashed = true + + local present = love.graphics.present + function love.graphics.present() + local r, g, b, a = love.graphics.getColor() + if justCrashed then + firstConsoleRender = love.timer.getTime() + justCrashed = false + end + safeCall(global.doConsoleRender) + love.graphics.setColor(r,g,b,a) + present() + end + + return function() + love.event.pump() + + local evts = {} + + for e, a, b, c in love.event.poll() do + if consoleOpen and e == "textinput" then + safeCall(textinput, a) + elseif consoleOpen and e == "wheelmoved" then + safeCall(wheelmoved, a, b) + elseif e == "keypressed" then + if safeCall(global.consoleHandleKey, a) then + table.insert(evts, {e,a,b,c}) + end + else + table.insert(evts, {e,a,b,c}) + end + end + for _,v in ipairs(evts) do -- Add back for the original handler + love.event.push(unpack(v)) + end + return ret() + end + end +end + +function global.doConsoleRender() + if openNextFrame then + consoleOpen = true + openNextFrame = false + currentHistory = { + index = 0, + val = "" + } + logOffset = 0 + gameKeyRepeat = love.keyboard.hasKeyRepeat() + love.keyboard.setKeyRepeat(true) + end + if not consoleOpen and not showNewLogs then + return + end + -- Setup + local width, height = love.graphics.getDimensions() + local padding = 10 + local lineWidth = width - padding * 2 + local bottom = height - padding * 2 + local now = love.timer.getTime() + if firstConsoleRender == nil then + if config.getValue("hyjackErrorHandler") then hyjackErrorHandler() end + firstConsoleRender = now + logger.logCmd("Press [/] to toggle console and press [shift] + [/] to toggle new log previews") + end + -- Input Box + love.graphics.setColor(0, 0, 0, .5) + if consoleOpen then + bottom = bottom - padding * 2 + local text = "> " .. inputText + local lineHeight, realWidth, singleLineHeight = calcHeight(text, lineWidth) + love.graphics.rectangle("fill", padding, bottom - lineHeight + padding, lineWidth, lineHeight + padding * 2) + love.graphics.setColor(1, 1, 1, 1) + love.graphics.printf(text, padding * 2, bottom - lineHeight + singleLineHeight, lineWidth - padding * 2) + + bottom = bottom - lineHeight - padding * 2 + end + + -- Main window + if consoleOpen then + love.graphics.setColor(0, 0, 0, .5) + love.graphics.rectangle("fill", padding, padding, lineWidth, bottom) + end + for i = #logger.logs, 1, -1 do + local v = logger.logs[i] + if consoleOpen and #logger.logs - logOffset < i then -- TODO: could this be more efficent? + goto finishrender + end + if not consoleOpen and v.time < firstConsoleRender then + break + end + local age = now - v.time + if not consoleOpen and age > showTime + fadeTime then + break + end + if not logger.levelMeta[v.level].shouldShow and not v.command then + goto finishrender + end + if not v.command and config.getValue("onlyCommands") then + goto finishrender + end + local msg = v.str + if consoleOpen and not v.hack_no_prefix then + msg = "[" .. string.sub(v.level, 1, 1) .. "] " .. msg + end + local lineHeight, realWidth = calcHeight(msg, lineWidth) + bottom = bottom - lineHeight + if bottom < padding then + break + end + + local opacityPercent = 1 + if not consoleOpen and age > showTime then + opacityPercent = (fadeTime - (age - showTime)) / fadeTime + end + + if not consoleOpen then + love.graphics.setColor(0, 0, 0, .5 * opacityPercent) + love.graphics.rectangle("fill", padding, bottom, lineWidth, lineHeight) + end + love.graphics.setColor(v.colour[1], v.colour[2], v.colour[3], opacityPercent) + + love.graphics.printf(msg, padding * 2, bottom, lineWidth - padding * 2) + ::finishrender:: + end +end + +function global.registerCommand(id, options) + if not options then + error("Options must be provided") + end + if not options.name and not string.match(options.name, "^[%l%d_-]$") then + error("Options.name must be provided and match pattern `^[%l%d_-]$`.") + end + if not options.exec or type(options.exec) ~= "function" then + error("Options.exec must be a function") + end + if not options.shortDesc or type(options.shortDesc) ~= "string" then + error("Options.shortDesc must be a string") + end + if not options.desc or type(options.desc) ~= "string" then + error("Options.desc must be a string") + end + local cmd = { + source = id, + name = options.name, + exec = options.exec, + shortDesc = options.shortDesc, + desc = options.desc + } + for k, v in ipairs(commands) do + if v.source == cmd.source and v.name == cmd.name then + error("This command already exists") + end + end + table.insert(commands, cmd) +end + +local function handleLogsChange(added) + added = added or 0 + logOffset = math.min(logOffset + added, #logger.logs) +end + +logger.handleLogsChange = handleLogsChange +config.configDefinition.showNewLogs.onUpdate = function(v) + showNewLogs = v +end + +return global diff --git a/DebugPlus/core.lua b/DebugPlus/core.lua new file mode 100644 index 0000000..ba1c1e5 --- /dev/null +++ b/DebugPlus/core.lua @@ -0,0 +1,466 @@ +local logger = require("debugplus.logger") +local util = require("debugplus.util") +local global = {} + +local enhancements = nil +local seals = nil +local suits = nil +local ranks = nil +local saveStateKeys = {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"} +local log = logger.log + +local function getSeals() + if seals then + return seals + end + seals = {"None"} + for i, v in pairs(G.P_SEALS) do + seals[v.order + 1] = i + end + + return seals +end + +local function getEnhancements() + if enhancements then + return enhancements + end + enhancements = {"c_base"} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + enhancements[v.order] = v.key + end + return enhancements +end + +local function getSuits() + if suits then + return suits + end + suits = {} + for k, v in pairs(G.C.SUITS) do + table.insert(suits, k) + end + return suits +end + +local function getRanks() + if ranks then + return ranks + end + ranks = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "Jack", "Queen", "King", "Ace"} -- No built in api for this, yippe + if SMODS and SMODS.Ranks then + for k, v in pairs(SMODS.Ranks) do + if not util.hasValue(ranks, v.key) then + table.insert(ranks, v.key) + end + end + end + return ranks +end + +function global.handleKeys(controller, key, dt) + if controller.hovering.target and controller.hovering.target:is(Card) then + local _card = controller.hovering.target + if key == 'w' then + if _card.playing_card then + for i, v in ipairs(getEnhancements()) do + if _card.config.center == G.P_CENTERS[v] then + local _next = i + 1 + if _next > #enhancements then + _card:set_ability(G.P_CENTERS[enhancements[1]], nil, true) + else + _card:set_ability(G.P_CENTERS[enhancements[_next]], nil, true) + end + break + end + end + end + end + if key == "e" then + if _card.playing_card then + for i, v in ipairs(getSeals()) do + if (_card:get_seal(true) or "None") == v then + local _next = i + 1 + if _next > #seals then + _next = 1 + end + if _next == 1 then + _card:set_seal(nil, true) + else + _card:set_seal(seals[_next], true) + end + break + end + end + end + end + if key == "a" then + if _card.ability.set == 'Joker' then + _card.ability.eternal = not _card.ability.eternal + end + end + if key == "s" then + if _card.ability.set == 'Joker' then + _card.ability.perishable = not _card.ability.perishable + _card.ability.perish_tally = G.GAME.perishable_rounds + end + end + if key == "d" then + if _card.ability.set == 'Joker' then + _card.ability.rental = not _card.ability.rental + _card:set_cost() + end + end + if key == "f" then + if _card.ability.set == 'Joker' or _card.playing_card or _card.area then + _card.ability.couponed = not _card.ability.couponed + _card:set_cost() + end + end + if key == "c" then + local _area + if _card.ability.set == 'Joker' then + _area = G.jokers + elseif _card.playing_card then + if G.hand and G.hand.config.card_count ~= 0 then + _area = G.hand + else + _area = G.deck + end + elseif _card.ability.consumeable then + _area = G.consumeables + end + if _area == nil then + return log("Error: Trying to dup card without an area") + end + local new_card = copy_card(_card, nil, nil, _card.playing_card) + new_card:add_to_deck() + if _card.playing_card then + table.insert(G.playing_cards, new_card) + end + _area:emplace(new_card) + + end + if key == "r" then + if _card.ability.name == "Glass Card" then + _card.shattered = true + end + _card:remove() + if _card.playing_card then + for j = 1, #G.jokers.cards do + eval_card(G.jokers.cards[j], { + cardarea = G.jokers, + remove_playing_cards = true, + removed = {_card} + }) + end + end + end + if key == 'up' then + if _card.playing_card then + for i, v in ipairs(getRanks()) do + if _card.base.value == v then + local _next = i + 1 + if _next > #ranks then + local new_card + for i, c in pairs(G.P_CARDS) do + if c.value == ranks[1] and c.suit == _card.base.suit then + new_card = c + break + end + end + if not new_card then + log("Error: Could not find card with rank", ranks[1], "and suit", _card.base.suit) + return + end + _card:set_base(new_card) + G.GAME.blind:debuff_card(_card) + else + local new_card + for i, c in pairs(G.P_CARDS) do + if c.value == ranks[_next] and c.suit == _card.base.suit then + new_card = c + break + end + end + if not new_card then + log("Error: Could not find card with rank", ranks[_next], "and suit", _card.base.suit) + return + end + _card:set_base(new_card) + G.GAME.blind:debuff_card(_card) + end + break + end + end + end + end + if key == 'down' then + if _card.playing_card then + for i, v in ipairs(getRanks()) do + if _card.base.value == v then + local _next = i - 1 + if _next < 1 then + local new_card + for i, c in pairs(G.P_CARDS) do + if c.value == ranks[#ranks] and c.suit == _card.base.suit then + new_card = c + break + end + end + if not new_card then + log("Error: Could not find card with rank", ranks[#ranks], "and suit", _card.base.suit) + return + end + _card:set_base(new_card) + G.GAME.blind:debuff_card(_card) + else + local new_card + for i, c in pairs(G.P_CARDS) do + if c.value == ranks[_next] and c.suit == _card.base.suit then + new_card = c + break + end + end + if not new_card then + log("Error: Could not find card with rank", ranks[_next], "and suit", _card.base.suit) + return + end + _card:set_base(new_card) + G.GAME.blind:debuff_card(_card) + end + break + end + end + end + end + if key == 'right' then + if _card.playing_card then + for i, v in ipairs(getSuits()) do + if _card.base.suit == v then + local _next = i + 1 + if _next > #suits then + _card:change_suit(suits[1]) + else + _card:change_suit(suits[_next]) + end + break + end + end + end + end + if key == 'left' then + if _card.playing_card then + for i, v in ipairs(getSuits()) do + if _card.base.suit == v then + local _next = i - 1 + if _next < 1 then + _card:change_suit(suits[#suits]) + else + _card:change_suit(suits[_next]) + end + break + end + end + end + end + end + + local _element = controller.hovering.target + if _element and _element.config and _element.config.tag then + local _tag = _element.config.tag + if key == "2" then + G.P_TAGS[_tag.key].unlocked = true + G.P_TAGS[_tag.key].discovered = true + G.P_TAGS[_tag.key].alerted = true + _tag.hide_ability = false + set_discover_tallies() + G:save_progress() + _element:set_sprite_pos(_tag.pos) + end + if key == "3" or key == "c" then + if G.STAGE == G.STAGES.RUN then + add_tag(Tag(_tag.key, false, 'Big')) + end + end + if key == "r" then + _element.config.tag:remove() + end + end + if _element and _element.config and _element.config.blind then + local _blind = _element.config.blind + if key == "2" then + G.P_BLINDS[_blind.key].unlocked = true + G.P_BLINDS[_blind.key].discovered = true + G.P_BLINDS[_blind.key].alerted = true + if _element.set_sprite_pos then -- vanilla + _element:set_sprite_pos(_blind.pos) + else -- SMODS + _element.children.center:set_sprite_pos(_blind.pos) + end + set_discover_tallies() + G:save_progress() + end + if key == "3" then + if G.STATE == G.STATES.BLIND_SELECT then + local par = G.blind_select_opts.boss.parent + G.GAME.round_resets.blind_choices.Boss = _blind.key + + G.blind_select_opts.boss:remove() + G.blind_select_opts.boss = UIBox { + T = {par.T.x, 0, 0, 0}, + definition = { + n = G.UIT.ROOT, + config = { + align = "cm", + colour = G.C.CLEAR + }, + nodes = {UIBox_dyn_container({create_UIBox_blind_choice('Boss')}, false, + get_blind_main_colour('Boss'), mix_colours(G.C.BLACK, get_blind_main_colour('Boss'), 0.8))} + }, + config = { + align = "bmi", + offset = { + x = 0, + y = G.ROOM.T.y + 9 + }, + major = par, + xy_bond = 'Weak' + } + } + par.config.object = G.blind_select_opts.boss + par.config.object:recalculate() + G.blind_select_opts.boss.parent = par + G.blind_select_opts.boss.alignment.offset.y = 0 + + for i = 1, #G.GAME.tags do + if G.GAME.tags[i]:apply_to_run({ + type = 'new_blind_choice' + }) then + break + end + end + end + end + end + + if key == "1" or key == "2" then -- Reload any tooltips when unlocking/discovering stuff + if _element.stop_hover and _element.hover then + _element:stop_hover() + _element:hover() + end + end + + if key == "m" then + G.FUNCS.change_pixel_smoothing({ + to_key = G.SETTINGS.GRAPHICS.texture_scaling + }) + log("Reloaded Atlases") + end + + for i, v in ipairs(saveStateKeys) do + if key == v and love.keyboard.isDown("z") then + if G.STAGE == G.STAGES.RUN then + if not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == + G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.STANDARD_PACK or G.STATE == G.STATES.BUFFOON_PACK or + G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then + save_run() + end + compress_and_save(G.SETTINGS.profile .. '/' .. 'debugsave' .. v .. '.jkr', G.ARGS.save_run) + log("Saved to slot", v) + end + end + if key == v and love.keyboard.isDown("x") then + G:delete_run() + G.SAVED_GAME = get_compressed(G.SETTINGS.profile .. '/' .. 'debugsave' .. v .. '.jkr') + if G.SAVED_GAME ~= nil then + G.SAVED_GAME = STR_UNPACK(G.SAVED_GAME) + end + G:start_run({ + savetext = G.SAVED_GAME + }) + log("Loaded slot", v) + end + end +end + +function global.registerButtons() + G.FUNCS.DT_win_blind = function() + if G.STATE ~= G.STATES.SELECTING_HAND then + return + end + G.GAME.chips = G.GAME.blind.chips + G.STATE = G.STATES.HAND_PLAYED + G.STATE_COMPLETE = true + end_round() + end + G.FUNCS.DT_double_tag = function() + if G.STAGE == G.STAGES.RUN then + add_tag(Tag('tag_double', false, 'Big')) + end + end +end + +function global.togglePerfUI() + if G.F_ENABLE_PERF_OVERLAY == G.SETTINGS.perf_mode then -- first time run + G.SETTINGS.perf_mode = true + end + G.F_ENABLE_PERF_OVERLAY = G.SETTINGS.perf_mode + if G.F_ENABLE_PERF_OVERLAY then + if not silent then + log("Enabled profiler overlay. Press 'p' again to disable it.") + end + else + if not silent then + log("Disabled profiler overlay.") + end + end +end + +function global.profileMessage() + if G.prof then + log("Enabled performance profiler. Press 'v' again to disable it.") + else + log("Disabled performance profiler. Check console for results.") + end +end + +function global.handleSpawn(controller, _card) + if _card.ability.set == 'Voucher' and G.shop_vouchers then + local center = _card.config.center + G.shop_vouchers.config.card_limit = G.shop_vouchers.config.card_limit + 1 + local card = Card(G.shop_vouchers.T.x + G.shop_vouchers.T.w / 2, G.shop_vouchers.T.y, G.CARD_W, G.CARD_H, + G.P_CARDS.empty, center, { + bypass_discovery_center = true, + bypass_discovery_ui = true + }) + create_shop_card_ui(card, 'Voucher', G.shop_vouchers) + G.shop_vouchers:emplace(card) + + end + if _card.ability.set == 'Booster' and G.shop_booster then + local center = _card.config.center + G.shop_booster.config.card_limit = G.shop_booster.config.card_limit + 1 + local card = Card(G.shop_booster.T.x + G.shop_booster.T.w / 2, G.shop_booster.T.y, G.CARD_W * 1.27, + G.CARD_H * 1.27, G.P_CARDS.empty, center, { + bypass_discovery_center = true, + bypass_discovery_ui = true + }) + + create_shop_card_ui(card, 'Booster', G.shop_booster) + card.ability.booster_pos = G.shop_booster.config.card_limit + G.shop_booster:emplace(card) + + end + +end + +function global.isOkayToHandleDebugForKey(key) + if not require("debugplus.config").getValue("ctrlKeybinds") then return true end + for k,v in ipairs({"tab", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}) do -- Keys that ignore the ctrl option (tab menu + collection keys + save state keys) + if key == v then return true end + end + if util.isCtrlDown() then return true end +end + +return global diff --git a/DebugPlus/docs/changelog.md b/DebugPlus/docs/changelog.md new file mode 100644 index 0000000..e883bad --- /dev/null +++ b/DebugPlus/docs/changelog.md @@ -0,0 +1,74 @@ +# DebugPlus 1.3.0 + +## Breaking Changes +- The watch joker command has been changed to watch center. It now works for any type of center, instead of just jokers. + +## Features +- Some tweaks the the table stringifier (what's used when you eval a table) + - Now no longer shows all values from all tables for 2 levels. Instead it shows all values from the top level table, and then shows only a subset of values from lower tables (ends at 3 levels) + - Tables with custom tostring functions are stringified instead of expanded (for example talisman numbers) +- New option to automatically expand printed tables using the table stringifier. +- New option to process strings before printing to the lovely console. + - This allows the expanding tables to also be printed and also works around some weird lovely bugs. +- Experimental option to make the console accessible from the crash screen. + - Toggleable in the config. Doesn't work in vanilla. +- Key repeat is now on in the console (allows you to hold keys like backspace). +- Split config into multiple tabs + + +# DebugPlus 1.2.0 + +Another minor release. This one was mostly just some adjustments to existing stuff, to work a bit better. + +## Features +- The `c` and `r` keybinds now work on tags. +- Whitespace before and after commands are now ignored when run. + +## Fixes +- Fix an issue with the script for making release zips, where the steamodded metadata was not included. (The release zip for v1.1.0 was recreated with this change applied). + +# DebugPlus 1.1.0 + +This is a minor release for DebugPlus, that adds some minor features and internal tweaks. + +## Features +- Copying a playing card now places it in the deck if your hand is empty. +- Internal changes to the logger +- Detect nested folders (when smods is loaded) and provide a useful error message +- Change to using json metadata for Steamodded (this shouldn't have any user facing effect). + +# DebugPlus 1.0.0 + +I've been cooking this for a while but now it's ready for general consumption. +DebugPlus v1.0.0 is a major change for the master branch, which has been mostly +unchanged for 5 months. + +## Breaking Changes + +> [!IMPORTANT] +> There's a high likelihood that you will be impacted by at least one of these. Make sure you read through this section. + +- The minimum required [lovely](https://github.com/ethangreen-dev/lovely-injector) version has been increased. + - v0.5.0-beta7 is the new minimum, but v0.6.0 is recommended. You can grab the latest lovely at https://github.com/ethangreen-dev/lovely-injector/releases. +- By default, most debug keybinds require you hold CTRL to use them. + - You may change this in the config. If Steamodded is installed, then go to Mods > DebugPlus > Config, otherwise go to Settings > DebugPlus to access the config. +- The layout of branches has changed a bit, if you are getting the code from the source. + - As of now, the master branch will be where main development goes. A new stable branch has been created if you just want the latest release. + - You can also now download the latest version from [GitHub Releases](https://github.com/WilsontheWolf/DebugPlus/releases). + +## Features +This build contains a bunch of new features, listed below: +- Improvements to the console + - Log levels + - Better support for logs with many lines + - Config options to manage which logs you see + - Scrolling + - If you're looking to use these additional log details in your mod, see the [developer docs](https://github.com/WilsontheWolf/DebugPlus/blob/master/docs/dev.md) for more info +- A command handler + - You can now run numerous commands to modify aspects of the game + - Accessible by opening the console (pressing `/`) + - Run the `help` command to see all the commands + - There is also an api for mods to add their own commands. See the [developer docs](https://github.com/WilsontheWolf/DebugPlus/blob/master/docs/dev.md) for more info +- Configuration + - Added some configuration options to control different aspects of the game. + - If steamodded is installed, then go to Mods > DebugPlus > Config, otherwise go to Settings > DebugPlus to access the config. diff --git a/DebugPlus/docs/dev.md b/DebugPlus/docs/dev.md new file mode 100644 index 0000000..ef0dbf4 --- /dev/null +++ b/DebugPlus/docs/dev.md @@ -0,0 +1,172 @@ +# Developers + +DebugPlus provides many features that are really only useful for people developing balatro mods. This page covers those features for devs, and is split up into 2 sections: + +1. [Integrating Other Mods Into DebugPlus](#integrating-other-mods-into-debugplus) +2. [Using DebugPlus To Help Develop Over Mods](#using-debugplus-to-help-develop-over-mods) + +## Integrating Other Mods Into DebugPlus + +DebugPlus provides a public api, which allows mods to integrate into DebugPlus, providing devs and users a nicer experince with other mods. This section outlines how to use these features. + +### API Basics + +To access the DebugPlus API you can require the `debugplus-api` module. From there, you will want to call `isVersionCompatible` with the version you expect. This allows mods to detect if an incompatible version of DebugPlus is installed, and not crash. If you prefer to just look at some code, see [examples/example.lua](../examples/example.lua). + +This typically looks like: + +```lua +local success, dpAPI = pcall(require, "debugplus-api") + +if success and dpAPI.isVersionCompatible(1) then + -- Do stuff with the api here. +end +``` + +The version is the api version, and does not nessicarily follow the main mod version. The current latest and only version is `1`. + +### Registering an ID + +DebugPlus requires you to register a unique mod "id" to be tied to the things in DebugPlus's api. This is done with the `registerID` method. Assuming you have done the example in [API Basics](#api-basics), you can register a mod like so: + +```lua +local debugplus = dpAPI.registerID("MyMod") +``` + +### Logging + +DebugPlus provides a logger to allow users to log at different log levels, with their mod name appended to logs. + +> [!NOTE] +> If you are writing a [Steamodded](https://github.com/Steamodded/smods) mod, you can use [Steamodded's logging tools](https://github.com/Steamodded/smods/wiki/Logging) to also get these benefits, without relying on DebugPlus. +> +> Additionally, if you don't want any fancy features, just logging with `print` works fine. + +You can get the logger from a [registered mod](#registering-an-id) like so: + +```lua +debugplus.logger +``` + +The logger is an object with the following keys: + +- log - Print an info log (equivalent to logger.info) +- debug = Print an debug log +- info = Print an info log +- warn = Print an warn log +- error = Print an info log + +These methods are equivalent to `print` so you can use them anywhere you call print. This also means you can have a fallback like so: + +```lua +local success, dpAPI = pcall(require, "debugplus-api") + +local logger = { -- Placeholder logger, for when DebugPlus isn't available + log = print, + debug = print, + info = print, + warn = print, + error = print +} + +if success and dpAPI.isVersionCompatible(1) then -- Make sure DebugPlus is available and compatible + local debugplus = dpAPI.registerID("Example") + logger = debugplus.logger -- Provides the logger object +end + +logger.log("Hi") +``` + +### Commands + +DebugPlus provides an API to register commands to be able to run in the console. You can register a command from a [registered mod](#registering-an-id) using `addCommand` like so: + +```lua +debugplus.addCommand({ + name = "test", + shortDesc = "Testing command", + desc = "This command is an example from the docs.", + exec = function (args, rawArgs, dp) + return "Hi Mom" + end +}) +``` + +Here is a brief list of the properties on the object passed to addCommand: + +- name - The name of the command. Can only use lowercase letters, numbers, `-` and `_`. This will be used for running your command. +- shortDesc - A short description of your command. Shown when running `help` without any arguments. +- desc - The full description of your command. It's a good idea to add usage examples in here. Show when running `help` with your command as an argument. +- exec - The function that is run when running your command. + +The arguments passed to exec are: + +- args - A list with arguments passed by the user. How it's parsed is up to DebugPlus, but you can assume that each argument is a separate part of the user input +- rawArgs - A string with the complete text the user provided for your command. Useful for when you want to handle args differently than DebugPlus does. +- dp - A table with a few different methods/values on it to help with commands: + - dp.hovered - The currently hovered ui element (equivalent to `G.CONTROLLER.hovering.target`) + +The return value for exec has three arguments: + +- The message to show the user. This is the only required argument and you will want to always return something here. +- The log level (defaults to INFO). Can be one of DEBUG, INFO, WARN, ERROR. +- colour (defaults to your log level's colour). Is a table with the first arg as red, second as green third as blue. + +When exec is run, DebugPlus will attempt to catch errors in the command. This is for convenience so you don't need to restart the game if there is a little bug in the command, but it's recommended you don't use errors to indicate the user did something wrong, but instead do `return "Useful error message", "ERROR"`. + +## Using DebugPlus To Help Develop Over Mods + +DebugPlus provides a handful of commands dedicated to use when developing mods. Here is some info regarding them: + +### Eval + +DebugPlus provides an eval command. This command will run whatever lua code you put in it, and print out the result. It also handles errors. It's super helpful for when testing out what happens when you do x. Eval has access to the dp object described in [Commands](#commands) for convince. + +### Reloading Atlases + +DebugPlus provides a keybind (ctrl+m by default) to reload the game's atlases. When developing a mod, you can make a change to your atlas, then reload it to see changes in game, without restarting the game. + +### Watch + +Watch is a command that watches for changes in a file, then performs an action on it when it changes. It has a few different sub commands which determines what it does when the file changes. All the subcommands for the types follow the same syntax, `watch ` where the path is a relative path to the file from the Balatro save directory. + +#### lua + +Running `watch lua ` will watch the provided file for changes and eval the code. Useful for when you want to do some testing on larger bits of code. + +> [!WARNING] +> Running this on an existing mod file is likely to cause issues due to side effects. Make sure you design your watched file around it being run multiple times. + +#### config_tab + +Running `watch config_tab ` will watch the provided file for changes and eval the code. The big difference between this and [lua](#lua) is that this will take the returned value, and render it in a config tab, similar to how `SMODS.config_tab` works. + +See [examples/watch_config_tab.lua](../examples/watch_config_tab.lua). + +#### center + +> [!NOTE] +> This watch depends on [Steamodded](https://github.com/Steamodded/smods) (v1.0.0+) to function. + +> [!WARNING] +> This watch command has side effects. Changes to objects will stay until you restart the game. + +Running `watch center ` will watch the provided file for changes and eval the code. The big difference between this and [lua](#lua) is that this will take the returned value, and use it in a similar way to the object passed to the different [`SMODS.Center`](https://github.com/Steamodded/smods/wiki/SMODS.Center)'s. The biggest difference between the SMODS.Center's is that the key needs to be the full key with the `j_` prefix and your mod prefix. + +DebugPlus will update the functions and loc_txt for your joker on the fly (and adds error protecton to the function). This allows you to rapidly iterate on a joker. + +See [examples/watch_joker.lua](../examples/watch_joker.lua) and [examples/watch_consumeable.lua](../examples/watch_consumeable.lua). + +#### shader + +> [!NOTE] +> This watch depends on [Steamodded](https://github.com/Steamodded/smods) (v1.0.0+) to function. + + +> [!WARNING] +> This watch command is fairly unstable. It's recommended to run `watch stop` after you're done with it to prevent side effects and crashes. If the shader code has an issue at runtime, it will crash. + +Running `watch shader ` will watch the provided shader file for changes and show a joker with the shader as an edition. This should just work with any edition shaders used for Steamodded. + +> [!NOTE] +> Due to a quirk with shaders, they have to be passed a variable with their name. DebugPlus uses some heuristics to guess the name. If DebugPlus fails to guess correctly, the shader will crash. If the game crashes with the watch command but not when used with Steamodded on an edition, please make a bug report. diff --git a/DebugPlus/keys.txt b/DebugPlus/keys.txt new file mode 100644 index 0000000..2a87a6c --- /dev/null +++ b/DebugPlus/keys.txt @@ -0,0 +1,32 @@ +Most of these require you hold ctrl to run (can be disabled in the config) + +Built in: +tab: menu +1: unlock card +2: discover card +3: spawn card +q: Switch edition +h: toggle UI +b: new run +l: load run +j: go to menu (might break) +8: toggle mouse visiblity +9: toggle tooltips +space: live_test (Seems to just be a localthunk code tester) +v: profiler (DebugPlus enhances) +p: performance mod (DebugPlus enhances) + +modded: +W: Set enhancement +E: Set seal +A: Toggle eternal +S: Toggle perishable +D: Toggle rental +F: Toggle coupon shop card +Hold z + 1-3: Save state to slot +hold x + 1-3: Load state from slot +R: Destroy Card +C: Duplicate Card +up / down: increase / decrease rank +left / right: cycle suit +M: Reload Atlases diff --git a/DebugPlus/logger.lua b/DebugPlus/logger.lua new file mode 100644 index 0000000..9af9493 --- /dev/null +++ b/DebugPlus/logger.lua @@ -0,0 +1,184 @@ +local global = {} +local logs = nil +local old_print = print +local util = require("debugplus.util") +local levelMeta = { + DEBUG = { + level = 'DEBUG', + colour = {1, 0, 1}, + shouldShow = false, + }, + INFO = { + level = 'INFO', + colour = {0, 1, 1}, + shouldShow = true, + }, + WARN = { + level = 'WARN', + colour = {1, 1, 0}, + shouldShow = true, + }, + ERROR = { + level = 'ERROR', + colour = {1, 0, 0}, + shouldShow = true, + } +} +global.levelMeta = levelMeta +local SMODSLogPattern = "[%d-]+ [%d:]+ :: (%S+) +:: (%S+) :: (.*)" +local SMODSLevelMeta = { + TRACE = levelMeta.DEBUG, + DEBUG = levelMeta.DEBUG, + INFO = levelMeta.INFO, + WARN = levelMeta.WARN, + ERROR = levelMeta.ERROR, + FATAL = levelMeta.ERROR +} + +function global.handleLogAdvanced(data, ...) + local stringifyPrint = require("debugplus.config").getValue("stringifyPrint") + if not stringifyPrint then + old_print(...) + end + local _str = "" + local stringify = tostring + if require("debugplus.config").getValue("processTables") then + stringify = util.stringifyTable + end + for _, v in ipairs({...}) do + _str = _str .. stringify(v) .. " " + end + if stringifyPrint then + old_print(_str) + end + local meta = { + str = _str, + time = love.timer.getTime(), + colour = data.colour, + level = data.level, + command = data.command, + } + if data.fromPrint then + local level, source, msg = string.match(_str, SMODSLogPattern) + if level then + local levelMeta = SMODSLevelMeta[level] or SMODSLevelMeta.INFO + meta = { + str = "[" .. source .. "] " .. msg, + time = love.timer.getTime(), + colour = levelMeta.colour, + level = levelMeta.level + } + else + -- Handling the few times the game itself prints + if _str:match("^LONG DT @ [%d.: ]+$") then -- LONG DT messages + meta.level = "DEBUG" + meta.colour = levelMeta.DEBUG.colour + elseif _str:match("^ERROR LOADING GAME: Card area '[%w%d_-]+' not instantiated before load") then -- Error loading areas + meta.level = "ERROR" + meta.colour = levelMeta.ERROR.colour + elseif _str:match("^\n [+-]+ \n | #") and debug.getinfo(3).short_src == "engine/controller.lua" then -- Profiler results table. Extra check cause I don't trust this pattern to not have false positives + meta.level = "DEBUG" + meta.colour = levelMeta.DEBUG.colour + meta.command = true + end + end + end + if not meta.colour then meta.colour = levelMeta[meta.level].colour end + + -- Dirty hack to work better with multiline text + if string.match(meta.str, "\n") then + local first = true + for w in string.gmatch(meta.str, "[^\n]+") do + local _meta = { + str = w, + time = meta.time, + colour = meta.colour, + level = meta.level, + command = meta.command, + hack_no_prefix = not first + } + first = false + table.insert(logs, _meta) + -- TODO: fix me + if logOffset ~= 0 then + global.handleLogsChange(1) + end + if #logs > 5000 then + table.remove(logs, 1) + end + end + else + table.insert(logs, meta) + global.handleLogsChange(1) + if logOffset ~= 0 then + global.handleLogsChange(1) + end + if #logs > 5000 then + table.remove(logs, 1) + end + end +end + +function global.handleLog(colour, level, ...) + global.handleLogAdvanced({ + colour = colour, + level = level, + command = true, + }, ...) +end + +function global.log(...) + global.handleLogAdvanced({ + colour = {.65, .36, 1}, + level = "INFO", + }, "[DebugPlus]", ...) +end + +function global.logCmd(...) + global.handleLog({.65, .36, 1}, "INFO", "[DebugPlus]", ...) +end + + +function global.errorLog(...) + global.handleLogAdvanced({ + colour = {1, 0, 0}, + level = "ERROR", + }, "[DebugPlus]", ...) +end + +function global.registerLogHandler() + if logs then + return + end + logs = {} + global.logs = logs + print = function(...) + global.handleLogAdvanced({ + colour = {0, 1, 1}, + level = "INFO", + fromPrint = true, + }, ...) + end +end + +function global.handleLogsChange() -- Placeholder. Overwritten in console.lua +end + + +-- config.configDefinition.logLevel.onUpdate = function(v) +-- for k, v in pairs(levelMeta) do +-- v.shouldShow = false +-- end + +-- levelMeta.ERROR.shouldShow = true +-- if v == "ERROR" then return end +-- levelMeta.WARN.shouldShow = true +-- if v == "WARN" then return end +-- levelMeta.INFO.shouldShow = true +-- if v == "INFO" then return end +-- levelMeta.DEBUG.shouldShow = true +-- end + +-- config.configDefinition.logLevel.onUpdate(config.getValue("logLevel")) + +return global diff --git a/DebugPlus/lovely/console.toml b/DebugPlus/lovely/console.toml new file mode 100644 index 0000000..0058bc3 --- /dev/null +++ b/DebugPlus/lovely/console.toml @@ -0,0 +1,48 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + + +# Console Stuff: + +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = 'require "engine/object"' +position = "before" +payload = ''' +do + local logger = require("debugplus.logger") + logger.registerLogHandler() +end +''' +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = 'G:draw()' +position = "after" +payload = ''' +do + local console = require("debugplus.console") + console.doConsoleRender() + timer_checkpoint('DebugPlus Console', 'draw') +end +''' +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = 'function love.keypressed(key)' +position = "after" +payload = ''' +local console = require("debugplus.console") +if not console.consoleHandleKey(key) then return end +''' +match_indent = true +overwrite = false diff --git a/DebugPlus/lovely/debug-enhancements.toml b/DebugPlus/lovely/debug-enhancements.toml new file mode 100644 index 0000000..98e6d88 --- /dev/null +++ b/DebugPlus/lovely/debug-enhancements.toml @@ -0,0 +1,189 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Core +[[patches]] +[patches.pattern] +target = "conf.lua" +pattern = "_RELEASE_MODE = true" +position = "at" +payload = '_RELEASE_MODE = false' +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if self.real_dt > 0.05 then print('LONG DT @ '..math.floor(G.TIMERS.REAL)..': '..self.real_dt) end" +position = "at" +payload = "if require('debugplus.config').getValue('enableLongDT') and self.real_dt > 0.05 then print('LONG DT @ '..math.floor(G.TIMERS.REAL)..': '..self.real_dt) end" +match_indent = true +overwrite = false + +# Note this patch provides the debugplus variable for the profile and perf patches +[[patches]] +[patches.pattern] +target = "engine/controller.lua" +pattern = "if key == 'v' then" +position = "before" +payload = ''' +local debugplus = require("debugplus.core") +debugplus.handleKeys(self, key, dt) +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "engine/controller.lua" +pattern = 'if _card.ability.consumeable and G.consumeables and #G.consumeables.cards < G.consumeables.config.card_limit then' +position = "before" +payload = ''' +local debugplus = require("debugplus.core") +debugplus.handleSpawn(self, _card) +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = 'function create_UIBox_debug_tools()' +position = "after" +payload = ''' +local debugplus = require("debugplus.core") +debugplus.registerButtons() +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = 'UIBox_button{ label = {"Background"}, button = "DT_toggle_background", minw = 1.7, minh = 0.4, scale = 0.35},' +position = "after" +payload = ''' +UIBox_button{ label = {"Win Blind"}, button = "DT_win_blind", minw = 1.7, minh = 0.4, scale = 0.35}, +UIBox_button{ label = {"Double Tag"}, button = "DT_double_tag", minw = 1.7, minh = 0.4, scale = 0.35}, +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '{n=G.UIT.T, config={text = "and press [Q] to cycle Edition", scale = 0.25, colour = G.C.WHITE, shadow = true}}' +position = "at" +# This patch is super jank +# when lovely supports regex, move the save state to it's own block. +payload = ''' + {n=G.UIT.T, config={text = "hold [" .. require("debugplus.util").ctrlText .. "] (togglable in config)", scale = 0.25, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "and press [Q] to cycle Edition", scale = 0.25, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [W] to cycle Enhancement", scale = 0.25, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [E] to cycle Seal", scale = 0.25, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [A/S/D] to toggle Eternal/Perishable/Rental", scale = 0.2, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [F] to toggle Coupon (make free)", scale = 0.25, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [R/C] to destroy/copy card", scale = 0.25, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [M] to reload atlases", scale = 0.25, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [UP/DOWN] to cycle rank", scale = 0.2, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.00}, nodes={ + {n=G.UIT.T, config={text = "press [RIGHT/LEFT] to cycle suit", scale = 0.2, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [z] plus [1-3] to save a save state", scale = 0.2, colour = G.C.WHITE, shadow = true}} +}}, +{n=G.UIT.R, config={align = "cm", padding = 0.00}, nodes={ + {n=G.UIT.T, config={text = "press [x] plus [1-3] to load a save state", scale = 0.2, colour = G.C.WHITE, shadow = true}} +''' +match_indent = true +overwrite = true + +[[patches]] # Readds the debug info to v1.0.1g+ / show hud config +[patches.pattern] +target = "game.lua" +pattern = 'if not _RELEASE_MODE and G.DEBUG and not G.video_control and G.F_VERBOSE then' +position = "at" +payload = '''if require("debugplus.config").getValue("showHUD") and not G.video_control and G.F_VERBOSE then''' +match_indent = true +overwrite = false + +# +## Better vanilla debugs +# + +[[patches]] +[patches.pattern] +target = "game.lua" +match_indent = true +pattern = 'love.graphics.print("Current FPS: "..fps, 10, 10)' +position = "at" +payload = ''' +do + local otherSize = 0 + for k,v in pairs(G.E_MANAGER.queues or {}) do + if k ~= 'base' then + otherSize = otherSize + #v + end + end + if otherSize ~= 0 then + love.graphics.print(string.format("Current FPS: %d\nBase event queue: %d\nOther event queues: %d", fps, #(G.E_MANAGER.queues and G.E_MANAGER.queues.base or {}), otherSize), 10, 10) + else + love.graphics.print(string.format("Current FPS: %d\nBase event queue: %d", fps, #(G.E_MANAGER.queues and G.E_MANAGER.queues.base or {})), 10, 10) + end +end +''' + +# Performance profiler enabled by pressing v, prints results to console. +[[patches]] +[patches.pattern] +target = "engine/controller.lua" +pattern = '''print(G.prof.report()); G.prof = nil end''' +position = "after" +payload = ''' +debugplus.profileMessage() +''' +match_indent = true + +# Profiler overlay enabled by pressing p +[[patches]] +[patches.pattern] +target = "engine/controller.lua" +pattern = '''G.SETTINGS.perf_mode = not G.SETTINGS.perf_mode''' +position = "after" +payload = ''' +debugplus.togglePerfUI() +''' +match_indent = true + +# Makes the text in the profiler not get obscured +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''love.graphics.setColor(a == 2 and 0.5 or 1, a == 2 and 1 or 0.5, 1,1)''' +position = "before" +payload = ''' +v_off = v_off + section_h +end +local v_off = v_off - section_h * #b.checkpoint_list +for k, v in ipairs(b.checkpoint_list) do +''' +match_indent = true diff --git a/DebugPlus/lovely/misc.toml b/DebugPlus/lovely/misc.toml new file mode 100644 index 0000000..3f75cb2 --- /dev/null +++ b/DebugPlus/lovely/misc.toml @@ -0,0 +1,29 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 1 # Needed to not break SMODS + +# Handles the ctrl keybind check +[[patches]] +[patches.pattern] +target = "engine/controller.lua" +pattern = '''if not _RELEASE_MODE then''' +position = "at" +payload = '''if not _RELEASE_MODE and require("debugplus.core").isOkayToHandleDebugForKey(key) then''' +match_indent = true + +# Add the config tab to the settings (for when SMODS isn't present) +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local t = create_UIBox_generic_options({back_func = 'options',contents = {create_tabs(''' +position = "before" +payload = ''' +if not require("debugplus.config").SMODSLoaded then + tabs[#tabs+1] = { + label = "DebugPlus", + tab_definition_function = require("debugplus.config").fakeConfigTab, + } +end +''' +match_indent = true diff --git a/DebugPlus/lovely/modules.toml b/DebugPlus/lovely/modules.toml new file mode 100644 index 0000000..5901321 --- /dev/null +++ b/DebugPlus/lovely/modules.toml @@ -0,0 +1,47 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Modules +[[patches]] +[patches.module] +source = "util.lua" +before = "main.lua" +name = "debugplus.util" + +[[patches]] +[patches.module] +source = "config.lua" +before = "main.lua" +name = "debugplus.config" + +[[patches]] +[patches.module] +source = "watcher.lua" +before = "main.lua" +name = "debugplus.watcher" + +[[patches]] +[patches.module] +source = "logger.lua" +before = "main.lua" +name = "debugplus.logger" + +[[patches]] +[patches.module] +source = "console.lua" +before = "main.lua" +name = "debugplus.console" + +[[patches]] +[patches.module] +source = "api.lua" +before = "main.lua" +name = "debugplus-api" + +[[patches]] +[patches.module] +source = "core.lua" +before = "engine/controller.lua" +name = "debugplus.core" diff --git a/DebugPlus/smods.json b/DebugPlus/smods.json new file mode 100644 index 0000000..f4d0f17 --- /dev/null +++ b/DebugPlus/smods.json @@ -0,0 +1,10 @@ +{ + "id": "DebugPlus", + "name": "DebugPlus", + "display_name": "", + "author": ["WilsontheWolf"], + "description": "Better Debug Tools for Balatro ", + "prefix": "DebugPlus", + "main_file": "smods.lua", + "version": "1.3.0" +} diff --git a/DebugPlus/smods.lua b/DebugPlus/smods.lua new file mode 100644 index 0000000..757143b --- /dev/null +++ b/DebugPlus/smods.lua @@ -0,0 +1,24 @@ +-- Steamodded is not necessary. This just adds a bit of compatibility. + +if SMODS.Atlas then + SMODS.Atlas({ + key = "modicon", + path = "modicon.png", + px = 32, + py = 32 + }) +end + +if SMODS.current_mod then + local configSuccess, config = pcall(require, "debugplus.config") + + if not configSuccess then + error("DebugPlus modules not successfully initialized.\nMake sure your DebugPlus folder is not nested (there should be a bunch of files in the DebugPlus folder and not just another folder).\n\n" .. (config or "No further info.")) + end + SMODS.current_mod.config_tab = true + SMODS.current_mod.extra_tabs = config.generateConfigTabs + config.SMODSLoaded = true + + function SMODS.current_mod.load_mod_config() end + function SMODS.current_mod.save_mod_config() end +end diff --git a/DebugPlus/util.lua b/DebugPlus/util.lua new file mode 100644 index 0000000..ee7a9e6 --- /dev/null +++ b/DebugPlus/util.lua @@ -0,0 +1,70 @@ +local global = {} +local isMac = love.system.getOS() == 'OS X' +global.ctrlText = isMac and "CMD" or "CTRL" + +function global.stringifyTable(tab, depth, num, dec, indent) + if not indent then + indent = "" + end + if not depth then + depth = 4 + end + if not num then + num = math.huge + end + if not dec then + dec = 3 + end + if depth == 0 or num <= 0 then + return tostring(tab) + end + if type(tab) ~= "table" then + return tostring(tab) + end + if (getmetatable(tab) or {}).__tostring then -- For tables with custom tostring values (such as a talisman number) + return tostring(tab) .. "hi" + end + local res = "Table:\n" + local count = 0 + for k, v in pairs(tab) do + count = count + 1 + if count < num + 1 then + res = res .. indent .. tostring(k) .. ": " .. global.stringifyTable(v, depth - 1, math.max(1, (num == math.huge and 10 or num) - dec), dec, indent .. " ") .. "\n" + end + end + if count > num then + local c = count - num + res = res .. indent .. "+" .. tostring(c) .. " more value" .. (c == 1 and "" or "s") .. ".\n" + end + return res +end + +function global.hasValue(tab, val) + for index, value in ipairs(tab) do + if value == val then + return index + end + end + + return false +end + +function global.isShiftDown() + return love.keyboard.isDown('lshift') or love.keyboard.isDown('rshift') +end + +if isMac then + function global.isCtrlDown() + return love.keyboard.isDown('lgui') or love.keyboard.isDown('rgui') + end +else + function global.isCtrlDown() + return love.keyboard.isDown('lctrl') or love.keyboard.isDown('rctrl') + end +end + +function global.trim(string) + return string:match("^%s*(.-)%s*$") +end + +return global diff --git a/DebugPlus/watcher.lua b/DebugPlus/watcher.lua new file mode 100644 index 0000000..bdd9ed9 --- /dev/null +++ b/DebugPlus/watcher.lua @@ -0,0 +1,320 @@ +local logger = require("debugplus.logger") +local modtime +local global = {} +local event +local file +local running = false +local currentType +-- For edition type +local editionIndex + +local function genSafeFunc(name, fn) + return function(...) + if not fn or type(fn) ~= "function" then + return + end + local res = {pcall(fn, ...)} + local succ = table.remove(res, 1) + if not succ then + logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Center function \"" .. name .. "\" errored:", unpack(res)) + return + end + return unpack(res) + end +end + + +local function evalLuaFile(content) + local fn, err = load(content, "@" .. file) + + if not fn then + logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Error Loading File:", err) + return false + end + local succ, err = pcall(fn) + if not succ then + logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Error Running File:", err) + return false + end + return true, err +end + +local function showTabOverlay(definition, tabName) + tabName = tabName or "Tab" + return G.FUNCS.overlay_menu({ + definition = create_UIBox_generic_options({ + contents = {{ + n = G.UIT.R, + nodes = {create_tabs({ + snap_to_nav = true, + colour = G.C.BOOSTER, + tabs = {{ + label = tabName, + chosen = true, + tab_definition_function = function() + return definition + end + }} + })} + }} + }) + }) + +end + +local types = { + lua = { + desc = "Starts watching the lua file provided.", + run = function(content) + return evalLuaFile(content) + end, + }, + config_tab = { + desc = "Starts watching the lua file provided. The returned value is rendered like a config tab (such as the one in SMODS.current_mod.config_tab). Note that invalid tabs will likely crash the game.", + run = function(content) + local success, res = evalLuaFile(content) + if not success then return false end + if type(res) ~= "table" or next(res) == nil then + logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Config tab doesn't look valid. Not rendering to prevent a crash. Make sure you're returning something.") + return + end + showTabOverlay(res) + end, + }, + shader = { + desc = "Starts watching the the shader file provided. Pops up a ui with a joker to preview the shader on.", + check = function() + if SMODS and SMODS.Shaders and SMODS.Edition then + return true + end + return false, "Steamodded (v1.0.0~+) is necessary to watch shader files." + end, + run = function(content) + local result, shader = pcall(love.graphics.newShader, content) + if not result then + return logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Error Loading Shader:", shader) + end + local name = content:match("extern [%w_]+ vec2 (%w+);"); + if not name then + return logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Could not guess name of shader :/. Not applying to avoid crash.") + end + + G.SHADERS.debugplus_watcher_shader = shader + SMODS.Shaders.debugplus_watcher_shader = { + original_key = name + } + if not editionIndex then + editionIndex = #G.P_CENTER_POOLS.Edition + 1 + end + G.P_CENTER_POOLS.Edition[editionIndex] = { + key = "e_debugplus_watcher_edition", + shader = "debugplus_watcher_shader" + } + + -- Make an area with a joker with our editon + local area = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + G.CARD_W, + G.CARD_H, + {card_limit = 5, type = 'title', highlight_limit = 0, deck_height = 0.75, thin_draw = 1} + ) + local card = Card(area.T.x + area.T.w/2, area.T.y, G.CARD_W, G.CARD_H, + nil, G.P_CENTERS["j_joker"]) + card.edition = {debugplus_watcher_edition = true} + area:emplace(card) + + showTabOverlay({ + -- ROOT NODE + n = G.UIT.ROOT, + config = { + r = 0.1, + minw = 7, + minh = 5, + align = "tm", + padding = 1, + colour = G.C.BLACK + }, + nodes = {{ + n = G.UIT.R, + config = { + align = "cm", + padding = 0.07, + no_fill = true, + scale = 1 + }, + nodes = {{ + n = G.UIT.O, + config = { + object = area + } + }} + }} + }, "Shader Test") + + return true + end, + cleanup = function() + table.remove(G.P_CENTER_POOLS.Edition, editionIndex) + G.SHADERS.debugplus_watcher_shader = nil + SMODS.Shaders.debugplus_watcher_shader = nil + end + }, + center = { + desc = "Starts watching the lua file provided. The returned table is used to modify the center given in the key value. The table is similar to SMODS.Joker and friends.", + check = function() -- Not entirely sure what to all check for here. + if SMODS and SMODS.Joker then + return true + end + return false, "Steamodded (v1.0.0~+) is necessary to watch centers." + end, + run = function(content) + local success, res = evalLuaFile(content) + if not success then return false end + if not res or type(res) ~= "table" then + logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Center config doesn't look correct. Make sure you are returning an object.") + return + end + if not res.key then + logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Center config is missing a key.") + return + end + local center = G.P_CENTERS[res.key] + if not center then + logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] The key \"" .. res.key .. "\" does not exist. Make sure your object has been loaded and the key is correct (don't forget the object prefix (e.g. j_) and your mod prefix) you can get the key by hovering over your object and then running `eval dp.hovered.config.center.key`.") + return + end + if res.loc_txt then + local loc_txt = res.loc_txt + local loc = G.localization.descriptions[center.set][res.key] + local loc_changed = false + + if loc_txt.name then + if loc_txt.name ~= loc.name then + loc_changed = true + loc.name = loc_txt.name + end + end + + if loc_txt.text then + if #loc_txt.text ~= #loc.text then + loc_changed = true + else + for k, v in ipairs(loc_txt.text) do + if v ~= loc.text[k] then + loc_changed = true + break + end + end + end + loc.text = loc_txt.text + end + + if loc_changed then + init_localization() + end + end + + if res.pos then + center.pos.x = res.pos.x + center.pos.y = res.pos.y + end + + for k,v in pairs(res) do + if type(v) ~= "function" then + goto finishfunc + end + center[k] = genSafeFunc(k, v) + ::finishfunc:: + end + return true + end, + } +} + +local function loadFile() + local info = love.filesystem.getInfo(file) + local showReloaded = modtime ~= nil + if info.modtime == modtime then + return + end + modtime = info.modtime + local content = love.filesystem.read(file) + local result, subResult = pcall(currentType.run, content) + if not result then + return logger.handleLog({1, 0, 0}, "ERROR", "[Watcher] Error Running Watcher:", subResult) + end + if showReloaded and subResult then + logger.handleLog({0, 1, 0}, "INFO", "[Watcher] Reloaded") + end + return true +end + +local function makeEvent() + event = Event { + blockable = false, + blocking = false, + pause_force = true, + no_delete = true, + trigger = "after", + delay = .5, + func = function() + if not running then + return true + end + loadFile() + event.start_timer = false + end + } +end + +function global.startWatching(_file, _type) + if not _file then + return nil, "No file" + end + local info = love.filesystem.getInfo(_file) + if not info then + return nil, "File doesn't exist" + end + if not (info.type == "file") then + return nil, "Not a regular file" + end + if not event then makeEvent() end + if running and currentType and currentType.cleanup and type(currentType.cleanup) == "function" then + currentType.cleanup() + end + modtime = nil + file = _file + currentType = types[_type] + if not currentType then + return nil, "Shit's erroring (no type)" + end + if currentType.check and type(currentType.check) == "function" then + local res, msg = currentType.check(); + if not res then + return nil, msg or "Pre-check failed!" + end + end + if not running then + running = true + loadFile() + G.E_MANAGER:add_event(event) + end + return true +end + +function global.stopWatching() + running = false + if currentType.cleanup and type(currentType.cleanup) == "function" then + currentType.cleanup() + end +end + +global.types = types; + +global.subCommandDesc = "" + +for k,v in pairs(types) do + global.subCommandDesc = global.subCommandDesc .. "watch " .. k .. " [file] - " .. (v.desc or "Wilson forgot to make a description for me.") .. "\n" +end + +return global diff --git a/HandyBalatro/README.md b/HandyBalatro/README.md new file mode 100644 index 0000000..c6e4b89 --- /dev/null +++ b/HandyBalatro/README.md @@ -0,0 +1,41 @@ +# Handy - Balatro QoL controls mod + +It's a small [Lovely](https://github.com/ethangreen-dev/lovely-injector) mod that adds additional controls to Balatro. + +## New Controls +- `Shift + LMB` on card to: + - Buy joker, card, booster pack, voucher or consumable in shop + - Select joker or card in booster pack + - Sell joker in joker slots + - Sell consumable in consumable slots + +https://github.com/user-attachments/assets/6406309f-f629-41c5-851d-90f99cb35cfa + +- `Ctrl + LMB` on card to: + - Use consumable *(if possible)* in shop, booster pack or consumable slots + +https://github.com/user-attachments/assets/7ed89be3-a362-42c0-ac86-ccbc8fded62a + +- `LMB` to select a card and then `Left/Right arrow` to: + - Move highlight in direction between jokers or consumables + - Hold `Shift` to move card instead + - Hold `Ctrl` to move to the first/last card + +https://github.com/user-attachments/assets/2d40e4a6-9ea1-4ffa-a7fd-e0e8b9856b2b + +- Hold `LMB` and move to: + - Select cards in hand + +https://github.com/user-attachments/assets/9ec9dfba-a7df-4ecf-a3e0-67dc802c310d + +- `Enter` to: + - Skip "Cash Out" stage + +https://github.com/user-attachments/assets/85082445-052a-4f96-aaf0-51e1af1fdaf3 + +## Dangerous controls +- Hold `Shift + MMB (Mouse 3, Wheel Button)` and move to: + - Mass-sell jokers or consumables + +https://github.com/user-attachments/assets/4c958f09-74c3-4c11-88ab-48989c41dfde + diff --git a/HandyBalatro/assets/1x/icon.png b/HandyBalatro/assets/1x/icon.png new file mode 100644 index 0000000..2c61de5 Binary files /dev/null and b/HandyBalatro/assets/1x/icon.png differ diff --git a/HandyBalatro/assets/2x/icon.png b/HandyBalatro/assets/2x/icon.png new file mode 100644 index 0000000..7cafe16 Binary files /dev/null and b/HandyBalatro/assets/2x/icon.png differ diff --git a/HandyBalatro/config_ui.lua b/HandyBalatro/config_ui.lua new file mode 100644 index 0000000..d4b5bbc --- /dev/null +++ b/HandyBalatro/config_ui.lua @@ -0,0 +1,551 @@ +Handy.UI.PARTS = { + create_module_checkbox = function(module, label, text_prefix, text_lines, skip_keybinds) + local desc_lines = { + { n = G.UIT.R, config = { minw = 5.25 } }, + } + + if skip_keybinds then + table.insert(desc_lines, { + n = G.UIT.R, + config = { padding = 0.025 }, + nodes = { + { + n = G.UIT.T, + config = { + text = text_prefix .. " " .. text_lines[1], + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + }, + }) + else + local key_desc = module.key_2 + and { + { + n = G.UIT.T, + config = { + text = text_prefix .. " [", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + ref_table = module, + ref_value = "key_1", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + text = "] or [", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + ref_table = module, + ref_value = "key_2", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + text = "] " .. text_lines[1], + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + } + or { + { + n = G.UIT.T, + config = { + text = text_prefix .. " [", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + ref_table = module, + ref_value = "key_1", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + text = "] " .. text_lines[1], + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + } + table.insert(desc_lines, { + n = G.UIT.R, + config = { padding = 0.025 }, + nodes = key_desc, + }) + end + + for i = 2, #text_lines do + table.insert(desc_lines, { + n = G.UIT.R, + config = { padding = 0.025 }, + nodes = { + { + n = G.UIT.T, + config = { + text = text_lines[i], + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + }, + }) + end + + local label_lines = {} + if type(label) == "string" then + label = { label } + end + for i = 1, #label do + table.insert(label_lines, { + n = G.UIT.R, + config = { minw = 2.75 }, + nodes = { + { + n = G.UIT.T, + config = { + text = label[i], + scale = 0.4, + colour = G.C.WHITE, + }, + }, + }, + }) + end + + return { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + { + n = G.UIT.C, + config = { align = "cm" }, + nodes = label_lines, + }, + { + n = G.UIT.C, + config = { align = "cm" }, + nodes = { + create_toggle({ + callback = function(b) + return G.FUNCS.handy_toggle_module_enabled(b, module) + end, + label_scale = 0.4, + label = "", + ref_table = module, + ref_value = "enabled", + w = 0, + }), + }, + }, + { + n = G.UIT.C, + config = { minw = 0.1 }, + }, + { + n = G.UIT.C, + config = { align = "cm" }, + nodes = desc_lines, + }, + }, + } + end, + + create_module_section = function(label) + return { + n = G.UIT.R, + config = { align = "cm", padding = 0.1 }, + nodes = { + { + n = G.UIT.T, + config = { text = label, colour = G.C.WHITE, scale = 0.4, align = "cm" }, + }, + }, + } + end, + create_module_keybind = function(module, label, plus, dangerous) + return { + n = G.UIT.R, + config = { align = "cm", padding = 0.05 }, + nodes = { + { + n = G.UIT.C, + config = { align = "c", minw = 4 }, + nodes = { + { + n = G.UIT.T, + config = { text = label, colour = G.C.WHITE, scale = 0.35 }, + }, + }, + }, + { + n = G.UIT.C, + config = { align = "cm", minw = 0.75 }, + }, + UIBox_button({ + label = { module.key_1 or "None" }, + col = true, + colour = dangerous and G.C.MULT or G.C.CHIPS, + scale = 0.35, + minw = 2.75, + minh = 0.45, + ref_table = { + module = module, + key = "key_1", + }, + button = "handy_init_keybind_change", + }), + { + n = G.UIT.C, + config = { align = "cm", minw = 0.6 }, + nodes = { + { + n = G.UIT.T, + config = { text = plus and "+" or "or", colour = G.C.WHITE, scale = 0.3 }, + }, + }, + }, + UIBox_button({ + label = { module.key_2 or "None" }, + col = true, + colour = dangerous and G.C.MULT or G.C.CHIPS, + scale = 0.35, + minw = 2.75, + minh = 0.45, + ref_table = { + module = module, + key = "key_2", + }, + button = "handy_init_keybind_change", + }), + }, + } + end, +} + +Handy.UI.get_config_tab_overall = function() + return { + { + n = G.UIT.R, + config = { padding = 0.05, align = "cm" }, + nodes = { + create_option_cycle({ + minw = 3, + label = "Notifications level", + scale = 0.8, + options = { + "None", + "Dangerous", + "Game state", + "All", + }, + opt_callback = "handy_change_notifications_level", + current_option = Handy.config.current.notifications_level, + }), + }, + }, + { n = G.UIT.R, config = { padding = 0.05 }, nodes = {} }, + { + n = G.UIT.R, + nodes = { + { + n = G.UIT.C, + nodes = { + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.insta_highlight, + "Quick Highlight", + "Hold [Left Mouse]", + { + "and", + "hover cards in hand to highlight", + }, + true + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.insta_buy_or_sell, + "Quick Buy/Sell", + "Hold", + { + "to", + "buy or sell card on Left-Click", + "instead of selection", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox(Handy.config.current.insta_use, "Quick use", "Hold", { + "to", + "use (if possible) card on Left-Click", + "instead of selection", + "(overrides Quick Buy/Sell)", + }), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.move_highlight, + "Move highlight", + "Press", + { + "[" + .. tostring(Handy.config.current.move_highlight.dx.one_left.key_1) + .. "] or [" + .. tostring(Handy.config.current.move_highlight.dx.one_right.key_1) + .. "]", + "to move highlight in card area.", + "Hold [" + .. tostring(Handy.config.current.move_highlight.swap.key_1) + .. "] to move card instead.", + "Hold [" + .. tostring(Handy.config.current.move_highlight.to_end.key_1) + .. "] to move to first/last card", + }, + true + ), + }, + }, + { + n = G.UIT.C, + config = { minw = 4 }, + nodes = { + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.insta_cash_out, + "Quick Cash Out", + "Press", + { + "to", + "speedup animation and", + "skip Cash Out stage", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.insta_booster_skip, + { "Quick skip", "Booster Packs" }, + "Hold", + { + "to", + "skip booster pack", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.speed_multiplier, + "Speed Multiplier", + "Hold", + { + "and", + "[Wheel Up] to multiply or", + "[Wheel Down] to divide game speed", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.shop_reroll, + "Shop Reroll", + "Press", + { + "to", + "reroll a shop", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.play_and_discard, + "Play/Discard", + "Press", + { + "[" .. tostring(Handy.config.current.play_and_discard.play.key_1) .. "] to play a hand", + "or [" + .. tostring(Handy.config.current.play_and_discard.discard.key_1) + .. "] to discard", + }, + true + ), + }, + }, + }, + }, + } +end + +Handy.UI.get_config_tab_interactions = function() + return { + { + n = G.UIT.R, + nodes = { + { + n = G.UIT.C, + nodes = { + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.nopeus_interaction, + { "Nopeus:", "fast-forward" }, + "Hold", + { + "and", + "[Wheel Up] to increase or", + "[Wheel Down] to decrease", + "fast-forward setting", + } + ), + { + n = G.UIT.R, + config = { minh = 0.25 }, + }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.not_just_yet_interaction, + { "NotJustYet:", "End round" }, + "Press", + { + "to", + "end round", + } + ), + }, + }, + }, + }, + } +end + +Handy.UI.get_config_tab_dangerous = function() + return { + -- { + -- n = G.UIT.R, + -- config = { padding = 0.05, align = "cm" }, + -- nodes = { + + -- }, + -- }, + -- { n = G.UIT.R, config = { padding = 0.05 }, nodes = {} }, + { + n = G.UIT.R, + nodes = { + { + n = G.UIT.C, + nodes = { + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.dangerous_actions, + { "Dangerous", "actions" }, + "Enable", + { + "unsafe controls. They're", + "designed to be speed-first,", + "which can cause bugs or crashes", + }, + true + ), + { n = G.UIT.R, config = { minh = 0.5 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.dangerous_actions.immediate_buy_and_sell, + "Instant Sell", + "Hold", + { + "to", + "sell card on hover", + "very fast", + } + ), + { n = G.UIT.R, config = { minh = 0.1 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.dangerous_actions.immediate_buy_and_sell.queue, + "Sell Queue", + "Start", + { + "selling cards only when", + "keybind was released", + }, + true + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.dangerous_actions.nopeus_unsafe, + { "Nopeus: Unsafe", "fast-forward" }, + "Allow", + { + "increase fast-forward", + 'setting to "Unsafe"', + }, + true + ), + }, + }, + }, + }, + } +end + +Handy.UI.get_config_tab_keybinds = function() + return { + Handy.UI.PARTS.create_module_section("Quick Actions"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.insta_buy_or_sell, "Quick Buy/Sell"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.insta_use, "Quick Use"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.insta_cash_out, "Quick Cash Out"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.insta_booster_skip, "Quick skip Booster Packs"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.shop_reroll, "Shop reroll"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.play_and_discard.play, "Play hand"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.play_and_discard.discard, "Discard"), + Handy.UI.PARTS.create_module_keybind( + Handy.config.current.dangerous_actions.immediate_buy_and_sell, + "Instant Buy/Sell", + false, + true + ), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.not_just_yet_interaction, "NotJustYet: End round"), + } +end + +Handy.UI.get_config_tab_keybinds_2 = function() + return { + Handy.UI.PARTS.create_module_section("Game state"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.speed_multiplier, "Speed Multiplier"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.nopeus_interaction, "Nopeus: fast-forward"), + Handy.UI.PARTS.create_module_section("Move highlight"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.move_highlight.dx.one_left, "Move one left"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.move_highlight.dx.one_right, "Move one right"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.move_highlight.swap, "Move card"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.move_highlight.to_end, "Move to end"), + } +end + +Handy.UI.get_config_tab = function(_tab) + local result = { + n = G.UIT.ROOT, + config = { align = "cm", padding = 0.05, colour = G.C.CLEAR, minh = 5, minw = 5 }, + nodes = {}, + } + if _tab == "Overall" then + result.nodes = Handy.UI.get_config_tab_overall() + elseif _tab == "Interactions" then + result.nodes = Handy.UI.get_config_tab_interactions() + elseif _tab == "Dangerous" then + result.nodes = Handy.UI.get_config_tab_dangerous() + elseif _tab == "Keybinds" then + result.nodes = Handy.UI.get_config_tab_keybinds() + elseif _tab == "Keybinds 2" then + result.nodes = Handy.UI.get_config_tab_keybinds_2() + end + return result +end diff --git a/HandyBalatro/index.lua b/HandyBalatro/index.lua new file mode 100644 index 0000000..96685f3 --- /dev/null +++ b/HandyBalatro/index.lua @@ -0,0 +1,1622 @@ +Handy = setmetatable({ + last_clicked_area = nil, + last_clicked_card = nil, + + utils = {}, +}, {}) + +--- @generic T +--- @generic S +--- @param target T +--- @param source S +--- @param ... any +--- @return T | S +function Handy.utils.table_merge(target, source, ...) + assert(type(target) == "table", "Target is not a table") + local tables_to_merge = { source, ... } + if #tables_to_merge == 0 then + return target + end + + for k, t in ipairs(tables_to_merge) do + assert(type(t) == "table", string.format("Expected a table as parameter %d", k)) + end + + for i = 1, #tables_to_merge do + local from = tables_to_merge[i] + for k, v in pairs(from) do + if type(k) == "number" then + table.insert(target, v) + elseif type(k) == "string" then + if type(v) == "table" then + target[k] = target[k] or {} + target[k] = Handy.utils.table_merge(target[k], v) + else + target[k] = v + end + end + end + end + + return target +end + +function Handy.utils.table_contains(t, value) + for i = #t, 1, -1 do + if t[i] and t[i] == value then + return true + end + end + return false +end + +-- + +Handy.config = { + default = { + notifications_level = 3, + + insta_highlight = { + enabled = true, + }, + insta_buy_or_sell = { + enabled = true, + key_1 = "Shift", + key_2 = nil, + }, + insta_use = { + enabled = true, + key_1 = "Ctrl", + key_2 = nil, + }, + move_highlight = { + enabled = true, + + swap = { + enabled = true, + key_1 = "Shift", + key_2 = nil, + }, + to_end = { + enabled = true, + key_1 = "Ctrl", + key_2 = nil, + }, + + dx = { + one_left = { + enabled = true, + key_1 = "Left", + key_2 = nil, + }, + one_right = { + enabled = true, + key_1 = "Right", + key_2 = nil, + }, + }, + }, + + insta_cash_out = { + enabled = true, + key_1 = "Enter", + key_2 = nil, + }, + insta_booster_skip = { + enabled = true, + key_1 = "Enter", + key_2 = nil, + }, + + dangerous_actions = { + enabled = false, + + immediate_buy_and_sell = { + enabled = true, + key_1 = "Middle Mouse", + key_2 = nil, + + queue = { + enabled = false, + }, + }, + + nopeus_unsafe = { + enabled = true, + }, + }, + + speed_multiplier = { + enabled = true, + + key_1 = "Alt", + key_2 = nil, + }, + + shop_reroll = { + enabled = true, + key_1 = "Q", + key_2 = nil, + }, + play_and_discard = { + enabled = true, + play = { + enabled = true, + key_1 = nil, + key_2 = nil, + }, + discard = { + enabled = true, + key_1 = nil, + key_2 = nil, + }, + }, + + nopeus_interaction = { + enabled = true, + + key_1 = "]", + key_2 = nil, + }, + + not_just_yet_interaction = { + enabled = true, + key_1 = "Enter", + key_2 = nil, + }, + }, + current = {}, + + save = function() + if Handy.current_mod then + Handy.current_mod.config = Handy.config.current + SMODS.save_mod_config(Handy.current_mod) + end + end, +} +Handy.config.current = Handy.utils.table_merge({}, Handy.config.default) + +-- + +Handy.fake_events = { + check = function(arg) + local fake_event = { + UIBox = arg.UIBox, + config = { + ref_table = arg.card, + button = arg.button, + id = arg.id, + }, + } + arg.func(fake_event) + return fake_event.config.button ~= nil, fake_event.config.button + end, + execute = function(arg) + if type(arg.func) == "function" then + arg.func({ + UIBox = arg.UIBox, + config = { + ref_table = arg.card, + button = arg.button, + id = arg.id, + }, + }) + end + end, +} +Handy.controller = { + bind_module = nil, + bind_key = nil, + bind_button = nil, + + update_bind_button_text = function(text) + local button_text = Handy.controller.bind_button.children[1].children[1] + button_text.config.text_drawable = nil + button_text.config.text = text + button_text:update_text() + button_text.UIBox:recalculate() + end, + init_bind = function(button) + button.config.button = nil + Handy.controller.bind_button = button + Handy.controller.bind_module = button.config.ref_table.module + Handy.controller.bind_key = button.config.ref_table.key + + Handy.controller.update_bind_button_text( + "[" .. (Handy.controller.bind_module[Handy.controller.bind_key] or "None") .. "]" + ) + end, + complete_bind = function(key) + Handy.controller.bind_module[Handy.controller.bind_key] = key + Handy.controller.update_bind_button_text(key or "None") + + Handy.controller.bind_button.config.button = "handy_init_keybind_change" + Handy.controller.bind_button = nil + Handy.controller.bind_module = nil + Handy.controller.bind_key = nil + end, + cancel_bind = function() + Handy.controller.update_bind_button_text(Handy.controller.bind_module[Handy.controller.bind_key] or "None") + + Handy.controller.bind_button.config.button = "handy_init_keybind_change" + Handy.controller.bind_button = nil + Handy.controller.bind_module = nil + Handy.controller.bind_key = nil + end, + + process_bind = function(key) + if not Handy.controller.bind_button then + return false + end + local parsed_key = Handy.controller.parse(key) + if parsed_key == "Escape" then + parsed_key = nil + end + Handy.controller.complete_bind(parsed_key) + Handy.config.save() + return true + end, + + parse_table = { + ["mouse1"] = "Left Mouse", + ["mouse2"] = "Right Mouse", + ["mouse3"] = "Middle Mouse", + ["mouse4"] = "Mouse 4", + ["mouse5"] = "Mouse 5", + ["wheelup"] = "Wheel Up", + ["wheeldown"] = "Wheel Down", + ["lshift"] = "Shift", + ["rshift"] = "Shift", + ["lctrl"] = "Ctrl", + ["rctrl"] = "Ctrl", + ["lalt"] = "Alt", + ["ralt"] = "Alt", + ["lgui"] = "GUI", + ["rgui"] = "GUI", + ["return"] = "Enter", + ["kpenter"] = "Enter", + ["pageup"] = "Page Up", + ["pagedown"] = "Page Down", + ["numlock"] = "Num Lock", + ["capslock"] = "Caps Lock", + ["scrolllock"] = "Scroll Lock", + }, + resolve_table = { + ["Left Mouse"] = { "mouse1" }, + ["Right Mouse"] = { "mouse2" }, + ["Middle Mouse"] = { "mouse3" }, + ["Mouse 4"] = { "mouse4" }, + ["Mouse 5"] = { "mouse5" }, + ["Wheel Up"] = { "wheelup" }, + ["Wheel Down"] = { "wheeldown" }, + ["Shift"] = { "lshift", "rshift" }, + ["Ctrl"] = { "lctrl", "rctrl" }, + ["Alt"] = { "lalt", "ralt" }, + ["GUI"] = { "lgui", "rgui" }, + ["Enter"] = { "return", "kpenter" }, + ["Page Up"] = { "pageup" }, + ["Page Down"] = { "pagedown" }, + ["Num Lock"] = { "numlock" }, + ["Caps Lock"] = { "capslock" }, + ["Scroll Lock"] = { "scrolllock" }, + }, + + mouse_to_key_table = { + [1] = "mouse1", + [2] = "mouse2", + [3] = "mouse3", + [4] = "mouse4", + [5] = "mouse5", + }, + wheel_to_key_table = { + [1] = "wheelup", + [2] = "wheeldown", + }, + + mouse_buttons = { + ["Left Mouse"] = 1, + ["Right Mouse"] = 2, + ["Middle Mouse"] = 3, + ["Mouse 4"] = 4, + ["Mouse 5"] = 5, + }, + wheel_buttons = { + ["Wheel Up"] = 1, + ["Wheel Down"] = 2, + }, + + parse = function(raw_key) + if not raw_key then + return nil + end + if Handy.controller.parse_table[raw_key] then + return Handy.controller.parse_table[raw_key] + elseif string.sub(raw_key, 1, 2) == "kp" then + return "NUM " .. string.sub(raw_key, 3) + else + return string.upper(string.sub(raw_key, 1, 1)) .. string.sub(raw_key, 2) + end + end, + resolve = function(parsed_key) + if not parsed_key then + return nil + end + if Handy.controller.resolve_table[parsed_key] then + return unpack(Handy.controller.resolve_table[parsed_key]) + elseif string.sub(parsed_key, 1, 4) == "NUM " then + return "kp" .. string.sub(parsed_key, 5) + else + local str = string.gsub(string.lower(parsed_key), "%s+", "") + return str + end + end, + is_down = function(...) + local parsed_keys = { ... } + for i = 1, #parsed_keys do + local parsed_key = parsed_keys[i] + if parsed_key and parsed_key ~= "Unknown" then + if Handy.controller.wheel_buttons[parsed_key] then + -- Well, skip + elseif Handy.controller.mouse_buttons[parsed_key] then + if love.mouse.isDown(Handy.controller.mouse_buttons[parsed_key]) then + return true + end + else + local success, is_down = pcall(function() + return love.keyboard.isDown(Handy.controller.resolve(parsed_key)) + end) + if success and is_down then + return true + end + end + end + end + return false + end, + is = function(raw_key, ...) + if not raw_key then + return false + end + local parsed_keys = { ... } + for i = 1, #parsed_keys do + local parsed_key = parsed_keys[i] + if parsed_key then + local resolved_key_1, resolved_key_2 = Handy.controller.resolve(parsed_key) + if raw_key and raw_key ~= "Unknown" and (raw_key == resolved_key_1 or raw_key == resolved_key_2) then + return true + end + end + end + return false + end, + + is_module_key_down = function(module) + return module and module.enabled and Handy.controller.is_down(module.key_1, module.key_2) + end, + is_module_key = function(module, raw_key) + return module and module.enabled and Handy.controller.is(raw_key, module.key_1, module.key_2) + end, + + process_key = function(key, released) + if not released then + if Handy.controller.process_bind(key) then + return true + end + + Handy.move_highlight.use(key) + Handy.speed_multiplier.use(key) + Handy.shop_reroll.use(key) + Handy.play_and_discard.use(key) + end + Handy.insta_booster_skip.use(key, released) + Handy.insta_cash_out.use(key, released) + Handy.not_just_yet_interaction.use(key, released) + Handy.dangerous_actions.toggle_queue(key, released) + Handy.UI.state_panel.update(key, released) + return false + end, + process_mouse = function(mouse, released) + local key = Handy.controller.mouse_to_key_table[mouse] + if not released then + if Handy.controller.process_bind(key) then + return true + end + + Handy.move_highlight.use(key) + Handy.speed_multiplier.use(key) + Handy.shop_reroll.use(key) + Handy.play_and_discard.use(key) + end + Handy.insta_booster_skip.use(key, released) + Handy.insta_cash_out.use(key, released) + Handy.not_just_yet_interaction.use(key, released) + Handy.dangerous_actions.toggle_queue(key, released) + Handy.UI.state_panel.update(key, released) + return false + end, + process_wheel = function(wheel) + local key = Handy.controller.wheel_to_key_table[wheel] + + if Handy.controller.process_bind(key) then + return true + end + + Handy.move_highlight.use(key) + Handy.speed_multiplier.use(key) + Handy.nopeus_interaction.use(key) + Handy.shop_reroll.use(key) + Handy.play_and_discard.use(key) + Handy.UI.state_panel.update(key, false) + end, + process_card_click = function(card) + if Handy.insta_actions.use(card) then + return true + end + Handy.last_clicked_card = card + Handy.last_clicked_area = card.area + return false + end, + process_card_hover = function(card) + if Handy.insta_highlight.use(card) then + return true + end + if Handy.dangerous_actions.use(card) then + return true + end + return false + end, + process_update = function(dt) + Handy.insta_booster_skip.update() + Handy.insta_cash_out.update() + Handy.not_just_yet_interaction.update() + Handy.UI.update(dt) + end, +} + +-- + +Handy.insta_cash_out = { + is_hold = false, + + is_skipped = false, + is_button_created = false, + dollars = nil, + + can_execute = function(check) + if check then + return not not ( + Handy.insta_cash_out.is_hold + and G.STAGE == G.STAGES.RUN + and Handy.insta_cash_out.is_skipped + and not G.SETTINGS.paused + and G.round_eval + ) + else + return not not ( + Handy.insta_cash_out.is_hold + and G.STAGE == G.STAGES.RUN + and not Handy.insta_cash_out.is_skipped + and Handy.insta_cash_out.dollars + and not G.SETTINGS.paused + and G.round_eval + ) + end + end, + execute = function() + Handy.insta_cash_out.is_skipped = true + + if Handy.insta_cash_out.is_button_created then + G.GAME.current_round.dollars = Handy.insta_cash_out.dollars + Handy.insta_cash_out.dollars = nil + end + G.E_MANAGER:add_event(Event({ + trigger = "immediate", + func = function() + G.FUNCS.cash_out({ + config = { + id = "cash_out_button", + }, + }) + return true + end, + })) + return true + end, + + use = function(key, released) + if Handy.controller.is_module_key(Handy.config.current.insta_cash_out, key) then + Handy.insta_cash_out.is_hold = not released + end + return false + end, + + update = function() + if not Handy.config.current.insta_cash_out.enabled then + Handy.insta_cash_out.is_hold = false + end + return Handy.insta_cash_out.can_execute() and Handy.insta_cash_out.execute() or false + end, + + update_state_panel = function(state, key, released) + -- if G.STAGE ~= G.STAGES.RUN then + -- return false + -- end + -- if Handy.config.current.notifications_level < 4 then + -- return false + -- end + -- if Handy.insta_cash_out.can_execute(true) then + -- state.items.insta_cash_out = { + -- text = "Skip Cash Out", + -- hold = false, + -- order = 10, + -- } + -- return true + -- end + -- return false + end, +} + +Handy.insta_booster_skip = { + is_hold = false, + is_skipped = false, + + can_execute = function(check) + if check then + return not not ( + Handy.insta_booster_skip.is_hold + and G.STAGE == G.STAGES.RUN + and not G.SETTINGS.paused + and G.booster_pack + ) + end + return not not ( + Handy.insta_booster_skip.is_hold + and not Handy.insta_booster_skip.is_skipped + and G.STAGE == G.STAGES.RUN + and not G.SETTINGS.paused + and G.booster_pack + and Handy.fake_events.check({ + func = G.FUNCS.can_skip_booster, + }) + ) + end, + execute = function() + Handy.insta_booster_skip.is_skipped = true + G.E_MANAGER:add_event(Event({ + func = function() + G.FUNCS.skip_booster() + return true + end, + })) + return true + end, + + use = function(key, released) + if Handy.controller.is_module_key(Handy.config.current.insta_booster_skip, key) then + Handy.insta_booster_skip.is_hold = not released + end + return false + end, + + update = function() + if not Handy.config.current.insta_booster_skip.enabled then + Handy.insta_booster_skip.is_hold = false + end + return Handy.insta_booster_skip.can_execute() and Handy.insta_booster_skip.execute() or false + end, + + update_state_panel = function(state, key, released) + if G.STAGE ~= G.STAGES.RUN then + return false + end + if Handy.config.current.notifications_level < 4 then + return false + end + if Handy.insta_booster_skip.can_execute(true) then + state.items.insta_booster_skip = { + text = "Skip Booster Packs", + hold = Handy.insta_booster_skip.is_hold, + order = 10, + } + return true + end + return false + end, +} + +Handy.insta_highlight = { + can_execute = function(card) + return G.STAGE == G.STAGES.RUN + and Handy.config.current.insta_highlight.enabled + and card + and card.area == G.hand + -- TODO: fix it + and not next(love.touch.getTouches()) + and love.mouse.isDown(1) + and not card.highlighted + end, + execute = function(card) + card.area:add_to_highlighted(card) + return false + end, + + use = function(card) + return Handy.insta_highlight.can_execute(card) and Handy.insta_highlight.execute(card) or false + end, + + update_state_panel = function(state, key, released) end, +} + +Handy.insta_actions = { + get_actions = function() + return { + buy_or_sell = Handy.controller.is_module_key_down(Handy.config.current.insta_buy_or_sell), + use = Handy.controller.is_module_key_down(Handy.config.current.insta_use), + } + end, + can_execute = function(card, buy_or_sell, use) + return not not (G.STAGE == G.STAGES.RUN and (buy_or_sell or use) and card and card.area) + end, + execute = function(card, buy_or_sell, use, only_sell) + local target_button = nil + local is_shop_button = false + local is_custom_button = false + local is_playable_consumeable = false + + local base_background = G.UIDEF.card_focus_ui(card) + local base_attach = base_background:get_UIE_by_ID("ATTACH_TO_ME").children + local card_buttons = G.UIDEF.use_and_sell_buttons(card) + local result_funcs = {} + for _, node in ipairs(card_buttons.nodes) do + if node.config and node.config.func then + result_funcs[node.config.func] = node + end + end + local is_booster_pack_card = (G.pack_cards and card.area == G.pack_cards) and not card.ability.consumeable + + if use then + if card.area == G.hand and card.ability.consumeable then + local success, playale_consumeable_button = pcall(function() + -- G.UIDEF.use_and_sell_buttons(G.hand.highlighted[1]).nodes[1].nodes[2].nodes[1].nodes[1] + return card_buttons.nodes[1].nodes[2].nodes[1].nodes[1] + end) + if success and playale_consumeable_button then + target_button = playale_consumeable_button + is_custom_button = true + is_playable_consumeable = true + end + elseif result_funcs.can_select_alchemical or result_funcs.can_select_crazy_card then + -- Prevent cards to be selected when usage is required: + -- Alchemical cards, Cines + else + target_button = base_attach.buy_and_use + or (not is_booster_pack_card and base_attach.use) + or card.children.buy_and_use_button + is_shop_button = target_button == card.children.buy_and_use_button + end + elseif buy_or_sell then + target_button = card.children.buy_button + or result_funcs.can_select_crazy_card -- Cines + or result_funcs.can_select_alchemical -- Alchemical cards + or result_funcs.can_use_mupack -- Multipacks + or result_funcs.can_reserve_card -- Code cards, for example + or base_attach.buy + or base_attach.redeem + or base_attach.sell + or (is_booster_pack_card and base_attach.use) + + if only_sell and target_button ~= base_attach.sell then + target_button = nil + end + is_shop_button = target_button == card.children.buy_button + end + + if target_button and not is_custom_button and not is_shop_button then + for _, node in ipairs(card_buttons.nodes) do + if target_button == node then + is_custom_button = true + end + end + end + + local target_button_UIBox + local target_button_definition + + local cleanup = function() + base_background:remove() + if target_button_UIBox and is_custom_button then + target_button_UIBox:remove() + end + end + + if target_button then + if is_playable_consumeable then + card.area:add_to_highlighted(card) + if not card.highlighted then + cleanup() + return false + end + end + + target_button_UIBox = (is_custom_button and UIBox({ + definition = target_button, + config = {}, + })) or target_button + target_button_definition = (is_custom_button and target_button) + or (is_shop_button and target_button.definition) + or target_button.definition.nodes[1] + + local check, button = Handy.fake_events.check({ + func = G.FUNCS[target_button_definition.config.func], + button = nil, + id = target_button_definition.config.id, + card = card, + UIBox = target_button_UIBox, + }) + if check then + Handy.fake_events.execute({ + func = G.FUNCS[button or target_button_definition.config.button], + button = nil, + id = target_button_definition.config.id, + card = card, + UIBox = target_button_UIBox, + }) + cleanup() + return true + end + end + + cleanup() + return false + end, + + use = function(card) + if card.ability and card.ability.handy_dangerous_actions_used then + return true + end + + local actions = Handy.insta_actions.get_actions() + + return Handy.insta_actions.can_execute(card, actions.buy_or_sell, actions.use) + and Handy.insta_actions.execute(card, actions.buy_or_sell, actions.use) + or false + end, + + update_state_panel = function(state, key, released) + if G.STAGE ~= G.STAGES.RUN then + return false + end + if Handy.config.current.notifications_level < 4 then + return false + end + local result = false + local actions = Handy.insta_actions.get_actions() + if actions.use then + state.items.insta_use = { + text = "Quick use", + hold = true, + order = 10, + } + result = true + end + if actions.buy_or_sell then + state.items.quick_buy_and_sell = { + text = "Quick buy and sell", + hold = true, + order = 11, + } + result = true + end + return result + end, +} + +Handy.move_highlight = { + dx = { + one_left = -1, + one_right = 1, + }, + + get_dx = function(key, area) + for module_key, module in pairs(Handy.config.current.move_highlight.dx) do + if Handy.controller.is_module_key(module, key) then + return Handy.move_highlight.dx[module_key] + end + end + return nil + end, + get_actions = function(key, area) + return { + swap = Handy.controller.is_module_key_down(Handy.config.current.move_highlight.swap), + to_end = Handy.controller.is_module_key_down(Handy.config.current.move_highlight.to_end), + } + end, + + can_swap = function(key, area) + if not area then + return false + end + return not Handy.utils.table_contains({ + G.pack_cards, + G.shop_jokers, + G.shop_booster, + G.shop_vouchers, + }, area) + end, + cen_execute = function(key, area) + return not not ( + Handy.config.current.move_highlight.enabled + and G.STAGE == G.STAGES.RUN + and area + and area.highlighted + and area.highlighted[1] + and Handy.utils.table_contains({ + G.consumeables, + G.jokers, + G.cine_quests, + G.pack_cards, + G.shop_jokers, + G.shop_booster, + G.shop_vouchers, + }, area) + ) + end, + execute = function(key, area) + local dx = Handy.move_highlight.get_dx(key, area) + if not dx then + return false + end + + local current_card = area.highlighted[1] + for current_index = #area.cards, 1, -1 do + if area.cards[current_index] == current_card then + local actions = Handy.move_highlight.get_actions(key, area) + local next_index = actions.to_end and (dx > 0 and #area.cards or 1) + or ((#area.cards + current_index + dx - 1) % #area.cards) + 1 + if current_index == next_index then + return + end + local next_card = area.cards[next_index] + if not next_card then + return + end + if actions.swap and Handy.move_highlight.can_swap(key, area) then + if actions.to_end or next_index == 1 or next_index == #area.cards then + table.remove(area.cards, current_index) + table.insert(area.cards, next_index, current_card) + else + area.cards[next_index] = current_card + area.cards[current_index] = next_card + end + else + area:remove_from_highlighted(current_card) + area:add_to_highlighted(next_card) + end + return + end + end + end, + + use = function(key, area) + area = area or Handy.last_clicked_area + return Handy.move_highlight.cen_execute(key, area) and Handy.move_highlight.execute(key, area) or false + end, + + update_state_panel = function(state, key, released) end, +} + +Handy.dangerous_actions = { + sell_queue = {}, + + sell_next_card = function() + local card = table.remove(Handy.dangerous_actions.sell_queue, 1) + if not card then + stop_use() + return + end + + G.GAME.STOP_USE = 0 + Handy.insta_actions.execute(card, true, false, true) + + G.E_MANAGER:add_event(Event({ + blocking = false, + func = function() + if card.ability then + card.ability.handy_dangerous_actions_used = nil + end + return true + end, + })) + Handy.dangerous_actions.sell_next_card() + end, + + can_execute = function(card) + return G.STAGE == G.STAGES.RUN + and Handy.config.current.dangerous_actions.enabled + and card + and not (card.ability and card.ability.handy_dangerous_actions_used) + end, + execute = function(card) + if Handy.controller.is_module_key_down(Handy.config.current.dangerous_actions.immediate_buy_and_sell) then + if Handy.config.current.dangerous_actions.immediate_buy_and_sell.queue.enabled then + if not card.ability then + card.ability = {} + end + card.ability.handy_dangerous_actions_used = true + + table.insert(Handy.dangerous_actions.sell_queue, card) + Handy.UI.state_panel.update(nil, nil) + return false + else + local result = Handy.insta_actions.execute(card, true, false) + if result then + if not card.ability then + card.ability = {} + end + card.ability.handy_dangerous_actions_used = true + + G.CONTROLLER.locks.selling_card = nil + G.CONTROLLER.locks.use = nil + G.GAME.STOP_USE = 0 + + G.E_MANAGER:add_event(Event({ + func = function() + if card.ability then + card.ability.handy_dangerous_actions_used = nil + end + return true + end, + })) + end + return result + end + end + return false + end, + + use = function(card) + return Handy.dangerous_actions.can_execute(card) and Handy.dangerous_actions.execute(card) or false + end, + + toggle_queue = function(key, released) + if Handy.controller.is_module_key(Handy.config.current.dangerous_actions.immediate_buy_and_sell, key) then + if released then + Handy.dangerous_actions.sell_next_card() + else + Handy.dangerous_actions.sell_queue = {} + end + end + end, + + update_state_panel = function(state, key, released) + if G.STAGE ~= G.STAGES.RUN then + return false + end + + if not Handy.config.current.dangerous_actions.enabled then + return false + end + if Handy.config.current.notifications_level < 2 then + return false + end + if Handy.controller.is_module_key_down(Handy.config.current.dangerous_actions.immediate_buy_and_sell) then + state.dangerous = true + state.items.dangerous_hint = { + text = "[Unsafe] Bugs can appear!", + dangerous = true, + hold = true, + order = 99999999, + } + if state.items.quick_buy_and_sell then + state.items.quick_buy_and_sell.dangerous = true + elseif Handy.insta_actions.get_actions().buy_or_sell then + local text = "Quick sell" + if Handy.config.current.dangerous_actions.immediate_buy_and_sell.queue.enabled then + text = text .. " [" .. #Handy.dangerous_actions.sell_queue .. " in queue]" + end + state.items.quick_buy_and_sell = { + text = text, + hold = true, + order = 11, + dangerous = true, + } + end + return true + end + return false + end, +} + +Handy.speed_multiplier = { + value = 1, + + get_actions = function(key) + return { + multiply = key == Handy.controller.wheel_to_key_table[1], + divide = key == Handy.controller.wheel_to_key_table[2], + } + end, + can_execute = function(key) + return Handy.config.current.speed_multiplier.enabled + and not G.OVERLAY_MENU + and Handy.controller.is_module_key_down(Handy.config.current.speed_multiplier) + end, + + execute = function(key) + local actions = Handy.speed_multiplier.get_actions(key) + if actions.multiply then + Handy.speed_multiplier.multiply() + end + if actions.divide then + Handy.speed_multiplier.divide() + end + return false + end, + + multiply = function() + Handy.speed_multiplier.value = math.min(512, Handy.speed_multiplier.value * 2) + end, + divide = function() + Handy.speed_multiplier.value = math.max(0.001953125, Handy.speed_multiplier.value / 2) + end, + + use = function(key) + return Handy.speed_multiplier.can_execute(key) and Handy.speed_multiplier.execute(key) or false + end, + + update_state_panel = function(state, key, released) + if not key or not Handy.speed_multiplier.can_execute(key) then + return false + end + if Handy.config.current.notifications_level < 3 then + return false + end + + local actions = Handy.speed_multiplier.get_actions(key) + + if actions.multiply or actions.divide then + state.items.change_speed_multiplier = { + text = "Game speed multiplier: " + .. ( + Handy.speed_multiplier.value >= 1 and Handy.speed_multiplier.value + or ("1/" .. (1 / Handy.speed_multiplier.value)) + ), + hold = false, + order = 5, + } + return true + end + return false + end, +} + +Handy.shop_reroll = { + can_execute = function(key) + return G.STATE == G.STATES.SHOP + and Handy.fake_events.check({ func = G.FUNCS.can_reroll, button = "reroll_shop" }) + and Handy.controller.is_module_key(Handy.config.current.shop_reroll, key) + end, + execute = function(key) + G.FUNCS.reroll_shop() + return false + end, + + use = function(key) + return Handy.shop_reroll.can_execute(key) and Handy.shop_reroll.execute(key) or false + end, +} + +Handy.play_and_discard = { + get_actions = function(key) + return { + discard = Handy.controller.is_module_key(Handy.config.current.play_and_discard.discard, key), + play = Handy.controller.is_module_key(Handy.config.current.play_and_discard.play, key), + } + end, + + can_execute = function(play, discard) + return not not ( + Handy.config.current.play_and_discard.enabled + and G.STATE == G.STATES.SELECTING_HAND + and ( + (discard and Handy.fake_events.check({ + func = G.FUNCS.can_discard, + })) or (play and Handy.fake_events.check({ + func = G.FUNCS.can_play, + })) + ) + ) + end, + execute = function(play, discard) + if discard then + Handy.fake_events.execute({ + func = G.FUNCS.discard_cards_from_highlighted, + }) + elseif play then + Handy.fake_events.execute({ + func = G.FUNCS.play_cards_from_highlighted, + }) + end + return false + end, + + use = function(key) + local actions = Handy.play_and_discard.get_actions(key) + return Handy.play_and_discard.can_execute(actions.play, actions.discard) + and Handy.play_and_discard.execute(actions.play, actions.discard) + or false + end, +} + +Handy.nopeus_interaction = { + is_present = function() + return type(Nopeus) == "table" + end, + + get_actions = function(key) + return { + increase = key == Handy.controller.wheel_to_key_table[1], + decrease = key == Handy.controller.wheel_to_key_table[2], + } + end, + + can_dangerous = function() + return not not ( + Handy.config.current.dangerous_actions.enabled + and Handy.config.current.dangerous_actions.nopeus_unsafe.enabled + ) + end, + can_execute = function(key) + return not not ( + Handy.config.current.nopeus_interaction.enabled + and Handy.nopeus_interaction.is_present() + and not G.OVERLAY_MENU + and Handy.controller.is_module_key_down(Handy.config.current.nopeus_interaction) + ) + end, + execute = function(key) + local actions = Handy.nopeus_interaction.get_actions(key) + if actions.increase then + Handy.nopeus_interaction.increase() + end + if actions.decrease then + Handy.nopeus_interaction.decrease() + end + end, + + change = function(dx) + if not Handy.nopeus_interaction.is_present() then + G.SETTINGS.FASTFORWARD = 0 + elseif Nopeus.Optimised then + G.SETTINGS.FASTFORWARD = math.min( + Handy.nopeus_interaction.can_dangerous() and 4 or 3, + math.max(0, (G.SETTINGS.FASTFORWARD or 0) + dx) + ) + else + G.SETTINGS.FASTFORWARD = math.min( + Handy.nopeus_interaction.can_dangerous() and 3 or 2, + math.max(0, (G.SETTINGS.FASTFORWARD or 0) + dx) + ) + end + end, + increase = function() + Handy.nopeus_interaction.change(1) + end, + decrease = function() + Handy.nopeus_interaction.change(-1) + end, + + use = function(key) + return Handy.nopeus_interaction.can_execute(key) and Handy.nopeus_interaction.execute(key) or false + end, + + update_state_panel = function(state, key, released) + if not Handy.nopeus_interaction.is_present() then + return false + end + if not key or not Handy.nopeus_interaction.can_execute(key) then + return false + end + + local actions = Handy.nopeus_interaction.get_actions(key) + + if actions.increase or actions.decrease then + local states = { + Nopeus.Off, + Nopeus.Planets, + Nopeus.On, + Nopeus.Unsafe, + } + if Nopeus.Optimised then + states = { + Nopeus.Off, + Nopeus.Planets, + Nopeus.On, + Nopeus.Optimised, + Nopeus.Unsafe, + } + end + + local is_dangerous = G.SETTINGS.FASTFORWARD == (#states - 1) + + if is_dangerous then + state.dangerous = true + if Handy.config.current.notifications_level < 2 then + return false + end + else + if Handy.config.current.notifications_level < 3 then + return false + end + end + + state.items.change_nopeus_fastforward = { + text = "Nopeus fast-forward: " .. states[(G.SETTINGS.FASTFORWARD or 0) + 1], + hold = false, + order = 4, + dangerous = is_dangerous, + } + return true + end + return false + end, +} + +Handy.not_just_yet_interaction = { + is_present = function() + return G and G.FUNCS and G.FUNCS.njy_endround ~= nil + end, + + can_execute = function(check) + return not not ( + Handy.not_just_yet_interaction.is_present() + and GLOBAL_njy_vanilla_override + and G.STATE_COMPLETE + and G.buttons + and G.buttons.states + and G.buttons.states.visible + and G.GAME + and G.GAME.chips + and G.GAME.blind + and G.GAME.blind.chips + and to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) + ) + end, + execute = function() + stop_use() + G.STATE = G.STATES.NEW_ROUND + end_round() + end, + + use = function(key, released) + if Handy.controller.is_module_key(Handy.config.current.not_just_yet_interaction, key) then + GLOBAL_njy_vanilla_override = not released + end + return false + end, + + update = function() + if not Handy.config.current.not_just_yet_interaction.enabled then + GLOBAL_njy_vanilla_override = nil + end + return Handy.not_just_yet_interaction.can_execute() and Handy.not_just_yet_interaction.execute() or false + end, +} + +-- + +-- + +Handy.UI = { + counter = 1, + C = { + TEXT = HEX("FFFFFF"), + BLACK = HEX("000000"), + RED = HEX("FF0000"), + + DYN_BASE_APLHA = { + CONTAINER = 0.6, + + TEXT = 1, + TEXT_DANGEROUS = 1, + }, + + DYN = { + CONTAINER = HEX("000000"), + + TEXT = HEX("FFFFFF"), + TEXT_DANGEROUS = HEX("FFEEEE"), + }, + }, + state_panel = { + element = nil, + + title = nil, + items = nil, + + previous_state = { + dangerous = false, + title = {}, + items = {}, + sub_items = {}, + hold = false, + }, + current_state = { + dangerous = false, + title = {}, + items = {}, + sub_items = {}, + hold = false, + }, + + get_definition = function() + local state_panel = Handy.UI.state_panel + + local items_raw = {} + for _, item in pairs(state_panel.current_state.items) do + table.insert(items_raw, item) + end + + table.sort(items_raw, function(a, b) + return a.order < b.order + end) + + local items = {} + for _, item in ipairs(items_raw) do + table.insert(items, { + n = G.UIT.R, + config = { + align = "cm", + padding = 0.035, + }, + nodes = { + { + n = G.UIT.T, + config = { + text = item.text, + scale = 0.225, + colour = item.dangerous and Handy.UI.C.DYN.TEXT_DANGEROUS or Handy.UI.C.DYN.TEXT, + shadow = true, + }, + }, + }, + }) + end + + return { + n = G.UIT.ROOT, + config = { align = "cm", padding = 0.1, r = 0.1, colour = G.C.CLEAR, id = "handy_state_panel" }, + nodes = { + { + n = G.UIT.C, + config = { + align = "cm", + padding = 0.125, + r = 0.1, + colour = Handy.UI.C.DYN.CONTAINER, + }, + nodes = { + { + n = G.UIT.R, + config = { + align = "cm", + }, + nodes = { + { + n = G.UIT.T, + config = { + text = state_panel.current_state.title.text, + scale = 0.3, + colour = Handy.UI.C.DYN.TEXT, + shadow = true, + id = "handy_state_title", + }, + }, + }, + }, + { + n = G.UIT.R, + config = { + align = "cm", + }, + nodes = { + { + n = G.UIT.C, + config = { + align = "cm", + id = "handy_state_items", + }, + nodes = items, + }, + }, + }, + }, + }, + }, + } + end, + emplace = function() + if Handy.UI.state_panel.element then + Handy.UI.state_panel.element:remove() + end + local element = UIBox({ + definition = Handy.UI.state_panel.get_definition(), + config = { + instance_type = "ALERT", + align = "cm", + major = G.ROOM_ATTACH, + can_collide = false, + offset = { + x = 0, + y = 3.5, + }, + }, + }) + Handy.UI.state_panel.element = element + Handy.UI.state_panel.title = element:get_UIE_by_ID("handy_state_title") + Handy.UI.state_panel.items = element:get_UIE_by_ID("handy_state_items") + end, + + update = function(key, released) + local state_panel = Handy.UI.state_panel + + local state = { + dangerous = false, + title = {}, + items = {}, + sub_items = {}, + } + + local is_changed = false + + for _, part in ipairs({ + Handy.speed_multiplier, + Handy.insta_booster_skip, + Handy.insta_cash_out, + Handy.insta_actions, + Handy.insta_highlight, + Handy.move_highlight, + Handy.nopeus_interaction, + Handy.dangerous_actions, + }) do + local temp_result = part.update_state_panel(state, key, released) + is_changed = is_changed or temp_result or false + end + + if is_changed then + if state.dangerous then + state.title.text = "Dangerous actions" + else + state.title.text = "Quick actions" + end + + for _, item in pairs(state.items) do + if item.hold then + state.hold = true + end + end + + local color = Handy.UI.C.DYN.CONTAINER + local target_color = state.dangerous and Handy.UI.C.RED or Handy.UI.C.BLACK + color[1] = target_color[1] + color[2] = target_color[2] + color[3] = target_color[3] + + Handy.UI.counter = 0 + state_panel.previous_state = state_panel.current_state + state_panel.current_state = state + + state_panel.emplace() + else + state_panel.current_state.hold = false + end + end, + }, + + update = function(dt) + if Handy.UI.state_panel.current_state.hold then + Handy.UI.counter = 0 + elseif Handy.UI.counter < 1 then + Handy.UI.counter = Handy.UI.counter + dt + end + local multiplier = math.min(1, math.max(0, (1 - Handy.UI.counter) * 2)) + for key, color in pairs(Handy.UI.C.DYN) do + color[4] = (Handy.UI.C.DYN_BASE_APLHA[key] or 1) * multiplier + end + end, +} + +function Handy.UI.init() + Handy.UI.counter = 1 + Handy.UI.state_panel.emplace() + Handy.UI.update(0) +end + +-- + +local love_update_ref = love.update +function love.update(dt, ...) + love_update_ref(dt, ...) + Handy.controller.process_update(dt) +end + +local wheel_moved_ref = love.wheelmoved or function() end +function love.wheelmoved(x, y) + wheel_moved_ref(x, y) + Handy.controller.process_wheel(y > 0 and 1 or 2) +end + +-- + +function Handy.emplace_steamodded() + Handy.current_mod = SMODS.current_mod + Handy.config.current = Handy.utils.table_merge({}, Handy.config.default, SMODS.current_mod.config) + + Handy.current_mod.extra_tabs = function() + return { + { + label = "Overall", + tab_definition_function = function() + return Handy.UI.get_config_tab("Overall") + end, + }, + { + label = "Interactions", + tab_definition_function = function() + return Handy.UI.get_config_tab("Interactions") + end, + }, + { + label = "Dangerous", + tab_definition_function = function() + return Handy.UI.get_config_tab("Dangerous") + end, + }, + { + label = "Keybinds", + tab_definition_function = function() + return Handy.UI.get_config_tab("Keybinds") + end, + }, + { + label = "More keybinds", + tab_definition_function = function() + return Handy.UI.get_config_tab("Keybinds 2") + end, + }, + } + end + + G.E_MANAGER:add_event(Event({ + func = function() + G.njy_keybind = nil + return true + end, + })) +end + +function G.FUNCS.handy_toggle_module_enabled(arg, module) + if not module then + return + end + module.enabled = arg + if module == Handy.config.current.speed_multiplier then + Handy.speed_multiplier.value = 1 + elseif + module == Handy.config.current.dangerous_actions + or module == Handy.config.current.nopeus_interaction + or module == Handy.config.current.dangerous_actions.nopeus_unsafe + then + Handy.nopeus_interaction.change(0) + end + Handy.config.save() +end + +function G.FUNCS.handy_change_notifications_level(arg) + Handy.config.current.notifications_level = arg.to_key + Handy.config.save() +end + +function G.FUNCS.handy_init_keybind_change(e) + Handy.controller.init_bind(e) +end diff --git a/HandyBalatro/lovely.toml b/HandyBalatro/lovely.toml new file mode 100644 index 0000000..566b0d8 --- /dev/null +++ b/HandyBalatro/lovely.toml @@ -0,0 +1,189 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +[[patches]] +[patches.copy] +target = "main.lua" +position = "append" +sources = ["index.lua"] + +[[patches]] +[patches.copy] +target = "main.lua" +position = "append" +sources = ["config_ui.lua"] + +# Skipping Cash Out stage +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''local num_dollars = config.dollars or 1''' +position = "after" +payload = ''' +Handy.insta_cash_out.dollars = config.dollars or 1 +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''G.GAME.current_round.dollars = config.dollars''' +position = "before" +payload = ''' +Handy.insta_cash_out.is_button_created = true +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''G.deck:shuffle('cashout'..G.GAME.round_resets.ante)''' +position = "before" +payload = ''' +Handy.insta_cash_out.is_button_created = false +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''G.FUNCS.cash_out = function(e)''' +position = "after" +payload = ''' +if Handy.insta_cash_out.is_skipped and e.config.button then return end +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''G.ACC = math.min((G.ACC or 0) + dt*0.2*self.SETTINGS.GAMESPEED, 16)''' +position = "after" +payload = ''' + elseif Handy.insta_cash_out.is_skipped then G.ACC = 999 +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''G.STATE = G.STATES.SHOP''' +position = "after" +payload = ''' +Handy.insta_cash_out.is_skipped = false +''' +match_indent = true +overwrite = false + +# Skipping booster +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''G.GAME.PACK_INTERRUPT = nil''' +position = "after" +payload = ''' +Handy.insta_booster_skip.is_skipped = false +''' +match_indent = true +overwrite = false + +# Draw UI +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''self.GAME = saveTable and saveTable.GAME or self:init_game_object()''' +position = "after" +payload = ''' +Handy.UI.init() +''' +match_indent = true +overwrite = false + +# Mouse listeners +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = '''function love.mousepressed(x, y, button, touch)''' +position = "after" +payload = ''' +if not touch and Handy.controller.process_mouse(button, false) then return end +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = '''function love.mousereleased(x, y, button)''' +position = "after" +payload = ''' +if Handy.controller.process_mouse(button, true) then return end +''' +match_indent = true +overwrite = false + +# Key listeners +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = '''function love.keypressed(key)''' +position = "after" +payload = ''' +if Handy.controller.process_key(key, false) then return end +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = '''function love.keyreleased(key)''' +position = "after" +payload = ''' +if Handy.controller.process_key(key, true) then return end +''' +match_indent = true +overwrite = false + +# Handle card click +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''function Card:click()''' +position = "after" +payload = ''' +if Handy.controller.process_card_click(self) then return end +''' +match_indent = true +overwrite = false + +# Handle card hover +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''function Card:hover()''' +position = "after" +payload = ''' +if Handy.controller.process_card_hover(self) then return end +''' +match_indent = true +overwrite = false + +# Apply multiplier to speed +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = '''self.SPEEDFACTOR = self.SPEEDFACTOR + math.max(0, math.abs(G.ACC) - 2)''' +position = "after" +payload = ''' +self.SPEEDFACTOR = self.SPEEDFACTOR * Handy.speed_multiplier.value or 1 +''' +match_indent = true +overwrite = false diff --git a/HandyBalatro/steamodded.lua b/HandyBalatro/steamodded.lua new file mode 100644 index 0000000..a09ff89 --- /dev/null +++ b/HandyBalatro/steamodded.lua @@ -0,0 +1,24 @@ +--- STEAMODDED HEADER +--- MOD_NAME: Handy +--- MOD_ID: Handy +--- MOD_AUTHOR: [SleepyG11] +--- MOD_DESCRIPTION: Quality of Life controls for Balatro + +--- PRIORITY: 0 +--- DISPLAY_NAME: Handy +--- PREFIX: handy +--- VERSION: 1.1.5 +---------------------------------------------- +------------MOD CODE ------------------------- + +Handy.emplace_steamodded() + +SMODS.Atlas({ + key = "modicon", + path = "icon.png", + px = 32, + py = 32, +}) + +---------------------------------------------- +------------MOD CODE END---------------------- diff --git a/Incantation/Incantation.lua b/Incantation/Incantation.lua new file mode 100644 index 0000000..a4d294b --- /dev/null +++ b/Incantation/Incantation.lua @@ -0,0 +1,1066 @@ +--- STEAMODDED HEADER + +--- MOD_NAME: Incantation +--- MOD_ID: incantation +--- MOD_AUTHOR: [jenwalter666, MathIsFun_] +--- MOD_DESCRIPTION: Enables the ability to stack identical consumables. +--- PRIORITY: 89999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +--- BADGE_COLOR: 000000 +--- PREFIX: inc +--- VERSION: 0.5.10 +--- LOADER_VERSION_GEQ: 1.0.0 + +Incantation = {consumable_in_use = false, accelerate = false} --will port more things over to this global later, but for now it's going to be mostly empty + +local CFG = SMODS.current_mod.config + +local MaxStack = 9999 +local BulkUseLimit = 9999 +local NaiveBulkUseCancel = 100 +local AccelerateThreshold = 3 + +local HardLimit = 9007199254740992 + +SMODS.current_mod.config_tab = function() + return {n = G.UIT.ROOT, config = {r = 0.1, align = "cm", padding = 0.1, colour = G.C.BLACK, minw = 8, minh = 4}, nodes = { + {n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = { + {n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = { + create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "NegativesOnly" }, + }}, + {n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = { + { n = G.UIT.T, config = { text = localize('incant_negatives_only'), scale = 0.35, colour = G.C.UI.TEXT_LIGHT }}, + }}, + }}, + {n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = { + {n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = { + create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "StackAnything" }, + }}, + {n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = { + { n = G.UIT.T, config = { text = localize('incant_stack_anything'), scale = 0.35, colour = G.C.RED }}, + }}, + }}, + {n = G.UIT.R, config = {align = "cl", padding = 0}, nodes = { + {n = G.UIT.C, config = { align = "cl", padding = 0.05 }, nodes = { + create_toggle{ col = true, label = "", scale = 0.85, w = 0, shadow = true, ref_table = CFG, ref_value = "UnsafeMode" }, + }}, + {n = G.UIT.C, config = { align = "c", padding = 0 }, nodes = { + { n = G.UIT.T, config = { text = localize('incant_unsafe_mode'), scale = 0.35, colour = G.C.RED }}, + }}, + }} + }} +end + +local function tablecontains(haystack, needle) + for k, v in pairs(haystack) do + if v == needle then + return true + end + end + return false +end + +local Stackable = { + 'Planet', + 'Tarot', + 'Spectral' +} + +local StackableIndividual = { + 'c_black_hole', + 'c_cry_white_hole' +} + +local Divisible = { + 'Planet', + 'Tarot', + 'Spectral' +} + +local DivisibleIndividual = { + 'c_black_hole', + 'c_cry_white_hole' +} + +local BulkUsable = { + 'Planet' +} + +local BulkUsableIndividual = { + 'c_black_hole', + 'c_cry_white_hole' +} + +--Allow mods to add/remove their own card types to the list + +function AllowStacking(set) + if not tablecontains(Stackable, set) then + table.insert(Stackable, set) + end +end + +function AllowStackingIndividual(key) + if not tablecontains(StackableIndividual, key) then + table.insert(StackableIndividual, key) + end +end + +function AllowDividing(set) + AllowStacking(set) + if not tablecontains(Divisible, set) then + table.insert(Divisible, set) + end +end + +function AllowDividingIndividual(key) + AllowStackingIndividual(key) + if not tablecontains(DivisibleIndividual, key) then + table.insert(DivisibleIndividual, key) + end +end + +function AllowBulkUse(set) + AllowStacking(set) + AllowDividing(set) + if not tablecontains(BulkUsable, set) then + table.insert(BulkUsable, set) + end +end + +function AllowBulkUseIndividual(key) + AllowStackingIndividual(key) + AllowDividingIndividual(key) + if not tablecontains(BulkUsableIndividual, key) then + table.insert(BulkUsableIndividual, key) + end +end + +--[[ + ++++ MOD SUPPORT SKELETON : ALWAYS INCLUDE THIS IN YOUR MOD'S INITIALISATION SOMEWHERE +++ + +if not IncantationAddons then + IncantationAddons = { + Stacking = {}, + Dividing = {}, + BulkUse = {}, + StackingIndividual = {}, + DividingIndividual = {}, + BulkUseIndividual = {} + } +end + +then you can use table.insert() to insert sets/keys into the subtables contained within IncantationAddons + +]] + +if IncantationAddons then + if #IncantationAddons.Stacking > 0 then + for _, v in pairs(IncantationAddons.Stacking) do + AllowStacking(v) + end + end + if #IncantationAddons.StackingIndividual > 0 then + for _, v in pairs(IncantationAddons.StackingIndividual) do + AllowStackingIndividual(v) + end + end + if #IncantationAddons.Dividing > 0 then + for _, v in pairs(IncantationAddons.Dividing) do + AllowDividing(v) + end + end + if #IncantationAddons.DividingIndividual > 0 then + for _, v in pairs(IncantationAddons.DividingIndividual) do + AllowDividingIndividual(v) + end + end + if #IncantationAddons.BulkUse > 0 then + for _, v in pairs(IncantationAddons.BulkUse) do + AllowBulkUse(v) + end + end + if #IncantationAddons.BulkUseIndividual > 0 then + for _, v in pairs(IncantationAddons.BulkUseIndividual) do + AllowBulkUseIndividual(v) + end + end +end + +function Card:getQty() + return (self.ability or {}).qty or 1 +end + +function Card:setQty(quantity, dontupdatecost) + if not quantity then quantity = 1 end + if self:CanStack() then + if self.ability then + self.ability.qty = math.min(HardLimit, math.floor(quantity)) + self:create_stack_display() + if not dontupdatecost then self:set_cost() end + end + end +end + +function Card:addQty(quantity, dontupdatecost) + self:setQty(self:getQty() + math.floor(quantity), dontupdatecost) +end + +function Card:subQty(quantity, dont_dissolve, dontupdatecost) + if quantity >= self:getQty() and not dont_dissolve then + self:setQty(0) + self.ignorestacking = true + self:start_dissolve() + else + self:setQty(math.max(0, self:getQty() - math.ceil(quantity)), dontupdatecost) + end +end + +function Card:CanStack() + if self.area and G.consumeables and self.area ~= G.consumeables then + return false + elseif self.ability and (self.ability.set == 'Booster' or self.ability.set == 'Voucher') then + return false + elseif CFG.NegativesOnly and not (self.edition and self.edition.negative) then + return false + elseif CFG.StackAnything then + return true + end + + return (self.config.center and (type(self.config.center.can_stack) == 'function' and self.config.center:can_stack() or self.config.center.can_stack)) or tablecontains(Stackable, self.ability.set) or tablecontains(StackableIndividual, self.config.center_key) +end + +function Card:CanDivide() + if CFG.StackAnything then + return true + end + return (self.config.center and (type(self.config.center.can_divide) == 'function' and self.config.center:can_divide() or self.config.center.can_divide)) or tablecontains(Divisible, self.ability.set) or tablecontains(DivisibleIndividual, self.config.center_key) +end + +function Card:CanBulkUse(ignoreunsafe) + return (not ignoreunsafe and CFG.UnsafeMode) or not self.config.center.no_bulkuse and ((self.config.center and (type(self.config.center.can_bulk_use) == 'function' and self.config.center:can_bulk_use() or (self.config.center.can_bulk_use or (self.config.center.bulk_use and (type(self.config.center.bulk_use) == 'function'))))) or tablecontains(BulkUsable, self.ability.set) or tablecontains(BulkUsableIndividual, self.config.center_key)) +end + +function Card:getmaxuse() + --let modders define their own bulk-use limit in case of concerns with performance + return (self.config.center.bulk_use_limit or UseBulkCap) and math.min((self.config.center.bulk_use_limit or BulkUseLimit), self:getQty()) or (self:getQty()) +end + +function set_consumeable_usage(card, qty) + qty = math.floor(qty or 1) + if card.config.center_key and card.ability.consumeable then + if G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key] then + G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key].count = G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key].count + qty + else + G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key] = {count = 1, order = card.config.center.order} + end + if G.GAME.consumeable_usage[card.config.center_key] then + G.GAME.consumeable_usage[card.config.center_key].count = G.GAME.consumeable_usage[card.config.center_key].count + qty + else + G.GAME.consumeable_usage[card.config.center_key] = {count = 1, order = card.config.center.order, set = card.ability.set} + end + G.GAME.consumeable_usage_total = G.GAME.consumeable_usage_total or {tarot = 0, planet = 0, spectral = 0, tarot_planet = 0, all = 0} + if card.config.center.set == 'Tarot' then + G.GAME.consumeable_usage_total.tarot = G.GAME.consumeable_usage_total.tarot + qty + G.GAME.consumeable_usage_total.tarot_planet = G.GAME.consumeable_usage_total.tarot_planet + qty + elseif card.config.center.set == 'Planet' then + G.GAME.consumeable_usage_total.planet = G.GAME.consumeable_usage_total.planet + qty + G.GAME.consumeable_usage_total.tarot_planet = G.GAME.consumeable_usage_total.tarot_planet + qty + elseif card.config.center.set == 'Spectral' then G.GAME.consumeable_usage_total.spectral = G.GAME.consumeable_usage_total.spectral + qty + end + + G.GAME.consumeable_usage_total.all = G.GAME.consumeable_usage_total.all + qty + + if not card.config.center.discovered then + discover_card(card) + end + + if card.config.center.set == 'Tarot' or card.config.center.set == 'Planet' then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.GAME.last_tarot_planet = card.config.center_key + return true + end + })) + return true + end + })) + end + + end + G:save_settings() +end + +function Card:split(amount, forced, fullmax) + if not amount then amount = math.floor((self:getQty()) / 2) end + amount = math.max(1, amount) + if (self.ability.qty or 0) > (fullmax and 0 or 1) and (self:CanDivide() or forced) and not self.ignorestacking then + local traysize = G.consumeables.config.card_limit + if (self.edition or {}).negative then + traysize = traysize + 1 + end + local split = copy_card(self) + local qty2 = math.min(self.ability.qty - (fullmax and 0 or 1), amount) + G.consumeables.config.card_limit = #G.consumeables.cards + 1 + split.ignorestacking = true + split.created_from_split = true + split:add_to_deck() + G.consumeables:emplace(split, nil, nil, true) + split.ability.qty = qty2 + self.ability.qty = self.ability.qty - qty2 + G.consumeables.config.card_limit = traysize + if qty2 > 1 then + split:create_stack_display() + end + split:set_cost() + self:set_cost() + play_sound('card1') + return split + end +end + +function Card:try_merge() + if self:CanStack() and not self.ignorestacking then + for _, v in pairs(G.consumeables.cards) do + + if CFG.NegativesOnly and not (v.edition or {}).negative then + -- Ignore this + elseif v ~= self and not v.nomerging and not v.ignorestacking and v.config.center_key == self.config.center_key and (((v.edition or {}).type or '') == ((self.edition or {}).type or '')) and (v:getQty() < (UseStackCap and MaxStack or HardLimit)) then + local space = (UseStackCap and MaxStack or HardLimit) - (v:getQty()) + v.ability.qty = (v:getQty()) + math.min((self:getQty()), space) + v:create_stack_display() + v:juice_up(0.5, 0.5) + play_sound('card1') + v:set_cost() + if (self:getQty()) - space < 1 then + self.ignorestacking = true + self.area:remove_card(self) + self:remove() + return true + else + self.ability.qty = (self:getQty()) - space + self:set_cost() + end + end + end + end +end + +function Card:getEvalQty() + return self.cardinuse and math.min(self:getQty(), (self.bulkuse and not self.naivebulkuse) and self:getQty() or (self.OverrideBulkUseLimit or NaiveBulkUseCancel)) or self:getQty() +end + +local useconsumeref = Card.use_consumeable + +function Card:use_consumeable(area, copier) + local obj = self.config.center + local uselim = self.OverrideBulkUseLimit or NaiveBulkUseCancel + local qty = self:getQty() + if qty <= 0 then + self:setQty(1, true) + qty = 1 + end + if self.ability then + self.ability.qty_initial = qty + end + if not self.naivebulkuse and self.bulkuse and obj.bulk_use and type(obj.bulk_use) == 'function' then + set_consumeable_usage(self, qty) + return obj:bulk_use(self, area, copier, qty) + elseif not self.naivebulkuse and self.ability.consumeable.hand_type then + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize(self.ability.consumeable.hand_type, 'poker_hands'),chips = G.GAME.hands[self.ability.consumeable.hand_type].chips, mult = G.GAME.hands[self.ability.consumeable.hand_type].mult, level=G.GAME.hands[self.ability.consumeable.hand_type].level}) + level_up_hand(copier or self, self.ability.consumeable.hand_type, nil, qty) + update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''}) + set_consumeable_usage(self, qty) + elseif not self.naivebulkuse and self.ability.name == 'Black Hole' then + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize('k_all_hands'),chips = '...', mult = '...', level=''}) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function() + play_sound('tarot1') + self:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = true + return true end })) + update_hand_text({delay = 0}, {mult = '+', StatusText = true}) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function() + play_sound('tarot1') + self:juice_up(0.8, 0.5) + return true end })) + update_hand_text({delay = 0}, {chips = '+', StatusText = true}) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function() + play_sound('tarot1') + self:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = nil + return true end })) + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.9, delay = 0}, {level='+' .. qty}) + delay(1.3) + for k, v in pairs(G.GAME.hands) do + level_up_hand(self, k, true, qty) + end + update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''}) + set_consumeable_usage(self, qty) + else + Incantation.accelerate = qty > AccelerateThreshold or self.force_incantation_acceleration + self.cardinuse = true + Incantation.consumable_in_use = true + local lim = math.min(qty, uselim) + local newqty = math.max(0, qty - lim) + if not self.ability.qty then self.ability.qty = 1 end + for i = 1, lim do + useconsumeref(self,area,copier) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + delay = 0.1, + blockable = true, + func = function() + self.ability.qty = self.ability.qty - 1 + play_sound('button', self.ability.qty <= 0 and 1 or 0.85, 0.7) + return true + end + })) + end + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + blockable = true, + func = function() + Incantation.consumable_in_use = false + Incantation.accelerate = false + self.cardinuse = nil + self.bulkuse = nil + self.naivebulkuse = nil + self.OverrideBulkUseLimit = nil + if obj.keep_on_use and obj:keep_on_use(self) then + self.ignorestacking = false + self.ability.qty = obj.keep_on_use_retain_stack and qty or (newqty + 1) + else + if newqty > 0 then + self:split(newqty, true, true) + end + end + return true + end + })) + end +end + +local startdissolveref = Card.start_dissolve +function Card:start_dissolve(a,b,c,d) + if self.ability.qty and self.ability.qty > 1 and Incantation.consumable_in_use and self.cardinuse and not self.ignore_incantation_consumable_in_use then return end + self.ignorestacking = true + return startdissolveref(self,a,b,c,d) +end + +local usecardref = G.FUNCS.use_card + +function CanUseStackButtons() + if ((G.play and #G.play.cards > 0) or (G.CONTROLLER.locked) or (G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) and G.STATE ~= G.STATES.HAND_PLAYED and G.STATE ~= G.STATES.DRAW_TO_HAND and G.STATE ~= G.STATES.PLAY_TAROT then + return false + end + return true +end + +G.FUNCS.use_card = function(e, mute, nosave) + local card = e.config.ref_table + local useamount = card.bulkuse and card:getmaxuse() or 1 + if ((card.ability or {}).qty or 1) > useamount then + card.highlighted = false + card.bulkuse = false + card.OverrideBulkUseLimit = useamount + end + usecardref(e, mute, nosave) +end + +G.FUNCS.can_split_card = function(e) + local card = e.config.ref_table + local splitone = e.config.issplitone + if (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then + e.config.colour = splitone and G.C.GREEN or G.C.PURPLE + e.config.button = splitone and 'split_one' or 'split_half' + e.states.visible = true + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + e.states.visible = false + end +end + +function Card:MergeAvailable() + if ((self.config or {}).center or {}).set ~= 'Joker' and ((self.config or {}).center or {}).set ~= 'Booster' then + for k, v in pairs(G.consumeables.cards) do + if v then + if v ~= self and (v.config or {}).center_key == (self.config or {}).center_key and ((v.edition or {}).type or '') == ((self.edition or {}).type or '') then + return true + end + end + end + end +end + +G.FUNCS.can_merge_card = function(e) + local card = e.config.ref_table + if card:CanStack() and card.highlighted and not card.ignorestacking and CanUseStackButtons() and card:MergeAvailable() then + e.config.colour = G.C.BLUE + e.config.button = 'merge_card' + e.states.visible = true + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + e.states.visible = false + end +end + +G.FUNCS.can_use_all = function(e) + local card = e.config.ref_table + local obj = card.config.center + if card:CanBulkUse() and ((tablecontains(BulkUsable, card.ability.set) or tablecontains(BulkUsableIndividual, card.config.center_key)) or (obj.bulk_use and type(obj.bulk_use) == 'function')) and (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then + e.config.colour = G.C.DARK_EDITION + e.config.button = 'use_all' + e.states.visible = true + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + e.states.visible = false + end +end + +G.FUNCS.can_use_every_planet = function(e) + local card = e.config.ref_table + local obj = card.config.center + if (((card.config or {}).center or {}).set or '') == 'Planet' and card:CanBulkUse() and CanUseStackButtons() and not card.ignorestacking and not obj.ignore_allplanets then + e.config.colour = G.C.SECONDARY_SET.Planet + e.config.button = 'use_every_planet' + e.states.visible = true + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + e.states.visible = false + end +end + +G.FUNCS.can_use_naivebulk = function(e) + local card = e.config.ref_table + local obj = card.config.center + if (card:CanBulkUse() or CFG.UnsafeMode) and (CFG.UnsafeMode or not obj.bulk_use or type(obj.bulk_use) ~= 'function') and (card:getQty()) > 1 and card.highlighted and CanUseStackButtons() and not card.ignorestacking then + e.config.colour = G.C.BLACK + e.config.button = 'use_naivebulk' + e.states.visible = true + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + e.states.visible = false + end +end + +G.FUNCS.split_half = function(e) + local card = e.config.ref_table + card:split(math.floor(card.ability.qty / 2)) +end + +G.FUNCS.split_one = function(e) + local card = e.config.ref_table + card:split(1) +end + +G.FUNCS.merge_card = function(e) + local card = e.config.ref_table + card:try_merge() +end + +G.FUNCS.use_all = function(e) + local card = e.config.ref_table + local obj = card.config.center + if card:CanBulkUse() and (not obj.can_use or obj:can_use(card)) and (card:getQty()) > 1 and card.highlighted then + card.bulkuse = true + G.FUNCS.use_card(e, false, true) + end +end + +function runthrough_planets() + for i = 1, #G.play.cards do + local card = G.play.cards[i] + if card then + local obj = card.config.center + if (((card.config or {}).center or {}).set or '') == 'Planet' then + card.bulkuse = card:CanBulkUse() and math.max(1, card:getQty()) > 1 + card.force_incantation_acceleration = true + card:use_consumeable(G.consumeables) + if G.betmma_abilities then + for i = 1, #G.betmma_abilities.cards do + G.betmma_abilities.cards[i]:calculate_joker({using_consumeable = true, consumeable = card}) + end + end + for i = 1, #G.jokers.cards do + local effects = G.jokers.cards[i]:calculate_joker({using_consumeable = true, consumeable = card}) + if effects and effects.joker_repetitions then + rep_list = effects.joker_repetitions + for z=1, #rep_list do + if type(rep_list[z]) == 'table' and rep_list[z].repetitions then + for r=1, rep_list[z].repetitions do + card_eval_status_text(rep_list[z].card, 'jokers', nil, nil, nil, rep_list[z]) + G.jokers.cards[i]:calculate_joker({using_consumeable = true, consumeable = card, retrigger_joker = true}) + end + end + end + end + end + G.E_MANAGER:add_event(Event({func = function() + card.ignore_incantation_consumable_in_use = true + card:start_dissolve() + return true + end})) + end + end + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + blockable = true, + func = function() + Incantation.consumable_in_use = false + Incantation.accelerate = false + return true + end + })) + end +end + +G.FUNCS.use_every_planet = function(e) + local targets = {} + for i = 1, #G.consumeables.cards do + local card = G.consumeables.cards[i] + if card then + local obj = card.config.center + if (((card.config or {}).center or {}).set or '') == 'Planet' and (not obj.can_use or obj:can_use(card)) and not obj.ignore_allplanets then + table.insert(targets, card) + end + end + end + for i = 1, #targets do + local card = targets[i] + G.E_MANAGER:add_event(Event({func = function() + if math.ceil(i/2) == i/2 then play_sound('card1') end + card.area:remove_card(card) + G.play:emplace(card) + return true + end})) + end + G.E_MANAGER:add_event(Event({func = function() + runthrough_planets() + return true + end})) +end + +G.FUNCS.use_naivebulk = function(e) + local card = e.config.ref_table + local obj = card.config.center + if card:CanBulkUse() and (not obj.can_use or obj:can_use(card)) and (card:getQty()) > 1 and card.highlighted and CFG.UnsafeMode then + card.naivebulkuse = true + card.bulkuse = true + G.FUNCS.use_card(e, false, true) + end +end + +G.FUNCS.disablestackdisplay = function(e) + local card = e.config.ref_table + e.states.visible = ((card:getQty()) > 1 and not card.ignorestacking) or card.cardinuse +end + +function Card:create_stack_display() + if not self.children.stackdisplay and self:CanStack() and not self.playing_card then + self.children.stackdisplay = UIBox { + definition = { + n = G.UIT.ROOT, + config = { + minh = 0.6, + maxh = 1.2, + minw = 0.5, + maxw = 2, + r = 0.001, + padding = 0.1, + align = 'cm', + colour = adjust_alpha(darken(G.C.BLACK, 0.2), 0.6), + shadow = false, + func = 'disablestackdisplay', + ref_table = self + }, + nodes = { + { + n = G.UIT.T, + config = { + text = 'x', + scale = 0.4, + colour = G.C.MULT + } + }, + { + n = G.UIT.T, + config = { + ref_table = self.ability, + ref_value = 'qty', + scale = 0.4, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + config = { + align = 'cm', + bond = 'Strong', + parent = self + }, + states = { + collide = { can = false }, + drag = { can = true } + } + } + end +end + +local card_load_ref = Card.load +function Card:load(cardTable, other_card) + card_load_ref(self, cardTable, other_card) + if self.ability then + if self.ability.qty then + self:create_stack_display() + end + end +end + +local deckadd = Card.add_to_deck +function Card:add_to_deck(from_debuff) + deckadd(self, from_debuff) + if G.consumeables then + if self:CanStack() then + if not self.ignorestacking then + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + blocking = false, + func = function() + if self and self.area and self.area ~= 'shop' and self.area ~= 'pack_cards' then + self:try_merge() + end + return true + end + })) + end + self.ignorestacking = nil + end + end +end + +local hlref = Card.highlight + +function Card:highlight(is_highlighted) + local isplanet = ((self.ability or {}).set or '') == 'Planet' + if self:CanStack() and self.added_to_deck and not self.ignorestacking then + if is_highlighted then + if isplanet then + self.children.everyplanetbutton = UIBox { + definition = { + n = G.UIT.ROOT, + config = { + minh = 0.3, + maxh = 0.6, + minw = 0.3, + maxw = 4, + r = 0.08, + padding = 0.1, + align = 'cm', + colour = G.C.SECONDARY_SET.Planet, + shadow = true, + button = 'use_every_planet', + func = 'can_use_every_planet', + ref_table = self + }, + nodes = { + { + n = G.UIT.T, + config = { + text = 'MASS-USE PLANETS', + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + config = { + align = 'bmi', + offset = { + x = 0, + y = 1 + }, + bond = 'Strong', + parent = self + } + } + end + self.children.useallbutton = UIBox { + definition = { + n = G.UIT.ROOT, + config = { + minh = 0.3, + maxh = 0.6, + minw = 0.3, + maxw = 4, + r = 0.08, + padding = 0.1, + align = 'cm', + colour = G.C.DARK_EDITION, + shadow = true, + button = 'use_all', + func = 'can_use_all', + ref_table = self + }, + nodes = { + { + n = G.UIT.T, + config = { + text = 'BULK USE' .. (CFG.UnsafeMode and ' (NORMAL)' or ''), + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + config = { + align = 'bmi', + offset = { + x = 0, + y = 0.5 + }, + bond = 'Strong', + parent = self + } + } + self.children.mergebutton = UIBox { + definition = { + n = G.UIT.ROOT, + config = { + minh = 0.3, + maxh = 0.6, + minw = 0.3, + maxw = 4, + r = 0.08, + padding = 0.1, + align = 'cm', + colour = G.C.BLUE, + shadow = true, + button = 'merge_card', + func = 'can_merge_card', + ref_table = self + }, + nodes = { + { + n = G.UIT.T, + config = { + text = 'MERGE', + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + config = { + align = 'bmi', + offset = { + x = 0, + y = 1 + (isplanet and 0.5 or 0) + }, + bond = 'Strong', + parent = self + } + } + self.children.splithalfbutton = UIBox { + definition = { + n = G.UIT.ROOT, + config = { + minh = 0.3, + maxh = 0.6, + minw = 0.3, + maxw = 4, + r = 0.08, + padding = 0.1, + align = 'cm', + colour = G.C.PURPLE, + shadow = true, + button = 'split_half', + func = 'can_split_card', + ref_table = self + }, + nodes = { + { + n = G.UIT.T, + config = { + text = 'SPLIT HALF', + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + config = { + align = 'bmi', + offset = { + x = 0, + y = 1.5 + (isplanet and 0.5 or 0) + }, + bond = 'Strong', + parent = self + } + } + self.children.splitonebutton = UIBox { + definition = { + n = G.UIT.ROOT, + config = { + minh = 0.3, + maxh = 0.6, + minw = 0.3, + maxw = 4, + r = 0.08, + padding = 0.1, + align = 'cm', + issplitone = true, + colour = G.C.GREEN, + shadow = true, + button = 'split_one', + func = 'can_split_card', + ref_table = self + }, + nodes = { + { + n = G.UIT.T, + config = { + text = 'SPLIT ONE', + scale = 0.3, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + config = { + align = 'bmi', + offset = { + x = 0, + y = 2 + (isplanet and 0.5 or 0) + }, + bond = 'Strong', + parent = self + } + } + if CFG.UnsafeMode then + self.children.useallnaivebutton = UIBox { + definition = { + n = G.UIT.ROOT, + config = { + minh = 0.3, + maxh = 0.6, + minw = 0.3, + maxw = 4, + r = 0.08, + padding = 0.1, + align = 'cm', + colour = G.C.BLACK, + shadow = true, + button = 'use_naivebulk', + func = 'can_use_naivebulk', + ref_table = self + }, + nodes = { + { + n = G.UIT.T, + config = { + text = 'BULK USE (ONE-AT-A-TIME)', + scale = 0.3, + colour = G.C.RED + } + } + } + }, + config = { + align = 'bmi', + offset = { + x = 0, + y = 2.5 + (isplanet and 0.5 or 0) + }, + bond = 'Strong', + parent = self + } + } + end + else + if isplanet then + if self.children.everyplanetbutton then self.children.everyplanetbutton:remove();self.children.everyplanetbutton = nil end + end + if self.children.splithalfbutton then self.children.splithalfbutton:remove();self.children.splithalfbutton = nil end + if self.children.splitonebutton then self.children.splitonebutton:remove();self.children.splitonebutton = nil end + if self.children.mergebutton then self.children.mergebutton:remove();self.children.mergebutton = nil end + if self.children.useallbutton then self.children.useallbutton:remove();self.children.useallbutton = nil end + if CFG.UnsafeMode then + if self.children.useallnaivebutton then self.children.useallnaivebutton:remove();self.children.useallnaivebutton = nil end + end + end + end + return hlref(self,is_highlighted) +end + +local ccr = copy_card + +function copy_card(other, new_card, card_scale, playing_card, strip_edition) + local card = ccr(other, new_card, card_scale, playing_card, strip_edition) + if card then + if card:getQty() <= 0 then card:setQty(1) end + end + card.OverrideBulkUseLimit = nil + card.bulkuse = nil + card.naivebulkuse = nil + card.cardinuse = nil + card.eval_qty = nil + return card +end + +local costref = Card.set_cost +function Card:set_cost() + costref(self) + self.sell_cost = self.sell_cost * math.max(1, (self.ability or {}).qty or 1) + self.sell_cost_label = self.facing == 'back' and '?' or self.sell_cost +end + +if not (SMODS.Mods['jen'] or {}).can_load then + SMODS.Joker:take_ownership('perkeo', { + name = "Perkeo (Incantation)", + loc_vars = function(self, info_queue, center) + info_queue[#info_queue+1] = {key = 'e_negative_consumable', set = 'Edition', config = {extra = 1}} + return {vars = {center.ability.extra}} + end, + calculate = function(self, card, context) + if context.ending_shop then + if G.consumeables.cards[1] then + G.E_MANAGER:add_event(Event({ + func = function() + local total, checked, center = 0, 0, nil + for i = 1, #G.consumeables.cards do + total = total + (G.consumeables.cards[i]:getQty()) + end + local poll = pseudorandom(pseudoseed('perkeo'))*total + for i = 1, #G.consumeables.cards do + checked = checked + (G.consumeables.cards[i]:getQty()) + if checked >= poll then + center = G.consumeables.cards[i] + break + end + end + local card = copy_card(center, nil) + card.ability.qty = 1 + card:set_edition({negative = true}, true) + card:add_to_deck() + G.consumeables:emplace(card) + return true + end})) + card_eval_status_text(context.blueprint_card or card, 'extra', nil, nil, nil, {message = localize('k_duplicated_ex')}) + return {calculated = true} + end + end + end + }) +end diff --git a/Incantation/config.lua b/Incantation/config.lua new file mode 100644 index 0000000..405119a --- /dev/null +++ b/Incantation/config.lua @@ -0,0 +1,5 @@ +return { + NegativesOnly = false, + StackAnything = false, + UnsafeMode = false +} diff --git a/Incantation/localization/default.lua b/Incantation/localization/default.lua new file mode 100644 index 0000000..b03ed57 --- /dev/null +++ b/Incantation/localization/default.lua @@ -0,0 +1,9 @@ +return { + misc = { + dictionary = { + incant_negatives_only = "Stack negatives only", + incant_stack_anything = "Allow stack & divide on any consumable", + incant_unsafe_mode = 'Unsafe mode' + } + } +} \ No newline at end of file diff --git a/Incantation/lovely.toml b/Incantation/lovely.toml new file mode 100644 index 0000000..a582db3 --- /dev/null +++ b/Incantation/lovely.toml @@ -0,0 +1,22 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Acceleration in bulk use +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if (G.STATE == G.STATES.HAND_PLAYED) or (G.STATE == G.STATES.NEW_ROUND) then" +position = "at" +payload = "if (G.STATE == G.STATES.HAND_PLAYED) or (G.STATE == G.STATES.NEW_ROUND) or Incantation and Incantation.accelerate then" +match_indent = true + +# Observatory +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "Xmult_mod = G.P_CENTERS.v_observatory.config.extra" +position = "at" +payload = "Xmult_mod = G.P_CENTERS.v_observatory.config.extra^(self.ability.qty or 1)" +match_indent = true \ No newline at end of file diff --git a/JCursor/Cursor.png b/JCursor/Cursor.png new file mode 100644 index 0000000..3f38b39 Binary files /dev/null and b/JCursor/Cursor.png differ diff --git a/JCursor/Cursor1.png b/JCursor/Cursor1.png new file mode 100644 index 0000000..fb7cb9d Binary files /dev/null and b/JCursor/Cursor1.png differ diff --git a/JCursor/Cursor2.png b/JCursor/Cursor2.png new file mode 100644 index 0000000..d29d22f Binary files /dev/null and b/JCursor/Cursor2.png differ diff --git a/JCursor/CursorDrag.png b/JCursor/CursorDrag.png new file mode 100644 index 0000000..63055fd Binary files /dev/null and b/JCursor/CursorDrag.png differ diff --git a/JCursor/CursorDrag1.png b/JCursor/CursorDrag1.png new file mode 100644 index 0000000..e8f2ef8 Binary files /dev/null and b/JCursor/CursorDrag1.png differ diff --git a/JCursor/CursorDrag2.png b/JCursor/CursorDrag2.png new file mode 100644 index 0000000..a1fe6f0 Binary files /dev/null and b/JCursor/CursorDrag2.png differ diff --git a/JCursor/CursorHover.png b/JCursor/CursorHover.png new file mode 100644 index 0000000..e430feb Binary files /dev/null and b/JCursor/CursorHover.png differ diff --git a/JCursor/CursorHover1.png b/JCursor/CursorHover1.png new file mode 100644 index 0000000..6396253 Binary files /dev/null and b/JCursor/CursorHover1.png differ diff --git a/JCursor/CursorHover2.png b/JCursor/CursorHover2.png new file mode 100644 index 0000000..56eca39 Binary files /dev/null and b/JCursor/CursorHover2.png differ diff --git a/JCursor/JCursor.lua b/JCursor/JCursor.lua new file mode 100644 index 0000000..95b5f08 --- /dev/null +++ b/JCursor/JCursor.lua @@ -0,0 +1,106 @@ +--- STEAMODDED HEADER +--- MOD_NAME: J Cursor +--- MOD_ID: JCursor +--- MOD_AUTHOR: [Jie65535, MarioMak967] +--- MOD_DESCRIPTION: Custom Cursor (Mods/JCursor/Cursor.png CursorHover.png CursorDrag.png) + +---------------------------------------------- +------------MOD CODE ------------------------- + +-- The x-coordinate in the cursor's hot spot. +local HOT_X = 30 +-- The y-coordinate in the cursor's hot spot. +local HOT_Y = 6 + +local currentCursor +local function setCursor(cursor) + if currentCursor ~= cursor then + currentCursor = cursor + love.mouse.setCursor(cursor) + end +end + +local MOD_DIRECTORY = "/Mods/JCursor" +local function getCursor(state, hotx, hoty) + local filename = MOD_DIRECTORY .. "/" .. state .. ".png" + if love.filesystem.exists(filename) then + return love.mouse.newCursor(filename, hotx, hoty) + end + return nil +end + +local cursorDefault +local cursorHover +local cursorDrag +local function updateCursors() + cursorDefault = getCursor("Cursor", HOT_X, HOT_Y) + cursorHover = getCursor("CursorHover", HOT_X, HOT_Y) + cursorDrag = getCursor("CursorDrag", HOT_X, HOT_Y) + if cursorDefault then + setCursor(cursorDefault) + end +end + +-- init cursors +updateCursors() + +local function myDrag() + if cursorDrag then + setCursor(cursorDrag) + -- sendDebugMessage("drag start!") + end +end + +local function myStopDrag() + if cursorDrag and cursorDefault and currentCursor == cursorDrag then + setCursor(cursorDefault) + -- sendDebugMessage("drag stop!") + end +end + + +local hoverLevel = 0 +local function myHover() + if cursorHover and currentCursor == cursorDefault and hoverLevel == 0 then + setCursor(cursorHover) + -- sendDebugMessage("hover start!") + end + hoverLevel = hoverLevel + 1 +end + +local function myStopHover() + if hoverLevel > 0 then + hoverLevel = hoverLevel - 1 + end + if cursorHover + and cursorDefault + and currentCursor == cursorHover + and hoverLevel == 0 + then + setCursor(cursorDefault) + -- sendDebugMessage("hover stop!") + end +end + +local function injectCodeBefore(originalFunction, codeToInject) + return function(...) + codeToInject(...) + return originalFunction(...) + end +end + +Node.drag = injectCodeBefore(Node.drag, myDrag) +Node.stop_drag = injectCodeBefore(Node.stop_drag, myStopDrag) +-- Node.hover = injectCodeBefore(Node.hover, myHover) +-- Node.stop_hover = injectCodeBefore(Node.stop_hover, myStopHover) + +Card.hover = injectCodeBefore(Card.hover, myHover) +Card.stop_hover = injectCodeBefore(Card.stop_hover, myStopHover) + +-- UIElement.hover = injectCodeBefore(UIElement.hover, myHover) +-- UIElement.stop_hover = injectCodeBefore(UIElement.stop_hover, myStopHover) + +sendDebugMessage("JCursor loaded!") + +---------------------------------------------- +------------MOD CODE END---------------------- diff --git a/JCursor/README.md b/JCursor/README.md new file mode 100644 index 0000000..db7df58 --- /dev/null +++ b/JCursor/README.md @@ -0,0 +1,23 @@ +# J Cursor +Custom Cursor + +--- + +ModDir: `%AppData%\Balatro\Mods\JCursor\` + +Change the default cursor by replacing the following files: +- `Cursor.png` +- `CursorDrag.png` +- `CursorHover.png` + +Default cursors made by `@MarioMak967` + +--- + +Modify the top constant of Lua to determine the cursor hot spot +```lua +-- The x-coordinate in the cursor's hot spot. +local HOT_X = 20 +-- The y-coordinate in the cursor's hot spot. +local HOT_Y = 4 +``` \ No newline at end of file diff --git a/Steamodded/LICENSE b/Steamodded/LICENSE new file mode 100644 index 0000000..f288702 --- /dev/null +++ b/Steamodded/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/Steamodded/README.md b/Steamodded/README.md new file mode 100644 index 0000000..4d2aca8 --- /dev/null +++ b/Steamodded/README.md @@ -0,0 +1,29 @@ +# Steamodded - A Balatro ModLoader + +## Introduction + +Steamodded is a mod loader and injector for the game Balatro. Much like the [LÖVE framework](https://love2d.org/wiki/Main_Page) itself, it is built using [Lua](https://www.lua.org/). It is made with modularity and extensibility in mind, providing a large selection of APIs and other features to facilitate bringing your ideas to life. + +## Installation + +### How to Install Steamodded + +Click [here](https://github.com/Steamopollys/Steamodded/wiki). + +## How to Install a Mod + +- Navigate to your Mods directory (see the installation instructions). +- Put the mod into that directory. (The mod can be a single file if there is only one file provided, or it can be a whole folder.) +- Launch the game and enjoy! + +## Features + +Documentation for this project is currently incomplete. A collection of documentation pages and guides that are currently available can be found [here](https://github.com/Steamopollys/Steamodded/wiki). + +## Contributing + +This project is open for contribution; feel free to open a pull request. If you are adding new features, providing documentation is highly appreciated. + +## License + +This project is licensed under the GNU General Public License. This ensures that the software is free to use, modify, and distribute. For more details, click [here](https://github.com/Steamopollys/Steamodded/actions?tab=GPL-3.0-1-ov-file) diff --git a/Steamodded/TODO b/Steamodded/TODO new file mode 100644 index 0000000..455b484 --- /dev/null +++ b/Steamodded/TODO @@ -0,0 +1,29 @@ +# Documentation +## To do + + +- Challenge +- DeckSkin + +## In progress +- Enhancement + +## Done + +- Achievement +- Atlas +- Center (Joker, Consumable, Voucher, Back, Booster) +- Blind +- ObjectType +- UndiscoveredSprite +- Edition +- Language +- Sound +- Stake +- PokerHand +- Suit +- Rank +- Seal +- Sticker +- Rarity +- Tag diff --git a/Steamodded/assets/1x/default_achievements.png b/Steamodded/assets/1x/default_achievements.png new file mode 100644 index 0000000..112362f Binary files /dev/null and b/Steamodded/assets/1x/default_achievements.png differ diff --git a/Steamodded/assets/1x/mod_tags.png b/Steamodded/assets/1x/mod_tags.png new file mode 100644 index 0000000..95114c8 Binary files /dev/null and b/Steamodded/assets/1x/mod_tags.png differ diff --git a/Steamodded/assets/2x/default_achievements.png b/Steamodded/assets/2x/default_achievements.png new file mode 100644 index 0000000..5bfbaff Binary files /dev/null and b/Steamodded/assets/2x/default_achievements.png differ diff --git a/Steamodded/assets/2x/mod_tags.png b/Steamodded/assets/2x/mod_tags.png new file mode 100644 index 0000000..02879b1 Binary files /dev/null and b/Steamodded/assets/2x/mod_tags.png differ diff --git a/Steamodded/config.lua b/Steamodded/config.lua new file mode 100644 index 0000000..032ff99 --- /dev/null +++ b/Steamodded/config.lua @@ -0,0 +1,7 @@ +return { + ["no_mod_badges"] = false, + ["graphics_mipmap_level"] = 3, + ["graphics_mipmap_level_options"] = {0, 2, 4, 8}, + ["achievements"] = 1, + ["seeded_unlocks"] = false, +} diff --git a/Steamodded/icon.png b/Steamodded/icon.png new file mode 100644 index 0000000..fcddcf9 Binary files /dev/null and b/Steamodded/icon.png differ diff --git a/Steamodded/libs/json/init.lua b/Steamodded/libs/json/init.lua new file mode 100644 index 0000000..28215d7 --- /dev/null +++ b/Steamodded/libs/json/init.lua @@ -0,0 +1 @@ +return require((...) .. '.json') \ No newline at end of file diff --git a/Steamodded/libs/json/json.lua b/Steamodded/libs/json/json.lua new file mode 100644 index 0000000..54d4448 --- /dev/null +++ b/Steamodded/libs/json/json.lua @@ -0,0 +1,388 @@ +-- +-- json.lua +-- +-- Copyright (c) 2020 rxi +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy of +-- this software and associated documentation files (the "Software"), to deal in +-- the Software without restriction, including without limitation the rights to +-- use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +-- of the Software, and to permit persons to whom the Software is furnished to do +-- so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in all +-- copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +-- SOFTWARE. +-- + +local json = { _version = "0.1.2" } + +------------------------------------------------------------------------------- +-- Encode +------------------------------------------------------------------------------- + +local encode + +local escape_char_map = { + [ "\\" ] = "\\", + [ "\"" ] = "\"", + [ "\b" ] = "b", + [ "\f" ] = "f", + [ "\n" ] = "n", + [ "\r" ] = "r", + [ "\t" ] = "t", +} + +local escape_char_map_inv = { [ "/" ] = "/" } +for k, v in pairs(escape_char_map) do + escape_char_map_inv[v] = k +end + + +local function escape_char(c) + return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte())) +end + + +local function encode_nil(val) + return "null" +end + + +local function encode_table(val, stack) + local res = {} + stack = stack or {} + + -- Circular reference? + if stack[val] then error("circular reference") end + + stack[val] = true + + if rawget(val, 1) ~= nil or next(val) == nil then + -- Treat as array -- check keys are valid and it is not sparse + local n = 0 + for k in pairs(val) do + if type(k) ~= "number" then + error("invalid table: mixed or invalid key types") + end + n = n + 1 + end + if n ~= #val then + error("invalid table: sparse array") + end + -- Encode + for i, v in ipairs(val) do + table.insert(res, encode(v, stack)) + end + stack[val] = nil + return "[" .. table.concat(res, ",") .. "]" + + else + -- Treat as an object + for k, v in pairs(val) do + if type(k) ~= "string" then + error("invalid table: mixed or invalid key types") + end + table.insert(res, encode(k, stack) .. ":" .. encode(v, stack)) + end + stack[val] = nil + return "{" .. table.concat(res, ",") .. "}" + end +end + + +local function encode_string(val) + return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"' +end + + +local function encode_number(val) + -- Check for NaN, -inf and inf + if val ~= val or val <= -math.huge or val >= math.huge then + error("unexpected number value '" .. tostring(val) .. "'") + end + return string.format("%.14g", val) +end + + +local type_func_map = { + [ "nil" ] = encode_nil, + [ "table" ] = encode_table, + [ "string" ] = encode_string, + [ "number" ] = encode_number, + [ "boolean" ] = tostring, +} + + +encode = function(val, stack) + local t = type(val) + local f = type_func_map[t] + if f then + return f(val, stack) + end + error("unexpected type '" .. t .. "'") +end + + +function json.encode(val) + return ( encode(val) ) +end + + +------------------------------------------------------------------------------- +-- Decode +------------------------------------------------------------------------------- + +local parse + +local function create_set(...) + local res = {} + for i = 1, select("#", ...) do + res[ select(i, ...) ] = true + end + return res +end + +local space_chars = create_set(" ", "\t", "\r", "\n") +local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",") +local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u") +local literals = create_set("true", "false", "null") + +local literal_map = { + [ "true" ] = true, + [ "false" ] = false, + [ "null" ] = nil, +} + + +local function next_char(str, idx, set, negate) + for i = idx, #str do + if set[str:sub(i, i)] ~= negate then + return i + end + end + return #str + 1 +end + + +local function decode_error(str, idx, msg) + local line_count = 1 + local col_count = 1 + for i = 1, idx - 1 do + col_count = col_count + 1 + if str:sub(i, i) == "\n" then + line_count = line_count + 1 + col_count = 1 + end + end + error( string.format("%s at line %d col %d", msg, line_count, col_count) ) +end + + +local function codepoint_to_utf8(n) + -- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa + local f = math.floor + if n <= 0x7f then + return string.char(n) + elseif n <= 0x7ff then + return string.char(f(n / 64) + 192, n % 64 + 128) + elseif n <= 0xffff then + return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128) + elseif n <= 0x10ffff then + return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128, + f(n % 4096 / 64) + 128, n % 64 + 128) + end + error( string.format("invalid unicode codepoint '%x'", n) ) +end + + +local function parse_unicode_escape(s) + local n1 = tonumber( s:sub(1, 4), 16 ) + local n2 = tonumber( s:sub(7, 10), 16 ) + -- Surrogate pair? + if n2 then + return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000) + else + return codepoint_to_utf8(n1) + end +end + + +local function parse_string(str, i) + local res = "" + local j = i + 1 + local k = j + + while j <= #str do + local x = str:byte(j) + + if x < 32 then + decode_error(str, j, "control character in string") + + elseif x == 92 then -- `\`: Escape + res = res .. str:sub(k, j - 1) + j = j + 1 + local c = str:sub(j, j) + if c == "u" then + local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1) + or str:match("^%x%x%x%x", j + 1) + or decode_error(str, j - 1, "invalid unicode escape in string") + res = res .. parse_unicode_escape(hex) + j = j + #hex + else + if not escape_chars[c] then + decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string") + end + res = res .. escape_char_map_inv[c] + end + k = j + 1 + + elseif x == 34 then -- `"`: End of string + res = res .. str:sub(k, j - 1) + return res, j + 1 + end + + j = j + 1 + end + + decode_error(str, i, "expected closing quote for string") +end + + +local function parse_number(str, i) + local x = next_char(str, i, delim_chars) + local s = str:sub(i, x - 1) + local n = tonumber(s) + if not n then + decode_error(str, i, "invalid number '" .. s .. "'") + end + return n, x +end + + +local function parse_literal(str, i) + local x = next_char(str, i, delim_chars) + local word = str:sub(i, x - 1) + if not literals[word] then + decode_error(str, i, "invalid literal '" .. word .. "'") + end + return literal_map[word], x +end + + +local function parse_array(str, i) + local res = {} + local n = 1 + i = i + 1 + while 1 do + local x + i = next_char(str, i, space_chars, true) + -- Empty / end of array? + if str:sub(i, i) == "]" then + i = i + 1 + break + end + -- Read token + x, i = parse(str, i) + res[n] = x + n = n + 1 + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "]" then break end + if chr ~= "," then decode_error(str, i, "expected ']' or ','") end + end + return res, i +end + + +local function parse_object(str, i) + local res = {} + i = i + 1 + while 1 do + local key, val + i = next_char(str, i, space_chars, true) + -- Empty / end of object? + if str:sub(i, i) == "}" then + i = i + 1 + break + end + -- Read key + if str:sub(i, i) ~= '"' then + decode_error(str, i, "expected string for key") + end + key, i = parse(str, i) + -- Read ':' delimiter + i = next_char(str, i, space_chars, true) + if str:sub(i, i) ~= ":" then + decode_error(str, i, "expected ':' after key") + end + i = next_char(str, i + 1, space_chars, true) + -- Read value + val, i = parse(str, i) + -- Set + res[key] = val + -- Next token + i = next_char(str, i, space_chars, true) + local chr = str:sub(i, i) + i = i + 1 + if chr == "}" then break end + if chr ~= "," then decode_error(str, i, "expected '}' or ','") end + end + return res, i +end + + +local char_func_map = { + [ '"' ] = parse_string, + [ "0" ] = parse_number, + [ "1" ] = parse_number, + [ "2" ] = parse_number, + [ "3" ] = parse_number, + [ "4" ] = parse_number, + [ "5" ] = parse_number, + [ "6" ] = parse_number, + [ "7" ] = parse_number, + [ "8" ] = parse_number, + [ "9" ] = parse_number, + [ "-" ] = parse_number, + [ "t" ] = parse_literal, + [ "f" ] = parse_literal, + [ "n" ] = parse_literal, + [ "[" ] = parse_array, + [ "{" ] = parse_object, +} + + +parse = function(str, idx) + local chr = str:sub(idx, idx) + local f = char_func_map[chr] + if f then + return f(str, idx) + end + decode_error(str, idx, "unexpected character '" .. chr .. "'") +end + + +function json.decode(str) + if type(str) ~= "string" then + error("expected argument of type string, got " .. type(str)) + end + local res, idx = parse(str, next_char(str, 1, space_chars, true)) + idx = next_char(str, idx, space_chars, true) + if idx <= #str then + decode_error(str, idx, "trailing garbage") + end + return res +end + + +return json \ No newline at end of file diff --git a/Steamodded/libs/nativefs/LICENSE b/Steamodded/libs/nativefs/LICENSE new file mode 100644 index 0000000..828056f --- /dev/null +++ b/Steamodded/libs/nativefs/LICENSE @@ -0,0 +1,7 @@ +Copyright 2020 megagrump@pm.me + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Steamodded/libs/nativefs/README.md b/Steamodded/libs/nativefs/README.md new file mode 100644 index 0000000..07e594b --- /dev/null +++ b/Steamodded/libs/nativefs/README.md @@ -0,0 +1,94 @@ +# This is a Thunderstore package mirror of EngineerSmith's `nativefs` repository with an added lovely patch. + +... + +This is a re-host as previously it was taken down by the original developer. Pull requests for fixes are welcome! + +# Native filesystem for LÖVE + +nativefs replicates a subset of the [love.filesystem](https://love2d.org/wiki/love.filesystem) API, but without LÖVE's path restrictions. + +## Available functions + +### nativefs + +Links in this list point to their `love.filesystem` counterparts. All functions are designed to behave the same way as `love.filesystem`, but without the path restrictions that LÖVE imposes; i.e., they allow full access to the filesystem. + +* [nativefs.newFile](https://love2d.org/wiki/love.filesystem.newFile) +* [nativefs.newFileData](https://love2d.org/wiki/love.filesystem.newFileData) +* [nativefs.mount](https://love2d.org/wiki/love.filesystem.mount) +* [nativefs.unmount](https://love2d.org/wiki/love.filesystem.unmount) +* [nativefs.read](https://love2d.org/wiki/love.filesystem.read) +* [nativefs.write](https://love2d.org/wiki/love.filesystem.write) +* [nativefs.append](https://love2d.org/wiki/love.filesystem.append) +* [nativefs.lines](https://love2d.org/wiki/love.filesystem.lines) +* [nativefs.load](https://love2d.org/wiki/love.filesystem.load) +* [nativefs.getWorkingDirectory](https://love2d.org/wiki/love.filesystem.getWorkingDirectory) +* [nativefs.getDirectoryItems](https://love2d.org/wiki/love.filesystem.getDirectoryItems) +* [nativefs.getInfo](https://love2d.org/wiki/love.filesystem.getInfo) +* [nativefs.createDirectory](https://love2d.org/wiki/love.filesystem.createDirectory) +* [nativefs.remove](https://love2d.org/wiki/love.filesystem.remove) +* nativefs.getDirectoryItemsInfo +* nativefs.getDriveList +* nativefs.setWorkingDirectory + +#### Additional `nativefs` functions + +Functions that are not available in `love.filesystem`: + +* `nativefs.getDirectoryItemsInfo(path)` +Returns a list of items in a directory that contains not only the names, but also the information returned by `getInfo` for each item. The return value is a list of files and directories, in which each entry is a table as returned by [getInfo](https://love2d.org/wiki/love.filesystem.getInfo), with an additional `name` field for each entry. Using this function is faster than calling `getInfo` separately for each item. + +* `nativefs.getDriveList()` +Returns a table with all populated drive letters on Windows (`{ 'C:/', 'D:/', ...}`). On systems that don't use drive letters, a table with the single entry `/` is returned. + +* `nativefs.setWorkingDirectory(directory)` +Changes the working directory. + +### File + +`nativefs.newFile` returns a `File` object that provides these functions: + +* [File:open](https://love2d.org/wiki/\(File\):open) +* [File:close](https://love2d.org/wiki/\(File\):close) +* [File:read](https://love2d.org/wiki/\(File\):read) +* [File:write](https://love2d.org/wiki/\(File\):write) +* [File:lines](https://love2d.org/wiki/\(File\):lines) +* [File:isOpen](https://love2d.org/wiki/\(File\):isOpen) +* [File:isEOF](https://love2d.org/wiki/\(File\):isEOF) +* [File:getFilename](https://love2d.org/wiki/\(File\):getFilename) +* [File:getMode](https://love2d.org/wiki/\(File\):getMode) +* [File:getBuffer](https://love2d.org/wiki/\(File\):getBuffer) +* [File:setBuffer](https://love2d.org/wiki/\(File\):setBuffer) +* [File:getSize](https://love2d.org/wiki/\(File\):getSize) +* [File:seek](https://love2d.org/wiki/\(File\):seek) +* [File:tell](https://love2d.org/wiki/\(File\):tell) +* [File:flush](https://love2d.org/wiki/\(File\):flush) +* [File:type](https://love2d.org/wiki/Object:type) +* [File:typeOf](https://love2d.org/wiki/Object:typeOf) +* [File:release](https://love2d.org/wiki/Object:release) + +Function names in this list are links to their LÖVE `File` counterparts. `File` is designed to work the same way as LÖVE's `File` objects. + +## Example + +```lua +local nativefs = require("nativefs") + +-- Prints all information on files in C:\Windows +local files = nativefs.getDirectoryItemsInfo("C:/Windows") +for i = 1, #files do + if files[i].type == "file" then + local info = nativefs.getInfo("C:/Windows/" .. files[i].name) + print(i .. ": " .. files[i] .. " : Type:" .. info.type .. ", Size:" .. tostring(info.size) .. ", last modified:" .. tostring(info.modtime)) + end +end +``` +## License +Copyright 2020 megagrump@pm.me + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/Steamodded/libs/nativefs/icon.png b/Steamodded/libs/nativefs/icon.png new file mode 100644 index 0000000..20ae917 Binary files /dev/null and b/Steamodded/libs/nativefs/icon.png differ diff --git a/Steamodded/libs/nativefs/init.lua b/Steamodded/libs/nativefs/init.lua new file mode 100644 index 0000000..46d2786 --- /dev/null +++ b/Steamodded/libs/nativefs/init.lua @@ -0,0 +1 @@ +return require((...) .. '.nativefs') diff --git a/Steamodded/libs/nativefs/manifest.json b/Steamodded/libs/nativefs/manifest.json new file mode 100644 index 0000000..2908a51 --- /dev/null +++ b/Steamodded/libs/nativefs/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "nativefs", + "version_number": "1.0.0", + "website_url": "https://github.com/EngineerSmith/nativefs", + "description": "nativefs replicates a subset of the love.filesystem API, but without LÖVE's path restrictions.", + "dependencies": ["Thunderstore-lovely-0.3.0"] +} diff --git a/Steamodded/libs/nativefs/nativefs.lua b/Steamodded/libs/nativefs/nativefs.lua new file mode 100644 index 0000000..f8c2b18 --- /dev/null +++ b/Steamodded/libs/nativefs/nativefs.lua @@ -0,0 +1,495 @@ +--[[ +Copyright 2020 megagrump@pm.me + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]]-- + +-- module("nativefs", package.seeall) + +local ffi, bit = require('ffi'), require('bit') +local C = ffi.C + +local fopen, getcwd, chdir, unlink, mkdir, rmdir +local BUFFERMODE, MODEMAP +local ByteArray = ffi.typeof('unsigned char[?]') +local function _ptr(p) return p ~= nil and p or nil end -- NULL pointer to nil + +local File = { + getBuffer = function(self) return self._bufferMode, self._bufferSize end, + getFilename = function(self) return self._name end, + getMode = function(self) return self._mode end, + isOpen = function(self) return self._mode ~= 'c' and self._handle ~= nil end, +} + +function File:open(mode) + if self._mode ~= 'c' then return false, "File " .. self._name .. " is already open" end + if not MODEMAP[mode] then return false, "Invalid open mode for " .. self._name .. ": " .. mode end + + local handle = _ptr(fopen(self._name, MODEMAP[mode])) + if not handle then return false, "Could not open " .. self._name .. " in mode " .. mode end + + self._handle, self._mode = ffi.gc(handle, C.fclose), mode + self:setBuffer(self._bufferMode, self._bufferSize) + + return true +end + +function File:close() + if self._mode == 'c' then return false, "File is not open" end + C.fclose(ffi.gc(self._handle, nil)) + self._handle, self._mode = nil, 'c' + return true +end + +function File:setBuffer(mode, size) + local bufferMode = BUFFERMODE[mode] + if not bufferMode then + return false, "Invalid buffer mode " .. mode .. " (expected 'none', 'full', or 'line')" + end + + if mode == 'none' then + size = math.max(0, size or 0) + else + size = math.max(2, size or 2) -- Windows requires buffer to be at least 2 bytes + end + + local success = self._mode == 'c' or C.setvbuf(self._handle, nil, bufferMode, size) == 0 + if not success then + self._bufferMode, self._bufferSize = 'none', 0 + return false, "Could not set buffer mode" + end + + self._bufferMode, self._bufferSize = mode, size + return true +end + +function File:getSize() + -- NOTE: The correct way to do this would be a stat() call, which requires a + -- lot more (system-specific) code. This is a shortcut that requires the file + -- to be readable. + local mustOpen = not self:isOpen() + if mustOpen and not self:open('r') then return 0 end + + local pos = mustOpen and 0 or self:tell() + C.fseek(self._handle, 0, 2) + local size = self:tell() + if mustOpen then + self:close() + else + self:seek(pos) + end + return size +end + +function File:read(containerOrBytes, bytes) + if self._mode ~= 'r' then return nil, 0 end + + local container = bytes ~= nil and containerOrBytes or 'string' + if container ~= 'string' and container ~= 'data' then + error("Invalid container type: " .. container) + end + + bytes = not bytes and containerOrBytes or 'all' + bytes = bytes == 'all' and self:getSize() - self:tell() or math.min(self:getSize() - self:tell(), bytes) + + if bytes <= 0 then + local data = container == 'string' and '' or love.data.newFileData('', self._name) + return data, 0 + end + + local data = love.data.newByteData(bytes) + local r = tonumber(C.fread(data:getFFIPointer(), 1, bytes, self._handle)) + + if container == 'data' then + -- FileData from ByteData requires LÖVE 11.4+ + local ok, fd = pcall(love.filesystem.newFileData, data, self._name) + if ok then return fd end + end + + local str = data:getString() + data:release() + data = container == 'data' and love.filesystem.newFileData(str, self._name) or str + return data, r +end + +local function lines(file, autoclose) + local BUFFERSIZE = 4096 + local buffer, bufferPos = ByteArray(BUFFERSIZE), 0 + local bytesRead = tonumber(C.fread(buffer, 1, BUFFERSIZE, file._handle)) + + local offset = file:tell() + return function() + file:seek(offset) + + local line = {} + while bytesRead > 0 do + for i = bufferPos, bytesRead - 1 do + if buffer[i] == 10 then -- end of line + bufferPos = i + 1 + return table.concat(line) + end + + if buffer[i] ~= 13 then -- ignore CR + table.insert(line, string.char(buffer[i])) + end + end + + bytesRead = tonumber(C.fread(buffer, 1, BUFFERSIZE, file._handle)) + offset, bufferPos = offset + bytesRead, 0 + end + + if not line[1] then + if autoclose then file:close() end + return nil + end + return table.concat(line) + end +end + +function File:lines() + if self._mode ~= 'r' then error("File is not opened for reading") end + return lines(self) +end + +function File:write(data, size) + if self._mode ~= 'w' and self._mode ~= 'a' then + return false, "File " .. self._name .. " not opened for writing" + end + + local toWrite, writeSize + if type(data) == 'string' then + writeSize = (size == nil or size == 'all') and #data or size + toWrite = data + else + writeSize = (size == nil or size == 'all') and data:getSize() or size + toWrite = data:getFFIPointer() + end + + if tonumber(C.fwrite(toWrite, 1, writeSize, self._handle)) ~= writeSize then + return false, "Could not write data" + end + return true +end + +function File:seek(pos) + return self._handle and C.fseek(self._handle, pos, 0) == 0 +end + +function File:tell() + if not self._handle then return nil, "Invalid position" end + return tonumber(C.ftell(self._handle)) +end + +function File:flush() + if self._mode ~= 'w' and self._mode ~= 'a' then + return nil, "File is not opened for writing" + end + return C.fflush(self._handle) == 0 +end + +function File:isEOF() + return not self:isOpen() or C.feof(self._handle) ~= 0 or self:tell() == self:getSize() +end + +function File:release() + if self._mode ~= 'c' then self:close() end + self._handle = nil +end + +function File:type() return 'File' end + +function File:typeOf(t) return t == 'File' end + +File.__index = File + +----------------------------------------------------------------------------- + +local nativefs = {} +local loveC = ffi.os == 'Windows' and ffi.load('love') or C + +function nativefs.newFile(name) + if type(name) ~= 'string' then + error("bad argument #1 to 'newFile' (string expected, got " .. type(name) .. ")") + end + return setmetatable({ + _name = name, + _mode = 'c', + _handle = nil, + _bufferSize = 0, + _bufferMode = 'none' + }, File) +end + +function nativefs.newFileData(filepath) + local f = nativefs.newFile(filepath) + local ok, err = f:open('r') + if not ok then return nil, err end + + local data, err = f:read('data', 'all') + f:close() + return data, err +end + +function nativefs.mount(archive, mountPoint, appendToPath) + return loveC.PHYSFS_mount(archive, mountPoint, appendToPath and 1 or 0) ~= 0 +end + +function nativefs.unmount(archive) + return loveC.PHYSFS_unmount(archive) ~= 0 +end + +function nativefs.read(containerOrName, nameOrSize, sizeOrNil) + local container, name, size + if sizeOrNil then + container, name, size = containerOrName, nameOrSize, sizeOrNil + elseif not nameOrSize then + container, name, size = 'string', containerOrName, 'all' + else + if type(nameOrSize) == 'number' or nameOrSize == 'all' then + container, name, size = 'string', containerOrName, nameOrSize + else + container, name, size = containerOrName, nameOrSize, 'all' + end + end + + local file = nativefs.newFile(name) + local ok, err = file:open('r') + if not ok then return nil, err end + + local data, size = file:read(container, size) + file:close() + return data, size +end + +local function writeFile(mode, name, data, size) + local file = nativefs.newFile(name) + local ok, err = file:open(mode) + if not ok then return nil, err end + + ok, err = file:write(data, size or 'all') + file:close() + return ok, err +end + +function nativefs.write(name, data, size) + return writeFile('w', name, data, size) +end + +function nativefs.append(name, data, size) + return writeFile('a', name, data, size) +end + +function nativefs.lines(name) + local f = nativefs.newFile(name) + local ok, err = f:open('r') + if not ok then return nil, err end + return lines(f, true) +end + +function nativefs.load(name) + local chunk, err = nativefs.read(name) + if not chunk then return nil, err end + return loadstring(chunk, name) +end + +function nativefs.getWorkingDirectory() + return getcwd() +end + +function nativefs.setWorkingDirectory(path) + if not chdir(path) then return false, "Could not set working directory" end + return true +end + +function nativefs.getDriveList() + if ffi.os ~= 'Windows' then return { '/' } end + local drives, bits = {}, C.GetLogicalDrives() + for i = 0, 25 do + if bit.band(bits, 2 ^ i) > 0 then + table.insert(drives, string.char(65 + i) .. ':/') + end + end + return drives +end + +function nativefs.createDirectory(path) + local current = path:sub(1, 1) == '/' and '/' or '' + for dir in path:gmatch('[^/\\]+') do + current = current .. dir .. '/' + local info = nativefs.getInfo(current, 'directory') + if not info and not mkdir(current) then return false, "Could not create directory " .. current end + end + return true +end + +function nativefs.remove(name) + local info = nativefs.getInfo(name) + if not info then return false, "Could not remove " .. name end + if info.type == 'directory' then + if not rmdir(name) then return false, "Could not remove directory " .. name end + return true + end + if not unlink(name) then return false, "Could not remove file " .. name end + return true +end + +local function withTempMount(dir, fn, ...) + local mountPoint = _ptr(loveC.PHYSFS_getMountPoint(dir)) + if mountPoint then return fn(ffi.string(mountPoint), ...) end + if not nativefs.mount(dir, '__nativefs__temp__') then return false, "Could not mount " .. dir end + local a, b = fn('__nativefs__temp__', ...) + nativefs.unmount(dir) + return a, b +end + +function nativefs.getDirectoryItems(dir) + local result, err = withTempMount(dir, love.filesystem.getDirectoryItems) + return result or {} +end + +local function getDirectoryItemsInfo(path, filtertype) + local items = {} + local files = love.filesystem.getDirectoryItems(path) + for i = 1, #files do + local filepath = string.format('%s/%s', path, files[i]) + local info = love.filesystem.getInfo(filepath, filtertype) + if info then + info.name = files[i] + table.insert(items, info) + end + end + return items +end + +function nativefs.getDirectoryItemsInfo(path, filtertype) + local result, err = withTempMount(path, getDirectoryItemsInfo, filtertype) + return result or {} +end + +local function getInfo(path, file, filtertype) + local filepath = string.format('%s/%s', path, file) + return love.filesystem.getInfo(filepath, filtertype) +end + +local function leaf(p) + p = p:gsub('\\', '/') + local last, a = p, 1 + while a do + a = p:find('/', a + 1) + if a then + last = p:sub(a + 1) + end + end + return last +end + +function nativefs.getInfo(path, filtertype) + local dir = path:match("(.*[\\/]).*$") or './' + local file = leaf(path) + local result, err = withTempMount(dir, getInfo, file, filtertype) + return result or nil +end + +----------------------------------------------------------------------------- + +MODEMAP = { r = 'rb', w = 'wb', a = 'ab' } +local MAX_PATH = 4096 + +ffi.cdef([[ + int PHYSFS_mount(const char* dir, const char* mountPoint, int appendToPath); + int PHYSFS_unmount(const char* dir); + const char* PHYSFS_getMountPoint(const char* dir); + + typedef struct FILE FILE; + + FILE* fopen(const char* path, const char* mode); + size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream); + size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream); + int fclose(FILE* stream); + int fflush(FILE* stream); + size_t fseek(FILE* stream, size_t offset, int whence); + size_t ftell(FILE* stream); + int setvbuf(FILE* stream, char* buffer, int mode, size_t size); + int feof(FILE* stream); +]]) + +if ffi.os == 'Windows' then + ffi.cdef([[ + int MultiByteToWideChar(unsigned int cp, uint32_t flags, const char* mb, int cmb, const wchar_t* wc, int cwc); + int WideCharToMultiByte(unsigned int cp, uint32_t flags, const wchar_t* wc, int cwc, const char* mb, + int cmb, const char* def, int* used); + int GetLogicalDrives(void); + int CreateDirectoryW(const wchar_t* path, void*); + int _wchdir(const wchar_t* path); + wchar_t* _wgetcwd(wchar_t* buffer, int maxlen); + FILE* _wfopen(const wchar_t* path, const wchar_t* mode); + int _wunlink(const wchar_t* path); + int _wrmdir(const wchar_t* path); + ]]) + + BUFFERMODE = { full = 0, line = 64, none = 4 } + + local function towidestring(str) + local size = C.MultiByteToWideChar(65001, 0, str, #str, nil, 0) + local buf = ffi.new('wchar_t[?]', size + 1) + C.MultiByteToWideChar(65001, 0, str, #str, buf, size) + return buf + end + + local function toutf8string(wstr) + local size = C.WideCharToMultiByte(65001, 0, wstr, -1, nil, 0, nil, nil) + local buf = ffi.new('char[?]', size + 1) + C.WideCharToMultiByte(65001, 0, wstr, -1, buf, size, nil, nil) + return ffi.string(buf) + end + + local nameBuffer = ffi.new('wchar_t[?]', MAX_PATH + 1) + + fopen = function(path, mode) return C._wfopen(towidestring(path), towidestring(mode)) end + getcwd = function() return toutf8string(C._wgetcwd(nameBuffer, MAX_PATH)) end + chdir = function(path) return C._wchdir(towidestring(path)) == 0 end + unlink = function(path) return C._wunlink(towidestring(path)) == 0 end + mkdir = function(path) return C.CreateDirectoryW(towidestring(path), nil) ~= 0 end + rmdir = function(path) return C._wrmdir(towidestring(path)) == 0 end +else + BUFFERMODE = { full = 0, line = 1, none = 2 } + + ffi.cdef([[ + char* getcwd(char *buffer, int maxlen); + int chdir(const char* path); + int unlink(const char* path); + int mkdir(const char* path, int mode); + int rmdir(const char* path); + ]]) + + local nameBuffer = ByteArray(MAX_PATH) + + fopen = C.fopen + unlink = function(path) return ffi.C.unlink(path) == 0 end + chdir = function(path) return ffi.C.chdir(path) == 0 end + mkdir = function(path) return ffi.C.mkdir(path, 0x1ed) == 0 end + rmdir = function(path) return ffi.C.rmdir(path) == 0 end + + getcwd = function() + local cwd = _ptr(C.getcwd(nameBuffer, MAX_PATH)) + return cwd and ffi.string(cwd) or nil + end +end + +return nativefs diff --git a/Steamodded/localization/en-us.lua b/Steamodded/localization/en-us.lua new file mode 100644 index 0000000..453723b --- /dev/null +++ b/Steamodded/localization/en-us.lua @@ -0,0 +1,127 @@ +return { + descriptions = { + Other = { + load_success = { + text = { + 'Mod loaded', + '{C:green}successfully!' + } + }, + load_failure_d = { + text = { + 'Missing {C:attention}dependencies!', + '#1#', + } + }, + load_failure_c = { + text = { + 'Unresolved {C:attention}conflicts!', + '#1#' + } + }, + load_failure_d_c = { + text = { + 'Missing {C:attention}dependencies!', + '#1#', + 'Unresolved {C:attention}conflicts!', + '#2#' + } + }, + load_failure_o = { + text = { + '{C:attention}Outdated!{} Steamodded', + 'versions {C:money}0.9.8{} and below', + 'are no longer supported.' + } + }, + load_failure_i = { + text = { + '{C:attention}Incompatible!{} Needs version', + '#1# of Steamodded,', + 'but #2# is installed.' + } + }, + load_failure_p = { + text = { + '{C:attention}Prefix Conflict!{}', + 'This mod\'s prefix is', + 'the same as another mod\'s.', + '({C:attention}#1#{})' + } + }, + load_failure_m = { + text = { + '{C:attention}Main File Not Found!{}', + 'This mod\'s main file', + 'could not be found.', + '({C:attention}#1#{})' + } + }, + load_disabled = { + text = { + 'This mod has been', + '{C:attention}disabled!{}' + } + } + }, + Edition = { + e_negative_playing_card = { + name = "Negative", + text = { + "{C:dark_edition}+#1#{} hand size" + }, + }, + } + }, + misc = { + achievement_names = { + hidden_achievement = "???", + }, + achievement_descriptions = { + hidden_achievement = "Play more to find out!", + }, + dictionary = { + b_mods = 'Mods', + b_mods_cap = 'MODS', + b_modded_version = 'Modded Version!', + b_steamodded = 'Steamodded', + b_credits = 'Credits', + b_open_mods_dir = 'Open Mods directory', + b_no_mods = 'No mods have been detected...', + b_mod_list = 'List of Activated Mods', + b_mod_loader = 'Mod Loader', + b_developed_by = 'developed by ', + b_rewrite_by = 'Rewrite by ', + b_github_project = 'Github Project', + b_github_bugs_1 = 'You can report bugs and', + b_github_bugs_2 = 'submit contributions there.', + b_disable_mod_badges = 'Disable Mod Badges', + b_author = 'Author', + b_authors = 'Authors', + b_unknown = 'Unknown', + b_lovely_mod = '(Lovely Mod) ', + b_by = ' By: ', + b_config = "Config", + b_additions = 'Additions', + b_stickers = 'Stickers', + b_achievements = "Achievements", + b_applies_stakes_1 = 'Applies ', + b_applies_stakes_2 = '', + b_graphics_mipmap_level = "Mipmap level", + b_browse = 'Browse', + b_search_prompt = 'Search for mods', + b_search_button = 'Search', + b_seeded_unlocks = 'Seeded unlocks', + b_seeded_unlocks_info = 'Enable unlocks and discoveries in seeded runs', + ml_achievement_settings = { + 'Disabled', + 'Enabled', + 'Bypass Restrictions' + } + }, + v_dictionary = { + c_types = '#1# Types', + cashout_hidden = '...and #1# more', + }, + } +} diff --git a/Steamodded/localization/es_419.lua b/Steamodded/localization/es_419.lua new file mode 100644 index 0000000..b016289 --- /dev/null +++ b/Steamodded/localization/es_419.lua @@ -0,0 +1,117 @@ +return { + descriptions = { + Other = { + load_success = { + text = { + '¡Mod cargado', + '{C:green}con éxito{}!' + } + }, + load_failure_d = { + text = { + '¡Faltan {C:attention}dependencias{}!', + '#1#', + } + }, + load_failure_c = { + text = { + '¡Hay {C:attention}conflictos{} sin resolver!', + '#1#' + } + }, + load_failure_d_c = { + text = { + '¡Faltan {C:attention}dependencias!', + '#1#', + '¡Hay {C:attention}conflictos{} sin resolver!', + '#2#' + } + }, + load_failure_o = { + text = { + '¡Steamodded {C:attention}obsoleto{}!', + 'Las versiones por debajo de {C:money}0.9.8{}', + 'ya no tienen soporte.' + } + }, + load_failure_i = { + text = { + '{C:attention}¡Incompatible!{} Necesita la versión', + '#1# de Steamodded,', + 'pero la #2# está instalada.' + } + }, + load_failure_p = { -- To be translated + text = { + '{C:attention}Prefix Conflict!{}', + 'This mod\'s prefix is', + 'the same as another mod\'s.', + '({C:attention}#1#{})' + } + }, + load_failure_m = { -- To be translated + text = { + '{C:attention}Main File Not Found!{}', + 'This mod\'s main file', + 'could not be found.', + '({C:attention}#1#{})' + } + }, + load_disabled = { + text = { + '¡Este mod ha sido', + '{C:attention}desactivado{}!' + } + } + }, + Edition = { + e_negative_playing_card = { + name = "Negativa", + text = { + "{C:dark_edition}+#1#{} de tamaño de mano" + }, + }, + } + }, + misc = { + achievement_names = { + hidden_achievement = "???", + }, + achievement_descriptions = { + hidden_achievement = "¡Juega más para descubirlo!", + }, + dictionary = { + b_mods = 'Mods', + b_mods_cap = 'MODS', + b_modded_version = 'Modded Version!', -- To be translated + b_steamodded = 'Steamodded', + b_credits = 'Créditos', + b_open_mods_dir = 'Abrir directorio de Mods', + b_no_mods = 'No se han detectado mods...', + b_mod_list = 'Lista de Mods activos', + b_mod_loader = 'Cargador de Mods', + b_developed_by = 'desarrollado por ', + b_rewrite_by = 'Reescrito por ', + b_github_project = 'Proyecto de Github', + b_github_bugs_1 = 'Puedes reportar errores', + b_github_bugs_2 = 'y contribuir allí.', + b_disable_mod_badges = 'Desactivar insignias de mods', + b_author = 'Autor/a', + b_authors = 'Autores', + b_unknown = 'Desconocido', + b_lovely_mod = '(Lovely Mod) ', -- TODO + b_by = ' Por: ', + b_config = "Configuración", + b_additions = 'Adiciones', + b_stickers = 'Stickers', -- TODO + b_achievements = "Logros", + b_applies_stakes_1 = 'Aplica ', + b_applies_stakes_2 = '', + b_graphics_mipmap_level = "Mipmap level", -- TODO + }, + v_dictionary = { + c_types = '#1# Tipos', + cashout_hidden = '...y #1# más', + }, + } +} diff --git a/Steamodded/localization/es_ES.lua b/Steamodded/localization/es_ES.lua new file mode 100644 index 0000000..dbe46bd --- /dev/null +++ b/Steamodded/localization/es_ES.lua @@ -0,0 +1,117 @@ +return { + descriptions = { + Other = { + load_success = { + text = { + '¡Mod cargado', + '{C:green}con éxito{}!' + } + }, + load_failure_d = { + text = { + '¡Faltan {C:attention}dependencias{}!', + '#1#', + } + }, + load_failure_c = { + text = { + '¡Hay {C:attention}conflictos{} sin resolver!', + '#1#' + } + }, + load_failure_d_c = { + text = { + '¡Faltan {C:attention}dependencias!', + '#1#', + '¡Hay {C:attention}conflictos{} sin resolver!', + '#2#' + } + }, + load_failure_o = { + text = { + '¡Steamodded {C:attention}obsoleto{}!', + 'Las versiones por debajo de {C:money}0.9.8{}', + 'ya no tienen soporte.' + } + }, + load_failure_i = { + text = { + '{C:attention}¡Incompatible!{} Necesita la versión', + '#1# de Steamodded,', + 'pero la #2# está instalada.' + } + }, + load_failure_p = { -- To be translated + text = { + '{C:attention}Prefix Conflict!{}', + 'This mod\'s prefix is', + 'the same as another mod\'s.', + '({C:attention}#1#{})' + } + }, + load_failure_m = { -- To be translated + text = { + '{C:attention}Main File Not Found!{}', + 'This mod\'s main file', + 'could not be found.', + '({C:attention}#1#{})' + } + }, + load_disabled = { + text = { + '¡Este mod ha sido', + '{C:attention}desactivado{}!' + } + } + }, + Edition = { + e_negative_playing_card = { + name = "Negativa", + text = { + "{C:dark_edition}+#1#{} de tamaño de mano" + }, + }, + } + }, + misc = { + achievement_names = { + hidden_achievement = "???", + }, + achievement_descriptions = { + hidden_achievement = "¡Juega más para descubirlo!", + }, + dictionary = { + b_mods = 'Mods', + b_mods_cap = 'MODS', + b_modded_version = 'Modded Version!', -- To be translated + b_steamodded = 'Steamodded', + b_credits = 'Créditos', + b_open_mods_dir = 'Abrir directorio de Mods', + b_no_mods = 'No se han detectado mods...', + b_mod_list = 'Lista de Mods activos', + b_mod_loader = 'Cargador de Mods', + b_developed_by = 'desarrollado por ', + b_rewrite_by = 'Reescrito por ', + b_github_project = 'Proyecto de Github', + b_github_bugs_1 = 'Puedes reportar errores', + b_github_bugs_2 = 'y contribuir allí.', + b_disable_mod_badges = 'Desactivar insignias de mods', + b_author = 'Autor/a', + b_authors = 'Autores', + b_unknown = 'Desconocido', + b_lovely_mod = '(Lovely Mod) ', --TODO + b_by = ' Por: ', + b_config = "Configuración", + b_additions = 'Adiciones', + b_stickers = 'Stickers', -- TODO + b_achievements = "Logros", + b_applies_stakes_1 = 'Aplica ', + b_applies_stakes_2 = '', + b_graphics_mipmap_level = "Mipmap level", -- TODO + }, + v_dictionary = { + c_types = '#1# Tipos', + cashout_hidden = '...y #1# más', + }, + } +} diff --git a/Steamodded/localization/zh_CN.lua b/Steamodded/localization/zh_CN.lua new file mode 100644 index 0000000..f9e259d --- /dev/null +++ b/Steamodded/localization/zh_CN.lua @@ -0,0 +1,117 @@ +return { + descriptions = { + Other = { + load_success = { + text = { + '模组加载{C:green}成功!' + } + }, + load_failure_d = { + text = { + '{C:attention}依赖项{}缺失!', + '#1#' + } + }, + load_failure_c = { + text = { + '存在{C:attention}冲突项{}!', + '#1#' + } + }, + load_failure_d_c = { + text = { + '{C:attention}依赖项{}缺失!', + '#1#', + '存在{C:attention}冲突项{}!', + '#2#' + } + }, + load_failure_o = { + text = { + 'Steamodded版本{C:attention}过旧{}!', + '已不再支持', + '{C:money}0.9.8{}及以下版本' + } + }, + load_failure_i = { + text = { + '{C:attention}不兼容!', + '所需Steamodded版本为#1#', + '但当前为#2#' + } + }, + load_failure_p = { + text = { + '{C:attention}前缀冲突!{}', + '此模组的前缀和', + '另外一个模组相同!', + '({C:attention}#1#{})' + } + }, + load_failure_m = { -- To be translated + text = { + '{C:attention}Main File Not Found!{}', + 'This mod\'s main file', + 'could not be found.', + '({C:attention}#1#{})' + } + }, + load_disabled = { + text = { + '该模组', + '已被{C:attention}禁用{}!' + } + } + }, + Edition = { + e_negative_playing_card = { + name = "负片", + text = { + "手牌上限{C:dark_edition}+#1#" + }, + }, + } + }, + misc = { + achievement_names = { + hidden_achievement = "???", + }, + achievement_descriptions = { + hidden_achievement = "未发现", + }, + dictionary = { + b_mods = '模组', + b_mods_cap = '模组', + b_modded_version = '模组环境!', + b_steamodded = 'Steamodded', + b_credits = '鸣谢', + b_open_mods_dir = '打开模组目录', + b_no_mods = '未检测到任何模组……', + b_mod_list = '已启用模组列表', + b_mod_loader = '模组加载器', + b_developed_by = '作者:', + b_rewrite_by = '重写者:', + b_github_project = 'Github项目', + b_github_bugs_1 = '你可以在此汇报漏洞', + b_github_bugs_2 = '和提交贡献', + b_disable_mod_badges = '禁用模组横标', + b_author = '作者', + b_authors = '作者', + b_unknown = '未知', + b_lovely_mod = '(依赖Lovely加载器的补丁模组)', + b_by = ' 作者:', + b_config = "配置", + b_additions = '新增项目', + b_stickers = '贴纸', + b_achievements = "成就", + b_applies_stakes_1 = '', + b_applies_stakes_2 = '的限制也都起效', + b_graphics_mipmap_level = "多级渐远纹理层级", + }, + v_dictionary = { + c_types = '共有#1#种', + cashout_hidden = '……还有#1#', + }, + }, + +} diff --git a/Steamodded/lovely/achievements.toml b/Steamodded/lovely/achievements.toml new file mode 100644 index 0000000..ffc4aff --- /dev/null +++ b/Steamodded/lovely/achievements.toml @@ -0,0 +1,117 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +## Achievement API + +# fetch_achievements() +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = '''(?[\t ]*)if G\.F_NO_ACHIEVEMENTS then return end\n\n[\s\S]{4}--\|FROM LOCAL SETTINGS FILE''' +position = 'before' +# match_indent = true +line_prepend = '$indent' +payload = ''' +G.SETTINGS.ACHIEVEMENTS_EARNED = G.SETTINGS.ACHIEVEMENTS_EARNED or {} +for k, v in pairs(G.ACHIEVEMENTS) do + if not v.key then v.key = k end + for kk, vv in pairs(G.SETTINGS.ACHIEVEMENTS_EARNED) do + if G.ACHIEVEMENTS[kk] and G.ACHIEVEMENTS[kk].mod then + G.ACHIEVEMENTS[kk].earned = true + end + end +end''' + +# check_for_unlock +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''if G.GAME.challenge then return end''' +position = "after" +payload = ''' +fetch_achievements() -- Refreshes achievements +for k, v in pairs(G.ACHIEVEMENTS) do + if (not v.earned) and (v.unlock_condition and type(v.unlock_condition) == 'function') and v:unlock_condition(args) then + unlock_achievement(k) + end +end''' +match_indent = true + +# unlock_achievement() +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''if G.PROFILES[G.SETTINGS.profile].all_unlocked then return end''' +position = "at" +payload = '''if G.PROFILES[G.SETTINGS.profile].all_unlocked and (G.ACHIEVEMENTS and G.ACHIEVEMENTS[achievement_name] and not G.ACHIEVEMENTS[achievement_name].bypass_all_unlocked and SMODS.config.achievements < 3) or (SMODS.config.achievements < 3 and (G.GAME.seeded or G.GAME.challenge)) then return end''' +match_indent = true + +# unlock_achievement() - fix event queue leaking +# fixed smods achievements not unlocking due to above comment's memory leak fix +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''if G.F_NO_ACHIEVEMENTS then return end''' +position = "at" +payload = '''if G.F_NO_ACHIEVEMENTS and not (G.ACHIEVEMENTS[achievement_name] or {}).mod then return true end''' +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = '''(?[\t ]*)if G\.F_NO_ACHIEVEMENTS then return end\n[\s\S][\s\S]{16}--\|LOCAL SETTINGS FILE''' +position = 'before' +# match_indent = true +line_prepend = '$indent' +payload = ''' +if not G.ACHIEVEMENTS then fetch_achievements() end + +G.SETTINGS.ACHIEVEMENTS_EARNED[achievement_name] = true +G:save_progress() + +if G.ACHIEVEMENTS[achievement_name] and G.ACHIEVEMENTS[achievement_name].mod then + if not G.ACHIEVEMENTS[achievement_name].earned then + --|THIS IS THE FIRST TIME THIS ACHIEVEMENT HAS BEEN EARNED + achievement_set = true + G.FILE_HANDLER.force = true + end + G.ACHIEVEMENTS[achievement_name].earned = true +end + +if achievement_set then + notify_alert(achievement_name) + return true +end''' + +# create_UIBox_notify_alert +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local t_s = Sprite(0,0,1.5*(_atlas.px/_atlas.py),1.5,_atlas, _c and _c.pos or {x=3, y=0})''' +position = "before" +payload = '''if SMODS.Achievements[_achievement] then _c = SMODS.Achievements[_achievement]; _atlas = G.ASSET_ATLAS[_c.atlas] end''' +match_indent = true + +# option to allow unlocks and discoveries in seeded runs +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = 'if G\.GAME\.seeded or G\.GAME\.challenge then return end' +position = 'at' +payload = 'if not SMODS.seeded_unlocks and (G.GAME.seeded or G.GAME.challenge) then return end' + +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = 'if G\.GAME\.seeded then' +position = 'at' +payload = 'if false then' + +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = 'if G\.GAME\.challenge then' +position = 'at' +payload = 'if false then' \ No newline at end of file diff --git a/Steamodded/lovely/atlas.toml b/Steamodded/lovely/atlas.toml new file mode 100644 index 0000000..46cb0ac --- /dev/null +++ b/Steamodded/lovely/atlas.toml @@ -0,0 +1,102 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Sprite API + +# Card:set_sprites() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = 'G.ASSET_ATLAS\["centers"\]' +position = 'at' +payload = "G.ASSET_ATLAS[(G.GAME.viewed_back or G.GAME.selected_back) and ((G.GAME.viewed_back or G.GAME.selected_back)[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or (G.GAME.viewed_back or G.GAME.selected_back).atlas) or 'centers']" + +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = 'G.ASSET_ATLAS\[_center.atlas or _center.set\]' +position = 'at' +payload = ''' +G.ASSET_ATLAS[(_center.undiscovered and (_center.undiscovered[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.undiscovered.atlas)) + or (SMODS.UndiscoveredSprites[_center.set] and (SMODS.UndiscoveredSprites[_center.set][G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or SMODS.UndiscoveredSprites[_center.set].atlas)) + or _center.set] or G.ASSET_ATLAS["Joker"]''' + +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "G.ASSET_ATLAS\\['Joker'\\]" +position = 'at' +payload = "G.ASSET_ATLAS[_center[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.atlas or _center.set]" + +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = 'G.ASSET_ATLAS\[_center.set\]' +position = 'at' +payload = "G.ASSET_ATLAS[_center[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.atlas or _center.set]" + +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "(_center.set == 'Joker' and G.j_undiscovered.pos) or" +position = 'before' +payload = '(_center.undiscovered and _center.undiscovered.pos) or (SMODS.UndiscoveredSprites[_center.set] and SMODS.UndiscoveredSprites[_center.set].pos) or' +match_indent = true + +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = '''(_center.set == 'Booster' and G.booster_undiscovered.pos))''' +position = 'at' +payload = '''(_center.set == 'Booster' and G.booster_undiscovered.pos) or G.j_undiscovered.pos)''' +match_indent = true + +# get_front_spriteinfo() + +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = 'return G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(G.SETTINGS.colourblind_option and 2 or 1)], _front.pos' +position = 'at' +match_indent = true +payload = 'return G.ASSET_ATLAS[G.SETTINGS.colourblind_option and _front.hc_atlas or _front.lc_atlas or {}] or G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(G.SETTINGS.colourblind_option and 2 or 1)], _front.pos' + + +# Game:set_render_settings() +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = "G:set_render_settings()" +position = 'at' +match_indent = true +payload = "SMODS.injectObjects(SMODS.Atlas)" + + +# create_UIBox_notify_alert() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = 'G.ASSET_ATLAS["icons"]' +position = 'after' +match_indent = false +payload = ''' + local _smods_atlas = _c and ((G.SETTINGS.colourblind_option and _c.hc_atlas or _c.lc_atlas) or _c.atlas) + if _smods_atlas then + _atlas = G.ASSET_ATLAS[_smods_atlas] or _atlas + end''' + +## Hide floating ? from undiscovered types +# Card:draw() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = '''shared_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod)''' +position = 'at' +match_indent = true +payload = ''' +if (self.config.center.undiscovered and not self.config.center.undiscovered.no_overlay) or not( SMODS.UndiscoveredSprites[self.ability.set] and SMODS.UndiscoveredSprites[self.ability.set].no_overlay) then + shared_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod) +else +end''' diff --git a/Steamodded/lovely/back.toml b/Steamodded/lovely/back.toml new file mode 100644 index 0000000..02d9a4c --- /dev/null +++ b/Steamodded/lovely/back.toml @@ -0,0 +1,140 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Back API + +# Back:init() +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = "if not selected_back then selected_back = G.P_CENTERS.b_red end" +position = 'after' +match_indent = true +payload = "self.atlas = selected_back.unlocked and selected_back.atlas or nil" + +# Back:change_to() +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = "if not new_back then new_back = G.P_CENTERS.b_red end" +position = 'after' +match_indent = true +payload = "self.atlas = new_back.unlocked and new_back.atlas or nil" + +# G.FUNCS.change_viewed_back +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = "G.PROFILES[G.SETTINGS.profile].MEMORY.deck = args.to_val" +position = 'after' +match_indent = true +payload = ''' +for key, val in pairs(G.sticker_card.area.cards) do + val.children.back = false + val:set_ability(val.config.center, true) +end''' + +# Back:apply_to_run() +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = "function Back:apply_to_run()" +position = 'after' +match_indent = true +payload = ''' + local obj = self.effect.center + if obj.apply and type(obj.apply) == 'function' then + obj:apply() + end''' + +# Back:trigger_effect(args) +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = "if not args then return end" +position = 'after' +match_indent = true +payload = ''' + local obj = self.effect.center + if obj.trigger_effect and type(obj.trigger_effect) == 'function' then + local o = {obj:trigger_effect(args)} + if o then return unpack(o) end + end''' + +## Back:generate_UI + +# Localization with `unlock` field in loc_txt, same as for Jokers +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = 'if not back_config.unlock_condition then' +position = 'at' +payload = ''' +local localized_by_smods +local key_override +if back_config.locked_loc_vars and type(back_config.locked_loc_vars) == 'function' then + local res = back_config:locked_loc_vars() or {} + loc_args = res.vars or {} + key_override = res.key +end +if G.localization.descriptions.Back[key_override or back_config.key].unlock_parsed then + localize{type = 'unlocks', key = key_override or back_config.key, set = 'Back', nodes = loc_nodes, vars = loc_args} + localized_by_smods = true +end +if not back_config.unlock_condition then''' +match_indent = true +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = '''localize{type = 'descriptions', key = 'demo_locked', set = "Other", nodes = loc_nodes, vars = loc_args}''' +position = 'at' +payload = ''' +if not localized_by_smods then + localize{type = 'descriptions', key = 'demo_locked', set = "Other", nodes = loc_nodes, vars = loc_args} +end''' +match_indent = true + +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = 'loc_args = {other_name}' +position = 'at' +payload = 'loc_args = loc_args or {other_name}' +match_indent = true +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = 'loc_args = {tostring(back_config.unlock_condition.amount)}' +position = 'at' +payload = 'loc_args = loc_args or {tostring(back_config.unlock_condition.amount)}' +match_indent = true +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = 'loc_args = {other_name, colours = {get_stake_col(back_config.unlock_condition.stake)}}' +position = 'at' +payload = 'loc_args = loc_args or {other_name, colours = {get_stake_col(back_config.unlock_condition.stake)}}' +match_indent = true + +[[patches]] +[patches.pattern] +target = 'back.lua' +pattern = "if name_to_check == 'Blue Deck'*" +position = 'at' +match_indent = true +payload = ''' +local key_override +if back_config.loc_vars and type(back_config.loc_vars) == 'function' then + local res = back_config:loc_vars() or {} + loc_args = res.vars or {} + key_override = res.key +elseif name_to_check == 'Blue Deck' then loc_args = {effect_config.hands}''' + +[[patches]] +[patches.regex] +target = 'back.lua' +pattern = "key = back_config\\.key" +position = 'at' +payload = "key = key_override or back_config.key" \ No newline at end of file diff --git a/Steamodded/lovely/blind.toml b/Steamodded/lovely/blind.toml new file mode 100644 index 0000000..9464dd2 --- /dev/null +++ b/Steamodded/lovely/blind.toml @@ -0,0 +1,357 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Blind API + +## Set debuffed_by_blind, use it for Matador behavior +## Blind:debuff_card() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = 'card:set_debuff(true)' +position = 'after' +payload = "if card.debuff then card.debuffed_by_blind = true end" +match_indent = true +[[patches]] +[patches.regex] +target = 'blind.lua' +pattern = 'card:set_debuff\(true\); return end' +position = 'at' +payload = """ +card:set_debuff(true); if card.debuff then card.debuffed_by_blind = true end; return end""" + +## Card:set_debuff() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = ''' +self\.debuff = should_debuff +(?[\t ]*)end +''' +position = 'after' +payload = """if not self.debuff then self.debuffed_by_blind = false end + +""" +line_prepend = '$indent' + +## Blind functions + +# Blind:set_blind() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "G.GAME.last_blind = G.GAME.last_blind or {}" +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.blind +self.children.animatedSprite.atlas = G.ANIMATION_ATLAS[obj.atlas] or G.ANIMATION_ATLAS['blind_chips']''' + +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = '--add new debuffs' +position = 'before' +match_indent = true +payload = ''' +if not reset then + local obj = self.config.blind + if obj.set_blind and type(obj.set_blind) == 'function' then + obj:set_blind() + end +end''' + +# Blind:disable() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if self.name == 'The Water' then" +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.blind +if obj.disable and type(obj.disable) == 'function' then + obj:disable() +end''' + +# Blind:defeat() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if self.name == 'The Manacle' and not self.disabled then" +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.blind +if obj.defeat and type(obj.defeat) == 'function' then + obj:defeat() +end''' + +# Blind:debuff_card() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if self.debuff and not self.disabled and card.area ~= G.jokers then" +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.blind +if not self.disabled and obj.recalc_debuff and type(obj.recalc_debuff) == 'function' then + if obj:recalc_debuff(card, from_blind) then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + else + card:set_debuff(false) + end + return +elseif not self.disabled and obj.debuff_card and type(obj.debuff_card) == 'function' then + sendWarnMessage(("Blind object %s has debuff_card function, recalc_debuff is preferred"):format(obj.key), obj.set) + if obj:debuff_card(card, from_blind) then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + else + card:set_debuff(false) + end + return +end''' + +# Blind:stay_flipped() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if area == G.hand then" +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.blind +if obj.stay_flipped and type(obj.stay_flipped) == 'function' then + return obj:stay_flipped(area, card) +end''' + +# Blind:drawn_to_hand() +[[patches]] +[patches.regex] +target = 'blind.lua' +pattern = "(?[\t ]*)if self.name == 'Cerulean Bell' then\n" +position = 'before' +line_prepend = '$indent' +payload = ''' +local obj = self.config.blind +if obj.drawn_to_hand and type(obj.drawn_to_hand) == 'function' then + obj:drawn_to_hand() +end''' + +# Blind:debuff_hand() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if self.debuff then" +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.blind +if obj.debuff_hand and type(obj.debuff_hand) == 'function' then + return obj:debuff_hand(cards, hand, handname, check) +end''' + +# Blind:modify_hand() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if self.disabled then return mult, hand_chips, false end" +position = 'after' +match_indent = true +payload = ''' +local obj = self.config.blind +if obj.modify_hand and type(obj.modify_hand) == 'function' then + return obj:modify_hand(cards, poker_hands, text, mult, hand_chips) +end''' + +# Blind:press_play() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = 'if self.name == "The Hook" then' +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.blind +if obj.press_play and type(obj.press_play) == 'function' then + return obj:press_play() +end''' + +# Blind:get_loc_debuff_text() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = 'function Blind:get_loc_debuff_text()' +position = 'after' +match_indent = true +payload = ''' + local obj = self.config.blind + if obj.get_loc_debuff_text and type(obj.get_loc_debuff_text) == 'function' then + return obj:get_loc_debuff_text() + end''' + +# Blind:set_text() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "local loc_target = localize{type = 'raw_descriptions', key = self.config.blind.key, set = 'Blind', vars = loc_vars or self.config.blind.vars}" +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.blind +if obj.loc_vars and type(obj.loc_vars) == 'function' then + local res = obj:loc_vars() or {} + loc_vars = res.vars or {} +end''' + +# Blind:load() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = 'if G.P_BLINDS[blindTable.config_blind] then' +position = 'after' +match_indent = true +payload = ''' +if self.config.blind.atlas then + self.children.animatedSprite.atlas = G.ANIMATION_ATLAS[self.config.blind.atlas] +end''' + + +# create_UIBox_blind_choice() +# create_UIBox_round_scores_row() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = "(?[\t ]*)blind_choice.animation = AnimatedSprite\\(0,0, 1.4, 1.4, (?G.ANIMATION_ATLAS\\['blind_chips'\\]), blind_choice.config.pos\\)" +position = 'at' +root_capture = 'atlas' +payload = "G.ANIMATION_ATLAS[blind_choice.config.atlas] or G.ANIMATION_ATLAS['blind_chips']" + +# create_UIBox_your_collection_blinds() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = "(?[\t ]*)local temp_blind = AnimatedSprite\\(0,0,1.3,1.3, G.ANIMATION_ATLAS\\['blind_chips'\\], discovered and v.pos or G.b_undiscovered.pos\\)" +position = 'at' +payload = ''' + +local s = 1.3 +if math.ceil(#blind_tab/6) > 6 then + s = s * 6/math.ceil(#blind_tab/6) +end +local temp_blind = AnimatedSprite(0,0,s,s, G.ANIMATION_ATLAS[discovered and v.atlas or 'blind_chips'], discovered and v.pos or G.b_undiscovered.pos)''' +line_prepend = '$indent' + +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = 'blind_matrix[math.ceil((k-1)/5+0.001)][1+((k-1)%5)] = {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={' +match_indent = true +position = 'at' +payload = ''' +local blinds_per_row = math.ceil(#blind_tab / 6) +local row = math.ceil((k - 1) / blinds_per_row + 0.001) +table.insert(blind_matrix[row], { + n = G.UIT.C, + config = { align = "cm", padding = 0.1 }, + nodes = { + ((k - blinds_per_row) % (2 * blinds_per_row) == 1) and { n = G.UIT.B, config = { h = 0.2, w = 0.5 } } or nil, + { n = G.UIT.O, config = { object = temp_blind, focus_with_object = true } }, + ((k - blinds_per_row) % (2 * blinds_per_row) == 0) and { n = G.UIT.B, config = { h = 0.2, w = 0.5 } } or nil, + } +})''' + +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '[\t ]*\(k==6 or k ==16 or k == 26\) and \{n=G.UIT.B, config=\{h=0.2,w=0.5\}\} or nil,\n[\t ]*\{n=G.UIT.O, config=\{object = temp_blind, focus_with_object = true\}\},\n[\t ]*\(k==5 or k ==15 or k == 25\) and \{n=G.UIT.B, config=\{h=0.2,w=0.5\}\} or nil,\n[\t ]*\}\}' +position = 'at' +payload = '' + +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = 'table.sort(blind_tab, function (a, b) return a.order < b.order end)' +match_indent = true +position = 'at' +payload = ''' +table.sort(blind_tab, function(a, b) return a.order + (a.boss and a.boss.showdown and 1000 or 0) < b.order + (b.boss and b.boss.showdown and 1000 or 0) end)''' + +# add_round_eval_row() +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "local blind_sprite = AnimatedSprite(0, 0, 1.2,1.2, G.ANIMATION_ATLAS['blind_chips'], copy_table(G.GAME.blind.pos))" +match_indent = true +position = 'at' +payload = ''' +local obj = G.GAME.blind.config.blind +local blind_sprite = AnimatedSprite(0, 0, 1.2, 1.2, G.ANIMATION_ATLAS[obj.atlas] or G.ANIMATION_ATLAS['blind_chips'], copy_table(G.GAME.blind.pos))''' + +# create_UIBox_blind_choice() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = "local loc_target = localize{type = 'raw_descriptions', key = blind_choice.config.key, set = 'Blind', vars = {localize(G.GAME.current_round.most_played_poker_hand, 'poker_hands')}}" +match_indent = true +position = 'at' +payload = ''' +local loc_vars = nil +if blind_choice.config.name == 'The Ox' then + loc_vars = {localize(G.GAME.current_round.most_played_poker_hand, 'poker_hands')} +end +local obj = blind_choice.config +if obj.loc_vars and _G['type'](obj.loc_vars) == 'function' then + local res = obj:loc_vars() or {} + loc_vars = res.vars or {} +end +local loc_target = localize{type = 'raw_descriptions', key = blind_choice.config.key, set = 'Blind', vars = loc_vars or blind_choice.config.vars}''' + +# create_UIBox_blind_popup() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''local loc_target = localize{type = 'raw_descriptions', key = blind.key, set = 'Blind', vars = vars or blind.vars}''' +match_indent = true +position = 'at' +payload = ''' +local loc_vars = nil +if blind.collection_loc_vars and type(blind.collection_loc_vars) == 'function' then + local res = blind:collection_loc_vars() or {} + loc_vars = res.vars +end +local loc_target = localize{type = 'raw_descriptions', key = blind.key, set = 'Blind', vars = loc_vars or vars or blind.vars}''' + +# get_new_boss() +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = 'elseif not v.boss.showdown*' +match_indent = true +position = 'before' +payload = ''' +elseif v.in_pool and type(v.in_pool) == 'function' then + local res, options = v:in_pool() + if + ( + ((G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2) == + (v.boss.showdown or false) + ) or + (options or {}).ignore_showdown_check + then + eligible_bosses[k] = res and true or nil + end''' + +# G.UIDEF.challenge_description_tab +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = "local temp_blind = AnimatedSprite(0,0,1,1, G.ANIMATION_ATLAS['blind_chips'], v.pos)" +position = 'at' +match_indent = true +payload = "local temp_blind = AnimatedSprite(0,0,1,1, G.ANIMATION_ATLAS[v.atlas or ''] or G.ANIMATION_ATLAS['blind_chips'], v.pos)" \ No newline at end of file diff --git a/Steamodded/lovely/blind_ui.toml b/Steamodded/lovely/blind_ui.toml new file mode 100644 index 0000000..a5dac7a --- /dev/null +++ b/Steamodded/lovely/blind_ui.toml @@ -0,0 +1,150 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Allow blinds to have more than 2 lines + +# create_UIBox_blind_choice() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "if blind_state == 'Select' then blind_state = 'Current' end" +position = 'after' +payload = ''' +local blind_desc_nodes = {} +for k, v in ipairs(text_table) do + blind_desc_nodes[#blind_desc_nodes+1] = {n=G.UIT.R, config={align = "cm", maxw = 2.8}, nodes={ + {n=G.UIT.T, config={text = v or '-', scale = 0.32, colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.WHITE, shadow = not disabled}} + }} +end''' +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = ''' +(?[\t ]*)text_table\[1\] and \{n=G\.UIT\.R, config=\{align = "cm", minh = 0\.7, padding = 0\.05, minw = 2\.9}, nodes=\{ +[\t ]* text_table\[1\] and \{n=G\.UIT\.R, config=\{align = "cm", maxw = 2\.8\}, nodes=\{ +[\t ]* \{n=G\.UIT\.T, config=\{id = blind_choice\.config\.key, ref_table = \{val = ''\}, ref_value = 'val', scale = 0\.32, colour = disabled and G\.C\.UI\.TEXT_INACTIVE or G\.C\.WHITE, shadow = not disabled, func = 'HUD_blind_debuff_prefix'\}\}, +[\t ]* \{n=G\.UIT\.T, config=\{text = text_table\[1\] or '\-', scale = 0\.32, colour = disabled and G\.C\.UI\.TEXT_INACTIVE or G\.C\.WHITE, shadow = not disabled\}\} +[\t ]* \}\} or nil, +[\t ]* text_table\[2\] and \{n=G\.UIT\.R, config=\{align = "cm", maxw = 2\.8\}, nodes=\{ +[\t ]* \{n=G\.UIT\.T, config=\{text = text_table\[2\] or '\-', scale = 0\.32, colour = disabled and G\.C\.UI\.TEXT_INACTIVE or G\.C\.WHITE, shadow = not disabled\}\} +[\t ]* \}\} or nil, +[\t ]*\}\} or nil,''' +position = "at" +payload = ''' +text_table[1] and {n=G.UIT.R, config={align = "cm", minh = 0.7, padding = 0.05, minw = 2.9}, nodes = blind_desc_nodes} or nil,''' +line_prepend = '$indent' + +# create_UIBox_HUD_blind() +# Padding and contained nodes are set in G.FUNCS.HUD_blind_debuff (overrides.lua) +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = ''' +(?[\t ]*)\{n=G\.UIT\.R, config=\{align = "cm", padding = 0\.05\}, nodes=\{ +[\t ]* \{n=G\.UIT\.R, config=\{align = "cm", minh = 0\.3, maxw = 4\.2\}, nodes=\{ +[\t ]* \{n=G\.UIT\.T, config=\{ref_table = \{val = ''\}, ref_value = 'val', scale = scale\*0\.9, colour = G\.C\.UI\.TEXT_LIGHT, func = 'HUD_blind_debuff_prefix'\}\}, +[\t ]* \{n=G\.UIT\.T, config=\{ref_table = G\.GAME\.blind\.loc_debuff_lines, ref_value = 1, scale = scale\*0\.9, colour = G\.C\.UI\.TEXT_LIGHT, id = 'HUD_blind_debuff_1', func = 'HUD_blind_debuff'\}\} +[\t ]* \}\}, +[\t ]* \{n=G\.UIT\.R, config=\{align = "cm", minh = 0\.3, maxw = 4\.2\}, nodes=\{ +[\t ]* \{n=G\.UIT\.T, config=\{ref_table = G\.GAME\.blind\.loc_debuff_lines, ref_value = 2, scale = scale\*0\.9, colour = G\.C\.UI\.TEXT_LIGHT, id = 'HUD_blind_debuff_2', func = 'HUD_blind_debuff'\}\} +[\t ]* \}\}, +[\t ]*\}\},''' +position = "at" +payload = ''' +{n=G.UIT.R, config={align = "cm", id = 'HUD_blind_debuff', func = 'HUD_blind_debuff'}, nodes={}},''' +line_prepend = '$indent' + +# Blind:set_text +[[patches]] +[patches.regex] +target = "blind.lua" +pattern = """ +(?[\t ]*)self\\.loc_debuff_lines\\[1\\] = '' +[\t ]*self\\.loc_debuff_lines\\[2\\] = ''""" +position = 'at' +payload = 'EMPTY(self.loc_debuff_lines)' +line_prepend = '$indent' + +[[patches]] +[patches.pattern] +target = "blind.lua" +pattern = "for k, v in ipairs(loc_target) do" +position = 'before' +payload = 'EMPTY(self.loc_debuff_lines)' +match_indent = true + +[[patches]] +[patches.pattern] +target = "blind.lua" +pattern = "self.loc_debuff_text = self.loc_debuff_text..v..(k <= #loc_target and ' ' or '')" +position = 'after' +payload = "self.loc_debuff_lines[k] = v" +match_indent = true + +[[patches]] +[patches.regex] +target = "blind.lua" +pattern = """ +(?[\t ]*)self\\.loc_debuff_lines\\[1\\] = loc_target\\[1\\] or '' +[\t ]*self\\.loc_debuff_lines\\[2\\] = loc_target\\[2\\] or '' +""" +position = 'at' +payload = '' + +## Add a box with h=3.64 (magic number equal to the height of HUD_blind) +## centered inside 'row_blind' +# create_UIBox_HUD +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = """{n=G.UIT.R, config={align = "cm", id = 'row_blind', minw = 1, minh = 3.75}, nodes={}},""" +position = 'at' +payload = """{n=G.UIT.R, config={align = "cm", id = 'row_blind', minw = 1, minh = 3.75}, nodes={ + {n=G.UIT.B, config={w=0, h=3.64, id = 'row_blind_bottom'}, nodes={}} +}},""" +match_indent = true + +## Blind UI's bottom edge is aligned to it +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "config = {major = G.HUD:get_UIE_by_ID('row_blind'), align = 'cm', offset = {x=0,y=-10}, bond = 'Weak'}" +position = 'at' +payload = "config = {major = G.HUD:get_UIE_by_ID('row_blind_bottom'), align = 'bmi', offset = {x=0,y=-10}, bond = 'Weak'}" +match_indent = true + +## Patch G.GAME.blind:juice_up() across all files + +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = ''' +(?[\t ]*)G\.HUD_blind:get_UIE_by_ID\('HUD_blind_debuff_1'\):juice_up\(0\.3, 0\) +[\t ]*G\.HUD_blind:get_UIE_by_ID\('HUD_blind_debuff_2'\):juice_up\(0\.3, 0\) +[\t ]*G\.GAME\.blind:juice_up\(\)''' +position = 'at' +payload = 'SMODS.juice_up_blind()' +line_prepend = '$indent' + +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = ''' +(?[\t ]*)G\.HUD_blind:get_UIE_by_ID\('HUD_blind_debuff_1'\):juice_up\(0\.3, 0\) +[\t ]*G\.HUD_blind:get_UIE_by_ID\('HUD_blind_debuff_2'\):juice_up\(0\.3, 0\) +[\t ]*G\.GAME\.blind:juice_up\(\)''' +position = 'at' +payload = 'SMODS.juice_up_blind()' +line_prepend = '$indent' + +# remove statically added 1 from The Wheel's collection description +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '''\(k ==1 and blind\.name == 'The Wheel' and '1' or ''\)\.\.''' +position = 'at' +payload = '' \ No newline at end of file diff --git a/Steamodded/lovely/booster.toml b/Steamodded/lovely/booster.toml new file mode 100644 index 0000000..d35cf92 --- /dev/null +++ b/Steamodded/lovely/booster.toml @@ -0,0 +1,189 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +## Booster Pack API + +# Card:open +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)if self\.ability\.name:find\('Arcana'\) then \n[\s\S]{12}G\.STATE''' +position = "before" +payload = ''' +booster_obj = self.config.center +if booster_obj and SMODS.Centers[booster_obj.key] then + G.STATE = G.STATES.SMODS_BOOSTER_OPENED + SMODS.OPENED_BOOSTER = self +end''' +line_prepend = '$indent' + +# Card:open +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)if self\.ability\.name:find\('Arcana'\) then[\t\n ]*if G\.GAME\.used_vouchers\.v_omen_globe and pseudorandom\('omen_globe'\) > 0\.8 then''' # Possibly try to target something else +position = "at" +payload = '''if booster_obj.create_card and type(booster_obj.create_card) == "function" then + local _card_to_spawn = booster_obj:create_card(self, i) + if type((_card_to_spawn or {}).is) == 'function' and _card_to_spawn:is(Card) then + card = _card_to_spawn + else + card = SMODS.create_card(_card_to_spawn) + end +elseif self.ability.name:find('Arcana') then + if G.GAME.used_vouchers.v_omen_globe and pseudorandom('omen_globe') > 0.8 then''' +line_prepend = '$indent' + +## Stop cards from boosters getting double emplaced +# Card:open +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "G.pack_cards:emplace(v)" +position = "at" +payload = "--G.pack_cards:emplace(v)" +match_indent = true + +# Game:set_globals +[[patches]] +[patches.regex] +target = "globals.lua" +pattern = '''(?[\t ]*)self\.STATES = \{''' +position = "after" +payload = ''' + + SMODS_BOOSTER_OPENED = 999,''' +line_prepend = '$indent' + +# Game:update +[[patches]] +[patches.regex] +target = "game.lua" +pattern = '''(?[\t ]*)if self\.STATE == self\.STATES\.TAROT_PACK then''' +position = "before" +payload = ''' +if G.STATE == G.STATES.SMODS_BOOSTER_OPENED then + SMODS.OPENED_BOOSTER.config.center:update_pack(dt) +end + +''' +line_prepend = '$indent' + +# G.FUNC.can_skip_booster +# TODO customize whether pack can be skipped +[[patches]] +[patches.regex] +target = "functions/button_callbacks.lua" +pattern = '''(?[\t ]*)\(G\.STATE == G\.STATES\.PLANET_PACK or G\.STATE == G\.STATES\.STANDARD_PACK''' +position = "at" +payload = '''(G.STATE == G.STATES.SMODS_BOOSTER_OPENED or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.STANDARD_PACK''' + +# CardArea:draw() +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "(self.config.type == 'deck' and self ~= G.deck) or" +position = "before" +payload = ''' +(self.config.type == 'hand' and state == G.STATES.SMODS_BOOSTER_OPENED) or''' +match_indent = true + +# G.FUNCS.use_card +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "prev_state == G.STATES.SPECTRAL_PACK or prev_state == G.STATES.STANDARD_PACK or" +position = "after" +payload = ''' +prev_state == G.STATES.SMODS_BOOSTER_OPENED or''' +match_indent = true + +# CardArea:align_cards() +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "if self.config.type == 'hand' and (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK) then" +position = "at" +payload = "if self.config.type == 'hand' and (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then" +match_indent = true + +# CardArea:align_cards() +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "if self.config.type == 'hand' and not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK) then" +position = "at" +payload = "if self.config.type == 'hand' and not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then" +match_indent = true + +# Card:can_use_consumable() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK then" +position = "at" +payload = "if G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED then" +match_indent = true + +# G.FUNC.use_card() +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "if G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SPECTRAL_PACK then" +position = "at" +payload = "if G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED then" +match_indent = true + +# G.FUNC.use_card() +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "(G.STATE == G.STATES.BUFFOON_PACK and G.STATES.BUFFOON_PACK) or" +position = "before" +payload = "(G.STATE == G.STATES.SMODS_BOOSTER_OPENED and G.STATES.SMODS_BOOSTER_OPENED) or" +match_indent = true + +# G.FUNC.use_card() +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK) and" +position = "at" +payload = "if not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and" +match_indent = true + +# Card:use_consumeable() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)align = \(G\.STATE[\s\S]*and -0\.2 or 0},''' +position = "at" +payload = ''' +align = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and 'tm' or 'cm', +offset = {x = 0, y = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and -0.2 or 0},''' +line_prepend = '$indent' + +# G.FUNCS.use_card() +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "e.config.ref_table:redeem()" +position = "before" +payload = "if area == G.pack_cards then e.config.ref_table.cost = 0 end" +match_indent = true + +## Stopping ease_dollars anim from playing when voucher is free +# Card:redeem() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)ease_dollars\(-self\.cost\)\n[\s\S]{8}inc_career_stat\('c_shop_dollars_spent', self\.cost\)''' +position = "at" +payload = ''' +if self.cost ~= 0 then + ease_dollars(-self.cost) + inc_career_stat('c_shop_dollars_spent', self.cost) +end''' +line_prepend = '$indent' \ No newline at end of file diff --git a/Steamodded/lovely/center.toml b/Steamodded/lovely/center.toml new file mode 100644 index 0000000..5c1eca8 --- /dev/null +++ b/Steamodded/lovely/center.toml @@ -0,0 +1,487 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Center API + +# Card:set_ability() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = "(?[\t ]*)if not G\\.OVERLAY_MENU then \n" +position = 'before' +payload = ''' +local obj = self.config.center +if obj.set_ability and type(obj.set_ability) == 'function' then + obj:set_ability(self, initial, delay_sprites) +end + +''' +line_prepend = '$indent' +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self.ability.bonus = (self.ability.bonus or 0) + (center.config.bonus or 0)" +position = "after" +payload = """ +for k, v in pairs(center.config) do + if k ~= 'bonus' then + if type(v) == 'table' then + self.ability[k] = copy_table(v) + else + self.ability[k] = v + end + end +end""" +match_indent = true + +# Card:calculate_joker() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'if self.ability.set == "Planet" and not self.debuff then' +position = 'before' +payload = ''' +local obj = self.config.center +if self.ability.set ~= "Enhanced" and obj.calculate and type(obj.calculate) == 'function' then + local o, t = obj:calculate(self, context) + if o or t then return o, t end +end''' +match_indent = true + +# Card:update() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = 'if G.STAGE == G.STAGES.RUN then' +position = 'before' +match_indent = true +payload = ''' +local obj = self.config.center +if obj.update and type(obj.update) == 'function' then + obj:update(self, dt) +end''' + +# Card:generate_UIBox_ability_table() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "(?else)\n[\t ]*if self.ability.name == 'Loyalty Card' then\n[\t ]*self.ability.loyalty_remaining" +root_capture = 'else' +position = 'at' +payload = 'elseif context.joker_main then' + + +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = 'return generate_card_ui(self.config.center, nil, loc_vars, card_type, badges, hide_desc, main_start, main_end)' +position = 'at' +match_indent = true +payload = 'return generate_card_ui(self.config.center, nil, loc_vars, card_type, badges, hide_desc, main_start, main_end, self)' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "full_UI_table.name = localize{type = 'name', set = _c.set, key = _c.key, nodes = full_UI_table.name}" +position = 'at' +match_indent = true +payload = ''' +if not _c.generate_ui or type(_c.generate_ui) ~= 'function' then + full_UI_table.name = localize{type = 'name', set = _c.set, key = _c.key, nodes = full_UI_table.name} +end''' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "elseif specific_vars and specific_vars.debuffed then" +position = 'before' +match_indent = true +payload = ''' +elseif _c.generate_ui and type(_c.generate_ui) == 'function' then + _c:generate_ui(info_queue, card, desc_nodes, specific_vars, full_UI_table) + if specific_vars and specific_vars.pinned then info_queue[#info_queue+1] = {key = 'pinned_left', set = 'Other'} end + if specific_vars and specific_vars.sticker then info_queue[#info_queue+1] = {key = string.lower(specific_vars.sticker)..'_sticker', set = 'Other'} end''' + +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = "(?[\t ]+)if (?_c.name == 'Golden Ticket' then)" +line_prepend = '$indent' +position = 'at' +payload = ''' +if _c.locked_loc_vars and type(_c.locked_loc_vars) == 'function' then + local res = _c:locked_loc_vars(info_queue) or {} + loc_vars = res.vars or {} + specific_vars = specific_vars or {} + specific_vars.not_hidden = res.not_hidden or specific_vars.not_hidden +elseif $rest''' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +position = 'at' +match_indent = true +pattern = 'elseif desc_nodes ~= full_UI_table.main then' +payload = 'elseif desc_nodes ~= full_UI_table.main and not desc_nodes.name then' + +# check_for_unlock() +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = "(?[\t ]*)if not card.unlocked and card.unlock_condition and args.type == 'career_stat' then" +line_prepend = '$indent' +position = 'before' +payload = ''' + +local custom_check +if not card.unlocked and card.check_for_unlock and type(card.check_for_unlock) == 'function' then + ret = card:check_for_unlock(args) + if ret then unlock_card(card) end + custom_check = true +end''' + +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = "(?[\t ]*)if(? )not card.unlocked and card.unlock_condition and args.type == 'career_stat' then" +position = 'at' +root_capture = 'a' +payload = ' not custom_check and ' + +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = "(?[\t ]*)if(? )not card.unlocked and card.unlock_condition and card.unlock_condition.type == args.type then" +position = 'at' +root_capture = 'a' +payload = ' not custom_check and ' + +#Card:use_consumable() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "(?[\t ]*)if self.ability.consumeable.mod_conv or self.ability.consumeable.suit_conv then" +line_prepend = '$indent' +position = 'before' +payload = ''' +local obj = self.config.center +if obj.use and type(obj.use) == 'function' then + obj:use(self, area, copier) + return +end''' + +# Card:can_use_consumable() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "(?[\t ]*)if self.ability.name == 'The Hermit' or self.ability.consumeable.hand_type" +line_prepend = '$indent' +position = 'before' +payload = ''' +local obj = self.config.center +if obj.can_use and type(obj.can_use) == 'function' then + return obj:can_use(self) +end''' + +# G.UIDEF.card_h_popup() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = "(?[\t ]*)(?if AUT.badges.card_type or AUT.badges.force_rarity then)\n[\t ]*(?.*)\n[\t ]*end" +line_prepend = '$indent' +position = 'at' +payload = ''' +local obj = card.config.center +$if + if obj and (obj.set_card_type_badge or obj.type and obj.type.set_card_type_badge) then + if obj.type and type(obj.type.set_card_type_badge) == 'function' then + obj.type:set_card_type_badge(obj, card, badges) + end + if type(obj.set_card_type_badge) == 'function' then + obj:set_card_type_badge(card, badges) + end + else + $rest + end +end +if obj and obj.set_badges and type(obj.set_badges) == 'function' then + obj:set_badges(card, badges) +end''' + +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = "(?[\t ]*)if AUT.badges then\n([\t ]*.*\n){4}[\t ]*end" +line_prepend = '$indent' +position = 'after' +payload = ''' +if AUT.card_type ~= 'Locked' and AUT.card_type ~= 'Undiscovered' then + SMODS.create_mod_badges(card.config.center, badges) + if card.base then + SMODS.create_mod_badges(SMODS.Ranks[card.base.value], badges) + SMODS.create_mod_badges(SMODS.Suits[card.base.suit], badges) + end + if card.config and card.config.tag then + SMODS.create_mod_badges(SMODS.Tags[card.config.tag.key], badges) + end + badges.mod_set = nil +end''' + +# set_discover_tallies() +[[patches]] +[patches.regex] +target = 'functions/misc_functions.lua' +pattern = "(?[\t ]*)if v.set == 'Planet' then(\n[\t ]*.*){15}" +line_prepend = '$indent' +position = 'at' +payload = ''' +local tally = G.DISCOVER_TALLIES[v.set:lower()..'s'] +if tally then + tally.of = tally.of + 1 + if v.discovered then + tally.tally = tally.tally + 1 + end +end''' + +[[patches]] +[patches.regex] +target = 'functions/misc_functions.lua' +pattern = "[\t ]*tarots = \\{tally = 0, of = 0\\},\n(.*\n){2}" +line_prepend = '$indent' +position = 'at' +payload = '' + +[[patches]] +[patches.regex] +target = 'functions/misc_functions.lua' +pattern = "(?[\t ]*)for _, v in pairs\\(G.DISCOVER_TALLIES\\) do" +line_prepend = '$indent' +position = 'before' +payload = ''' +for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do + G.DISCOVER_TALLIES[v:lower()..'s'] = {tally = 0, of = 0} +end''' + +# create_UIBox_your_collection() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = "(?[\t ]*)local t = create_UIBox_generic_options\\(\\{ back_func = G.STAGE" +line_prepend = '$indent' +position = 'before' +payload = ''' +local consumable_nodes = {} +if #SMODS.ConsumableType.ctype_buffer <= 3 then + for _, key in ipairs(SMODS.ConsumableType.ctype_buffer) do + local id = 'your_collection_'..key:lower()..'s' + consumable_nodes[#consumable_nodes+1] = UIBox_button({button = id, label = {localize('b_'..key:lower()..'_cards')}, count = G.DISCOVER_TALLIES[key:lower()..'s'], minw = 4, id = id, colour = G.C.SECONDARY_SET[key]}) + end +else + consumable_nodes[#consumable_nodes+1] = UIBox_button({ button = 'your_collection_consumables', label = {localize('b_stat_consumables'), localize{ type = 'variable', key = 'c_types', vars = {#SMODS.ConsumableType.ctype_buffer} } }, count = G.DISCOVER_TALLIES['consumeables'], minw = 4, minh = 4, id = 'your_collection_consumables', colour = G.C.FILTER }) +end +''' + +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = "(?[\t ]*)nodes=\\{\n[\t ]*UIBox_button\\(\\{button = 'your_collection_tarots'(.*\n){3}[\t ]*}" +line_prepend = '$indent' +position = 'at' +payload = 'nodes = consumable_nodes' + +# Card:apply_to_run() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "(?[\t ]*)if center_table.name == 'Overstock'" +line_prepend = '$indent' +position = 'before' +payload = ''' +local obj = center or self.config.center +if obj.redeem and type(obj.redeem) == 'function' then + obj:redeem(self) + return +end''' + +# create_card_for_shop() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = "local total_rate = G.GAME.joker_rate + G.GAME.tarot_rate + G.GAME.planet_rate + G.GAME.playing_card_rate + G.GAME.spectral_rate" +match_indent = true +position = 'at' +payload = ''' +local total_rate = G.GAME.joker_rate + G.GAME.playing_card_rate +for _,v in ipairs(SMODS.ConsumableType.ctype_buffer) do + total_rate = total_rate + G.GAME[v:lower()..'_rate'] +end''' + +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '(?[\t ]*)for _, v in ipairs\((?
  • \{\n(.*\n){5}[\t ]*\})\) do' +line_prepend = '$indent' +position = 'at' +payload = ''' +-- need to preserve order to leave RNG unchanged +local rates = $li +for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do + if not (v == 'Tarot' or v == 'Planet' or v == 'Spectral') then + table.insert(rates, { type = v, val = G.GAME[v:lower()..'_rate'] }) + end +end +for _, v in ipairs(rates) do''' + +# create_card() +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if not forced_key and soulable and (not G.GAME.banned_keys['c_soul']) then" +match_indent = true +position = 'after' +payload = ''' + for _, v in ipairs(SMODS.Consumable.legendaries) do + if (_type == v.type.key or _type == v.soul_set) and not (G.GAME.used_jokers[v.key] and not next(find_joker("Showman")) and not v.can_repeat_soul) and (not v.in_pool or (type(v.in_pool) ~= "function") or v:in_pool()) then + if pseudorandom('soul_'..v.key.._type..G.GAME.round_resets.ante) > (1 - v.soul_rate) then + forced_key = v.key + end + end + end''' + +# Card:add_to_deck() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = '(?[\t ]*)if self.ability.h_size ~= 0 then\n[\t ]*G\.hand:change_size\(self.ability.h_size\)' +line_prepend = '$indent' +position = 'before' +payload = ''' +local obj = self.config.center +if obj and obj.add_to_deck and type(obj.add_to_deck) == 'function' then + obj:add_to_deck(self, from_debuff) +end''' + +# Card:remove_from_deck() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = '(?[\t ]*)if self.ability.h_size ~= 0 then\n[\t ]*G\.hand:change_size\(-self.ability.h_size\)' +line_prepend = '$indent' +position = 'before' +payload = ''' +local obj = self.config.center +if obj and obj.remove_from_deck and type(obj.remove_from_deck) == 'function' then + obj:remove_from_deck(self, from_debuff) +end''' + +# G.FUNCS.use_card() +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = "if card.area then card.area:remove_card(card) end" +match_indent = true +position = 'at' +payload = ''' +local nc +if card.ability.consumeable then + local obj = card.config.center + if obj.keep_on_use and type(obj.keep_on_use) == 'function' then + nc = obj:keep_on_use(card) + end +end +if not nc and card.area then card.area:remove_card(card) end''' + +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = "else draw_card(G.hand, G.play, 1, 'up', true, card, nil, mute) end" +match_indent = true +position = 'before' +payload = ''' +elseif nc then + area:remove_from_highlighted(card) + play_sound('cardSlide2', nil, 0.3) + dont_dissolve = true''' + +# Card:set_sprites() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "if not self.children.back then" +match_indent = true +position = 'at' +payload = ''' +if _center.set_sprites and type(_center.set_sprites) == 'function' then + _center:set_sprites(self, _front) +end +if true then''' + +# Card:load() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = 'if self.config.center.name == "Half Joker" then' +match_indent = true +position = 'at' +payload = ''' +local obj = self.config.center +if obj.load and type(obj.load) == 'function' then + obj:load(self, cardTable, other_card) +elseif self.config.center.name == "Half Joker" then''' + +# Card:calculate_dollar_bonus() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Cloud 9' and self.ability.nine_tally and self.ability.nine_tally > 0 then" +position = "before" +match_indent = true +payload = ''' +--asdf +local obj = self.config.center +if obj.calc_dollar_bonus and type(obj.calc_dollar_bonus) == 'function' then + return obj:calc_dollar_bonus(self) +end''' + +# Card:draw() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = '--If the card has any edition/seal, add that here' +position = 'before' +match_indent = true +payload = ''' +local center = self.config.center +if center.draw and type(center.draw) == 'function' then + center:draw(self, layer) +end +if center.set == 'Default' or center.set == 'Enhanced' and not center.replace_base_card then + if not center.no_suit then + local suit = SMODS.Suits[self.base.suit] or {} + if suit.draw and type(suit.draw) == 'function' then + suit:draw(self, layer) + end + end + if not center.no_rank then + local rank = SMODS.Ranks[self.base.value] or {} + if rank.draw and type(rank.draw) == 'function' then + rank:draw(self, layer) + end + end +end +''' + +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = 'if self.seal then' +position = 'at' +match_indent = true +payload = ''' +local seal = G.P_SEALS[self.seal or {}] or {} +if type(seal.draw) == 'function' then + seal:draw(self, layer) +elseif self.seal then +''' \ No newline at end of file diff --git a/Steamodded/lovely/challenge.toml b/Steamodded/lovely/challenge.toml new file mode 100644 index 0000000..e166a73 --- /dev/null +++ b/Steamodded/lovely/challenge.toml @@ -0,0 +1,21 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# function G.UIDEF.challenge_list_page() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "local challenge_unlocked = G.PROFILES[G.SETTINGS.profile].challenges_unlocked and (G.PROFILES[G.SETTINGS.profile].challenges_unlocked >= k)" +position = 'after' +payload = """ +if v.unlocked and type(v.unlocked) == 'function' then + challenge_unlocked = v:unlocked() +elseif type(v.unlocked) == 'boolean' then + challenge_unlocked = v.unlocked +end +challenge_unlocked = challenge_unlocked or G.PROFILES[G.SETTINGS.profile].all_unlocked + +""" +match_indent = true \ No newline at end of file diff --git a/Steamodded/lovely/compact_cashout.toml b/Steamodded/lovely/compact_cashout.toml new file mode 100644 index 0000000..3ce3b9b --- /dev/null +++ b/Steamodded/lovely/compact_cashout.toml @@ -0,0 +1,65 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + + +# +# End of round money +# + +# Hide off screen rows + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if config.name ~= 'bottom' then" +position = "after" +payload = ''' + total_cashout_rows = (total_cashout_rows or 0) + 1 + if total_cashout_rows > 7 then + return + end''' +match_indent = true + +# Reset rows amount + +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = 'G\.FUNCS\.evaluate_round = function\(\)' +position = "after" +payload = ''' + + total_cashout_rows = 0''' + +# Add UI row with total rows hidden + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "add_round_eval_row({name = 'bottom', dollars = dollars})" +position = "before" +payload = ''' +if total_cashout_rows > 7 then + local total_hidden = total_cashout_rows - 7 + + G.E_MANAGER:add_event(Event({ + trigger = 'before',delay = 0.38, + func = function() + local hidden = {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({ + string = {localize{type = 'variable', key = 'cashout_hidden', vars = {total_hidden}}}, + colours = {G.C.WHITE}, shadow = true, float = false, + scale = 0.45, + font = G.LANGUAGES['en-us'].font, pop_in = 0 + })}} + }} + + G.round_eval:add_child(hidden, G.round_eval:get_UIE_by_ID('bonus_round_eval')) + return true + end + })) +end''' +match_indent = true + diff --git a/Steamodded/lovely/compat_0_9_8.toml b/Steamodded/lovely/compat_0_9_8.toml new file mode 100644 index 0000000..aefd252 --- /dev/null +++ b/Steamodded/lovely/compat_0_9_8.toml @@ -0,0 +1,38 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# fallback for card.ability.name +# Card:set_ability() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self.ability.bonus = (self.ability.bonus or 0) + (center.config.bonus or 0)" +position = 'after' +payload = "if not self.ability.name then self.ability.name = center.key end" +match_indent = true + +# generate_card_ui() +# `card_type` is used to check whether card should be nil; non-recursive calls +# to generate_card_ui always have that arg set +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = '(?[\t ]*)function generate_card_ui\([^)]*\)\n' +position = "after" +line_prepend = '$indent' +payload = """ + if card == nil and card_type then + card = SMODS.compat_0_9_8.generate_UIBox_ability_table_card + end + +""" +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "for _, v in ipairs(info_queue) do" +position = 'before' +payload = "SMODS.compat_0_9_8.generate_UIBox_ability_table_card = nil" +match_indent = true + diff --git a/Steamodded/lovely/core.toml b/Steamodded/lovely/core.toml new file mode 100644 index 0000000..308ed41 --- /dev/null +++ b/Steamodded/lovely/core.toml @@ -0,0 +1,24 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "self.SPEEDFACTOR = 1" +position = "after" +payload = "initSteamodded()" +match_indent = true + +[[patches]] +[patches.copy] +target = "main.lua" +position = "append" +sources = ["src/core.lua"] + +[[patches]] +[patches.module] +before = "main.lua" +source = "version.lua" +name = "SMODS.version" diff --git a/Steamodded/lovely/crash_handler.toml b/Steamodded/lovely/crash_handler.toml new file mode 100644 index 0000000..078c3f0 --- /dev/null +++ b/Steamodded/lovely/crash_handler.toml @@ -0,0 +1,20 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = "function love.errhand(msg)" +position = "at" +payload = "if false then" +match_indent = true + +[[patches]] +[patches.copy] +target = "main.lua" +position = "prepend" +sources = [ + "src/crash_handler.lua", +] diff --git a/Steamodded/lovely/deck_skins.toml b/Steamodded/lovely/deck_skins.toml new file mode 100644 index 0000000..7501bfe --- /dev/null +++ b/Steamodded/lovely/deck_skins.toml @@ -0,0 +1,128 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +#========================================================# +# Choose any rank for custom deck and use provided atlas # +#========================================================# +[[patches]] +[patches.regex] +target = "functions/misc_functions.lua" +pattern = '''if _front and _front.suit and \(_front.value == 'Jack' or _front.value == 'Queen' or _front.value == 'King'\) then([\s\S]*?)end([\s\S]*?)end([\s\S]*?)end''' +position = "at" +payload = ''' +if _front and _front.suit and G.SETTINGS.CUSTOM_DECK and G.SETTINGS.CUSTOM_DECK.Collabs then + local collab = G.SETTINGS.CUSTOM_DECK.Collabs[_front.suit] + if collab and collab ~= 'default' then + local deckSkin = SMODS.DeckSkins[collab] + if deckSkin then + local hasRank = false + for i = 1, #deckSkin.ranks do + if deckSkin.ranks[i] == _front.value then hasRank = true break end + end + if hasRank then + local atlas = G.ASSET_ATLAS[G.SETTINGS.colourblind_option and deckSkin.hc_atlas or deckSkin.lc_atlas] + if atlas then + if deckSkin.posStyle == 'collab' then + return atlas, G.COLLABS.pos[_front.value] + elseif deckSkin.posStyle == 'suit' then + return atlas, { x = _front.pos.x, y = 0} + elseif deckSkin.posStyle == 'deck' then + return atlas, _front.pos + end + end + end + end + end +end +''' +#=======================# +# Extend custom deck ui # +#=======================# +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = '''local face_cards = CardArea\(([\s\S]*?)\)''' +position = "at" +payload = ''' +local rankCount = 0 +local lookup = {} +local options = G.COLLABS.options[_suit] +for i = 2, #options do + local skin = SMODS.DeckSkins[options[i]] + for j = 1, #skin.ranks do + if not lookup[skin.ranks[j]] then + lookup[skin.ranks[j]] = true + rankCount = rankCount + 1 + end + end +end + +local face_cards = CardArea( + 0,0, + math.min(math.max(rankCount*G.CARD_W*0.6, 4*G.CARD_W), 10*G.CARD_W), + 1.4*G.CARD_H, + {card_limit = rankCount, type = 'title', highlight_limit = 0}) +''' + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = '''for i = 1, 3 do([\s\S]*?)end''' +position = "at" +payload = ''' +local rank = SMODS.Ranks['2'] +local cards = {} +local smodSuit = SMODS.Suits[_suit] +repeat + if lookup[rank.key] then + local card_code = smodSuit.card_key .. '_' .. rank.card_key + local card = Card(0,0, G.CARD_W*1.2, G.CARD_H*1.2, G.P_CARDS[card_code], G.P_CENTERS.c_base) + card.no_ui = true + + cards[#cards + 1] = card + end + rank = SMODS.Ranks[rank.next[1]] +until rank == SMODS.Ranks['2'] + +for i = #cards, 1, -1 do + face_cards:emplace(cards[i]) +end +''' + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = '''function create_UIBox_customize_deck()([\s\S]*?)end''' +position = "at" +payload = ''' +function create_UIBox_customize_deck() + local suitTabs = {} + + local index = 1 + for i, suit in ipairs(SMODS.Suit:obj_list(true)) do + if G.COLLABS.options[suit.key] then + suitTabs[index] = { + label = localize(suit.key, 'suits_plural'), + tab_definition_function = G.UIDEF.custom_deck_tab, + tab_definition_function_args = suit.key + } + index = index + 1 + end + end + + if suitTabs[1] then + suitTabs[1].chosen = true + end + + local t = create_UIBox_generic_options({ back_func = 'options', snap_back = nil, contents = { + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + create_tabs( + {tabs = suitTabs, snap_to_nav = true, no_shoulders = true} + )}}} + }) + + return t +end +''' \ No newline at end of file diff --git a/Steamodded/lovely/dollar_row.toml b/Steamodded/lovely/dollar_row.toml new file mode 100644 index 0000000..06f543b --- /dev/null +++ b/Steamodded/lovely/dollar_row.toml @@ -0,0 +1,79 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Dollar row patches (API removed) + + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if num_dollars > 60 then" +position = "at" +payload = ''' +if num_dollars > 60 or num_dollars < -60 then''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "local dollar_string = localize('$')..num_dollars" +position = "at" +payload = ''' +local dollar_string +if num_dollars < 0 then --if negative + dollar_string = '-'..localize('$')..(num_dollars*-1) + G.E_MANAGER:add_event(Event({ + trigger = 'before',delay = 0.38, + func = function() + G.round_eval:add_child( + {n=G.UIT.R, config={align = "cm", id = 'dollar_row_'..(dollar_row+1)..'_'..config.name}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('$')..(num_dollars*-1)}, colours = {G.C.MONEY}, shadow = true, pop_in = 0, scale = 0.65, float = true})}} + }}, + G.round_eval:get_UIE_by_ID('dollar_'..config.name)) + play_sound('coin3', 0.9+0.2*math.random(), 0.7) + play_sound('coin6', 1.3, 0.8) + return true + end + })) +else --if positive + dollar_string = localize('$')..num_dollars''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "for i = 1, num_dollars or 1 do" +position = "at" +payload = ''' +local dollars_to_loop +if num_dollars < 0 then dollars_to_loop = num_dollars*-1 else dollars_to_loop = num_dollars end +for i = 1, dollars_to_loop do''' +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = '''(?[\t ]*)else\n[\t ]*local dollars_to_loop''' +position = "before" +line_prepend = "$indent" +payload = ''' +--asdf +end''' + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "local r = {n=G.UIT.T, config={text = localize('$'), colour = G.C.MONEY, scale = ((num_dollars > 20 and 0.28) or (num_dollars > 9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}}" +position = "at" +payload = ''' +local r +if i == 1 and num_dollars < 0 then + r = {n=G.UIT.T, config={text = '-', colour = G.C.RED, scale = ((num_dollars < -20 and 0.28) or (num_dollars < -9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}} + play_sound('coin3', 0.9+0.2*math.random(), 0.7 - (num_dollars < -20 and 0.2 or 0)) +else + if num_dollars < 0 then r = {n=G.UIT.T, config={text = localize('$'), colour = G.C.RED, scale = ((num_dollars > 20 and 0.28) or (num_dollars > 9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}} + else r = {n=G.UIT.T, config={text = localize('$'), colour = G.C.MONEY, scale = ((num_dollars > 20 and 0.28) or (num_dollars > 9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}} end +end''' +match_indent = true \ No newline at end of file diff --git a/Steamodded/lovely/edition.toml b/Steamodded/lovely/edition.toml new file mode 100644 index 0000000..9ef0081 --- /dev/null +++ b/Steamodded/lovely/edition.toml @@ -0,0 +1,517 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Fix debug mode edition cycling +[[patches]] +[patches.regex] +target = "engine/controller.lua" +pattern = ''' +(?[\t ]*)local _edition = \{ +[\t ]*foil = not _card\.edition, +[\t ]*holo = _card\.edition and _card\.edition\.foil, +[\t ]*polychrome = _card\.edition and _card\.edition\.holo, +[\t ]*negative = _card\.edition and _card\.edition\.polychrome, +[\t ]*\}''' +position = "at" +payload = ''' +local found_index = 1 +if _card.edition then + for i, v in ipairs(G.P_CENTER_POOLS.Edition) do + if v.key == _card.edition.key then + found_index = i + break + end + end +end +found_index = found_index + 1 +if found_index > #G.P_CENTER_POOLS.Edition then found_index = found_index - #G.P_CENTER_POOLS.Edition end +local _edition = G.P_CENTER_POOLS.Edition[found_index].key''' +line_prepend = "$indent" + + +# Sort P_CENTER_POOLS["Editions"] +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = 'table.sort(self.P_CENTER_POOLS["Enhanced"], function (a, b) return a.order < b.order end)' +position = 'after' +payload = 'table.sort(self.P_CENTER_POOLS["Edition"], function (a, b) return a.order < b.order end)' +match_indent = true + + +# generate_card_ui() +# Adds tooltips for all editions +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = ''' +(?[\t ]*)if v == 'foil' then info_queue\[#info_queue\+1\] = G\.P_CENTERS\['e_foil'\] end +[\t ]*if v == 'holographic' then info_queue\[#info_queue\+1\] = G\.P_CENTERS\['e_holo'\] end +[\t ]*if v == 'polychrome' then info_queue\[#info_queue\+1\] = G\.P_CENTERS\['e_polychrome'\] end +[\t ]*if v == 'negative' then info_queue\[#info_queue\+1\] = G\.P_CENTERS\['e_negative'\] end +[\t ]*if v == 'negative_consumable' then info_queue\[#info_queue\+1\] = \{key = 'e_negative_consumable', set = 'Edition', config = \{extra = 1\}\} end''' +position = 'at' +payload = ''' +v = (v == 'holographic' and 'holo' or v) +if v:sub(1,9) == 'negative_' then + info_queue[#info_queue+1] = {key = 'e_'..v, set = 'Edition', config = {extra = G.P_CENTERS['e_negative'].config.card_limit}} +end +if G.P_CENTERS[v] and G.P_CENTERS[v].set == 'Edition' then + info_queue[#info_queue + 1] = G.P_CENTERS[v] +end +if G.P_CENTERS['e_'..v] and G.P_CENTERS['e_'..v].set == 'Edition' then + info_queue[#info_queue + 1] = G.P_CENTERS['e_'..v] +end''' +line_prepend = "$indent" + +# get_badge_colour() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = 'return G.BADGE_COL[key] or {1, 0, 0, 1}' +position = 'before' +match_indent = true +payload = ''' +for _, v in ipairs(G.P_CENTER_POOLS.Edition) do + G.BADGE_COL[v.key:sub(3)] = v.badge_colour +end''' + +# Limit ARGS.send_to_shader[1] to wiggle between 0 and 2 instead of growing infinitely +# this makes shaders responsiveness on tilt reliable over time +# Card:draw() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +G\.TIMERS\.REAL/\(28\)''' +position = "at" +payload = '''math.sin(G.TIMERS.REAL/28) + 1''' + +# Allow editions to not draw shadow +# Card:draw() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +self\.ability\.effect ~= 'Glass Card' and not self\.greyed''' +position = "after" +payload = ''' and self:should_draw_shadow() ''' + +# If shader modifies shape of card, this will stop "back" layer of the card being rendered. +# Card:draw() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = ''' +elseif not self.greyed then''' +position = "before" +payload = ''' +elseif not self:should_draw_base_shader() then + -- Don't render base dissolve shader. +''' +match_indent = true + +# If shader modifies shape of card, this will stop "back" layer of the card being rendered. +# spectral cards and booster packs only. +# Card:draw() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = ''' +if self.ability.set == 'Booster' or self.ability.set == 'Spectral' then''' +position = "at" +payload = ''' +if (self.ability.set == 'Booster' or self.ability.set == 'Spectral') and self:should_draw_base_shader() then''' +match_indent = true + +# If shader modifies shape of card, this will stop "back" layer of the card being rendered. +# invisible joker and vouchers. +# Card:draw() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = ''' +self.children.center:draw_shader('voucher', nil, self.ARGS.send_to_shader)''' +position = "at" +payload = ''' +if self:should_draw_base_shader() then + self.children.center:draw_shader('voucher', nil, self.ARGS.send_to_shader) +end''' +match_indent = true + +# Inject shaders applying to cards +# Card:draw() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +(?[\t ]*)if self\.edition and self\.editi[A-z\.\:\n\t _(',)~=]*me', nil, self.ARGS.send_to_shader\) +[\t ]*end +[\t ]*end''' +position = "at" +payload = ''' +if self.edition then + for k, v in pairs(G.P_CENTER_POOLS.Edition) do + if self.edition[v.key:sub(3)] and v.shader then + if type(v.draw) == 'function' then + v:draw(self, layer) + else + self.children.center:draw_shader(v.shader, nil, self.ARGS.send_to_shader) + if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then + self.children.front:draw_shader(v.shader, nil, self.ARGS.send_to_shader) + end + end + end + end +end''' +line_prepend = "$indent" + +# Inject shaders applying to floating sprites +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self.children.floating_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod)" +position = "after" +payload = ''' +if self.edition then + for k, v in pairs(G.P_CENTER_POOLS.Edition) do + if v.apply_to_float then + if self.edition[v.key:sub(3)] then + self.children.floating_sprite:draw_shader(v.shader, nil, nil, nil, self.children.center, scale_mod, rotate_mod) + end + end + end +end''' +match_indent = true + +# Remove prefix from shader key when calling send() +[[patches]] +[patches.pattern] +target = "engine/sprite.lua" +pattern = "if _send then G.SHADERS[_shader or 'dissolve']:send(_shader,_send) end" +position = "at" +payload = ''' +if _send then + G.SHADERS[_shader or 'dissolve']:send((SMODS.Shaders[_shader or 'dissolve'] and SMODS.Shaders[_shader or 'dissolve'].original_key) or _shader,_send) +end''' +match_indent = true + +# Inject change to edition cost in shop +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '(?[\t ]*)self.ex([a-z._\s=+(0-9)]*)\n([\t ]*)([a-z._\s=+(0-9)]*)or 0\)' +position = "at" +payload = ''' +for k, v in pairs(G.P_CENTER_POOLS.Edition) do + if self.edition[v.key:sub(3)] then + if v.extra_cost then + self.extra_cost = self.extra_cost + v.extra_cost + end + end +end''' +line_prepend = "$indent" + +## Fix card_limit logic +# Card:add_to_deck() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)if self\.edition[A-z\.\:\n\t _(',)~=+\-0-9]*limit \+ 1''' +position = "at" +payload = ''' +if true then + if from_debuff then + self.ability.joker_added_to_deck_but_debuffed = nil + else + if self.edition and self.edition.card_limit then + if self.ability.consumeable then + G.consumeables.config.card_limit = G.consumeables.config.card_limit + self.edition.card_limit + else + G.jokers.config.card_limit = G.jokers.config.card_limit + self.edition.card_limit + end''' +line_prepend = "$indent" +# Card:remove_from_deck() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)if self\.edition[A-z\.\:\n\t _(',)~=+\-0-9]*limit \- 1''' +position = "at" +payload = ''' +if G.jokers then + if from_debuff then + self.ability.joker_added_to_deck_but_debuffed = true + else + if self.edition and self.edition.card_limit then + if self.ability.consumeable then + G.consumeables.config.card_limit = G.consumeables.config.card_limit - self.edition.card_limit + elseif self.ability.set == 'Joker' then + G.jokers.config.card_limit = G.jokers.config.card_limit - self.edition.card_limit + end''' +line_prepend = "$indent" +# Card:remove() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)if self\.ability\.queue_neg[A-z\.\:\n\t _(',)~=+\-0-9]*limit \- 1''' +position = "at" +payload = ''' +if self.ability.joker_added_to_deck_but_debuffed then + if self.edition and self.edition.card_limit then + if self.ability.consumeable then + G.consumeables.config.card_limit = G.consumeables.config.card_limit - self.edition.card_limit + elseif self.ability.set == 'Joker' then + G.jokers.config.card_limit = G.jokers.config.card_limit - self.edition.card_limit + end''' +line_prepend = "$indent" +# Card:save() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "added_to_deck = self.added_to_deck," +position = "after" +payload = "joker_added_to_deck_but_debuffed = self.joker_added_to_deck_but_debuffed," +match_indent = true + +# Alternate scoring effects for editions + +# Edition scoring on playing cards +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" # line 763 +pattern = '''hand_chips = mod_chips(hand_chips + (effects[ii].edition.chip_mod or 0))''' +position = "before" +payload = ''' +if effects[ii].edition.chip_mod then + hand_chips = mod_chips(hand_chips + effects[ii].edition.chip_mod) + local key_switch = (effects[ii].edition.chip_mod > 0 and 'a_chips' or 'a_chips_minus') + card_eval_status_text(scoring_hand[i], 'extra', nil, percent, nil, { + message = localize{type='variable', key=key_switch, vars={math.abs(effects[ii].edition.chip_mod)}}, + chip_mod = true, + colour = G.C.DARK_EDITION, + edition = true + }) + update_hand_text({delay = 0}, {chips = hand_chips}) +end +if effects[ii].edition.mult_mod then + mult = mult + effects[ii].edition.mult_mod + card_eval_status_text(scoring_hand[i], 'extra', nil, percent, nil, { + message = localize{type='variable', key='a_mult', vars={effects[ii].edition.mult_mod}}, + mult_mod = true, + colour = G.C.DARK_EDITION, + edition = true + }) + update_hand_text({delay = 0}, {mult = mult}) +end +if effects[ii].edition.x_mult_mod then + mult = mult * effects[ii].edition.x_mult_mod + card_eval_status_text(scoring_hand[i], 'extra', nil, percent, nil, { + message = localize{type='variable', key='a_xmult', vars={effects[ii].edition.x_mult_mod}}, + x_mult_mod = true, + colour = G.C.DARK_EDITION, + edition = true + }) + update_hand_text({delay = 0}, {mult = mult}) +end +if effects[ii].edition.p_dollars_mod then + if effects[ii].card then juice_card(effects[ii].card) end + ease_dollars(effects[ii].edition.p_dollars_mod) + card_eval_status_text(scoring_hand[i], 'dollars', effects[ii].edition.p_dollars_mod, percent) +end +if not effects[ii].edition then''' +match_indent = true +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = ''' edition = true\}\)''' +position = "after" +payload = '''end''' + +# Edition scoring on jokers +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "edition_effects.jokers.edition = true" +position = "after" +payload = ''' +if edition_effects.jokers.p_dollars_mod then + ease_dollars(edition_effects.jokers.p_dollars_mod) + card_eval_status_text(_card, 'dollars', edition_effects.jokers.p_dollars_mod, percent) +end''' +match_indent = true + + +# Adding p_dollars +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.edition.x_mult then" +position = "before" +payload = ''' +if self.edition.p_dollars then + ret.p_dollars_mod = self.edition.p_dollars +end''' +match_indent = true + + + +## Negative playing card logic +# CardArea:emplace() +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "function CardArea:emplace(*" +position = "after" +payload = ''' + if card.edition and card.edition.card_limit and (self == G.hand) then + self.config.real_card_limit = (self.config.real_card_limit or self.config.card_limit) + card.edition.card_limit + self.config.card_limit = math.max(0, self.config.real_card_limit) + end''' +match_indent = true +# CardArea:remove_card() +[[patches]] +[patches.pattern] +target = "cardarea.lua" +pattern = "card:remove_from_area()" +position = "before" +payload = ''' +if card.edition and card.edition.card_limit and (self == G.hand) then + self.config.real_card_limit = (self.config.real_card_limit or self.config.card_limit) - card.edition.card_limit + self.config.card_limit = math.max(0, self.config.real_card_limit) +end''' +match_indent = true + +# G.FUNCS.draw_from_deck_to_hand() +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local hand_space = e or*" +position = "at" +payload = """local hand_space = e +if not hand_space then + local limit = G.hand.config.card_limit - #G.hand.cards + local n = 0 + while n < #G.deck.cards do + local card = G.deck.cards[#G.deck.cards-n] + limit = limit - 1 + (card.edition and card.edition.card_limit or 0) + if limit < 0 then break end + n = n + 1 + end + hand_space = n +end""" +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "badges[#badges + 1] = 'negative_consumable'" +position = "after" +payload = """ +elseif self.edition.type == 'negative' and (self.ability.set == 'Enhanced' or self.ability.set == 'Default') then + badges[#badges + 1] = 'negative_playing_card'""" +match_indent = true + +[[patches]] +[patches.pattern] +target = "engine/sprite.lua" +pattern = "love.graphics.setShader( G.SHADERS[_shader or 'dissolve'], G.SHADERS[_shader or 'dissolve'])" +position = "before" +payload = ''' +local p_shader = SMODS.Shader.obj_table[_shader or 'dissolve'] +if p_shader and type(p_shader.send_vars) == "function" then + local sh = G.SHADERS[_shader or 'dissolve'] + local parent_card = self.role.major and self.role.major:is(Card) and self.role.major + local send_vars = p_shader.send_vars(self, parent_card) + + if type(send_vars) == "table" then + for key, value in pairs(send_vars) do + sh:send(key, value) + end + end +end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "if v == 'negative_consumable' then v = 'negative' end" +position = "at" +payload = '''if v == 'negative_consumable' or v == 'negative_playing_card' then v = 'negative' end''' +match_indent = true + +# +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = '''(?[\t ]*)(?local edition = poll_edition\('edi'\.\.\(key_append or ''\)\.\.G\.GAME\.round_resets\.ante\)(\n.*){2})''' +position = 'at' +line_prepend = '$indent' +payload = ''' +if not SMODS.bypass_create_card_edition then + $edi +end''' + +# eval_card() - add calculation calls for editions +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if context.cardarea == G.jokers or context.card == G.consumeables then" +match_indent = true +position = "before" +payload = """ +if card.edition and card.edition.key then + local ed = SMODS.Centers[card.edition.key] + if ed.calculate and type(ed.calculate) == 'function' then + context.from_playing_card = true + ed:calculate(card, context) + context.from_playing_card = nil + end +end""" + +# G.FUNCS.use_card() - add calculation calls for editions +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "e.config.ref_table:use_consumeable(area)" +match_indent = true +position = "after" +payload = """ +if card.edition and card.edition.key then + local ed = SMODS.Centers[card.edition.key] + if ed.calculate and type(ed.calculate) == 'function' then + ed:calculate(card, {from_consumable = true}) + end +end""" + +# G.FUNCS.evaluate_play() - calculate playing card retriggers from editions +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "for j=1,#reps do" +match_indent = true +position = "before" +payload = """ +--From edition +if scoring_hand[i].edition and scoring_hand[i].edition.key then + local ed = SMODS.Centers[scoring_hand[i].edition.key] + if ed.config and ed.config.retriggers then + for h = 1, ed.config.retriggers do + reps[#reps+1] = {seals = { + message = localize("k_again_ex"), + card = scoring_hand[i] + }} + end + end + if ed.calculate and type(ed.calculate) == 'function' then + local check = ed:calculate(scoring_hand[i], {retrigger_edition_check = true, cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = scoring_hand[i], repetition = true}) + if check and type(check) == 'table' and next(check) then + for j = 1, check.repetitions do + reps[#reps+1] = {seals = check} + end + end + end +end +""" diff --git a/Steamodded/lovely/enhancement.toml b/Steamodded/lovely/enhancement.toml new file mode 100644 index 0000000..843a2fd --- /dev/null +++ b/Steamodded/lovely/enhancement.toml @@ -0,0 +1,276 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +## no_rank, no_suit, all_suits + +# Card:get_id() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.effect == 'Stone Card' and not self.vampired then" +match_indent = true +position = "at" +payload = "if (self.ability.effect == 'Stone Card' or self.config.center.no_rank) and not self.vampired then" + +# Card:get_chip_bonus() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +(?[\t ]*)if self\.ability\.effect == 'Stone Card' then +[\t ]* return self\.ability\.bonus \+ \(self\.ability\.perma_bonus or 0\) +[\t ]*end''' +position = "at" +payload = ''' +if self.ability.effect == 'Stone Card' or self.config.center.replace_base_card then + return self.ability.bonus + (self.ability.perma_bonus or 0) +end''' +line_prepend = '$indent' + +# Card:calculate_joker() +# Raised Fist +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if temp_ID >= G.hand.cards[i].base.id and G.hand.cards[i].ability.effect ~= 'Stone Card' then temp_Mult = G.hand.cards[i].base.nominal; temp_ID = G.hand.cards[i].base.id; raised_card = G.hand.cards[i] end" +match_indent = true +position = "at" +payload = """ +if temp_ID >= G.hand.cards[i].base.id and (G.hand.cards[i].ability.effect ~= 'Stone Card' and not G.hand.cards[i].config.center.no_rank) then + temp_Mult = G.hand.cards[i].base.nominal + temp_ID = G.hand.cards[i].base.id + raised_card = G.hand.cards[i] +end""" +# Flower Pot, Seeing Double +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if context.scoring_hand[i].ability.name ~= 'Wild Card' then" +match_indent = true +position = "at" +payload = "if context.scoring_hand[i].ability.name ~= 'Wild Card' and not context.scoring_hand[i].config.center.any_suit then" +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if context.scoring_hand[i].ability.name == 'Wild Card' then" +match_indent = true +position = "at" +payload = "if context.scoring_hand[i].ability.name == 'Wild Card' or context.scoring_hand[i].config.center.any_suit then" + +# Card:get_suit() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)if self\.ability\.effect == 'Stone Card' then''' +line_prepend = '$indent' +position = "at" +payload = "if self.ability.effect == 'Stone Card' or self.config.center.no_suit then" +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'if self.ability.name == "Wild Card" then' +match_indent = true +position = "at" +payload = "if self.ability.name == 'Wild Card' or self.config.center.any_suit then" +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'if self.ability.name == "Wild Card" and not self.debuff then' +match_indent = true +position = "at" +payload = "if (self.ability.name == 'Wild Card' or self.config.center.any_suit) and not self.debuff then" + +# check_for_unlock +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if v.ability.name ~= 'Stone Card' and v.base.suit == 'Hearts' then" +match_indent = true +position = "at" +payload = "if (v.ability.name ~= 'Stone Card' and not v.config.center.no_suit) and v.base.suit == 'Hearts' then" + +# reset_idol_card() +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "valid_idol_cards[#valid_idol_cards+1] = v" +match_indent = true +position = "at" +payload = """ +if (not v.config.center.no_suit) and (not v.config.center.no_rank) then + valid_idol_cards[#valid_idol_cards+1] = v +end""" + +# reset_mail_rank() +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "valid_mail_cards[#valid_mail_cards+1] = v" +match_indent = true +position = "at" +payload = """ +if not v.config.center.no_rank then + valid_mail_cards[#valid_mail_cards+1] = v +end""" + +# reset_castle_card() +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "valid_castle_cards[#valid_castle_cards+1] = v" +match_indent = true +position = "at" +payload = """ +if not v.config.center.no_suit then + valid_castle_cards[#valid_castle_cards+1] = v +end""" + +# G.FUNCS.evaluate_play() +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if G.play.cards[i].ability.effect == 'Stone Card' then" +match_indent = true +position = "at" +payload = "if G.play.cards[i].ability.effect == 'Stone Card' or G.play.cards[i].config.center.always_scores then" +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if scoring_hand[i].ability.effect ~= 'Stone Card' then" +match_indent = true +position = "at" +payload = "if scoring_hand[i].ability.effect ~= 'Stone Card' and not scoring_hand[i].config.center.no_rank then" +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "G.GAME.cards_played[scoring_hand[i].base.value].suits[scoring_hand[i].base.suit] = true" +match_indent = true +position = "at" +payload = """ +if not scoring_hand[i].config.center.no_suit then + G.GAME.cards_played[scoring_hand[i].base.value].suits[scoring_hand[i].base.suit] = true +end""" + + +## replace_base_card +# Determines whether to draw the base card's front or not +# Card:draw() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.children.front and self.ability.effect ~= 'Stone Card' then" +match_indent = true +position = "at" +payload = "if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then" + +# Card:generate_UIBox_ability_table() +# replaces two consecutive lines +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if (_c.name == 'Stone Card') then full_UI_table.name = true end" +match_indent = true +position = "at" +payload = "if _c.name == 'Stone Card' or _c.replace_base_card then full_UI_table.name = true" +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if (specific_vars.playing_card and (_c.name ~= 'Stone Card')) then" +match_indent = true +position = "at" +payload = "elseif specific_vars.playing_card then" + +# eval_card() +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "function eval_card(card, context)" +match_indent = true +position = "after" +payload = """ + local enhancement_calculated = false + local center = card.config.center""" +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "local jokers = card:calculate_joker(context)" +match_indent = true +position = "before" +payload = """ +if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then + center:calculate(card, context, ret) + enhancement_calculated = true +end""" +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "local seals = card:calculate_seal(context)" +match_indent = true +position = "before" +payload = """ +if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then + center:calculate(card, context, ret) + enhancement_calculated = true +end""" +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if context.cardarea == G.jokers or context.card == G.consumeables then" +match_indent = true +position = "before" +payload = """ +if not enhancement_calculated and card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then + center:calculate(card, context, ret) + enhancement_calculated = true +end +local seals = card:calculate_seal(context) +if seals then + ret.seals = seals +end""" + + +## Add additional eval_card() calls + +# Game:update_draw_to_hand() + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "G.GAME.current_round.discards_used == 0 and G.GAME.facing_blind then" +match_indent = true +position = "after" +payload = """ +for i = 1, #G.hand.cards do + eval_card(G.hand.cards[i], {first_hand_drawn = true}) +end""" + +# G.FUNCS.discard_cards_from_highlighted() + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "inc_career_stat('c_cards_discarded', highlighted_count)" +match_indent = true +position = "after" +payload = """ +for i = 1, #G.hand.cards do + eval_card(G.hand.cards[i], {pre_discard = true, full_hand = G.hand.highlighted, hook = hook}) +end""" +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = ''' +(?[\t ]*)G\.hand\.highlighted\[i\]:calculate_seal\(\{discard = true\}\) +[\t ]*local removed = false''' +line_prepend = '$indent' +position = "at" +payload = """ +local removed = false +local eval = nil +eval = eval_card(G.hand.highlighted[i], {discard = true, full_hand = G.hand.highlighted}) +if eval and eval.remove then + removed = true + card_eval_status_text(G.hand.highlighted[i], 'jokers', nil, 1, nil, eval) +end""" diff --git a/Steamodded/lovely/fixes.toml b/Steamodded/lovely/fixes.toml new file mode 100644 index 0000000..4b8b68a --- /dev/null +++ b/Steamodded/lovely/fixes.toml @@ -0,0 +1,537 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Fixes for either base game code or general mod compatibility + +## Mods assume Game:start_run() is called with non-nil argument +# G.FUNCS.start_run() +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = "G.FUNCS.start_run = function(e, args)" +position = 'after' +match_indent = true +payload = "args = args or {}" + +## Allows running the game without Steam being active +# love.load() +[[patches]] +[patches.regex] +target = 'main.lua' +pattern = "(?[\t ]*)if not \\(st.init and st:init\\(\\)\\) then\n[\t ]*(?love.event.quit\\(\\))" +position = 'at' +root_capture = 'quit' +payload = 'st = nil' + + +## Prevents the game from crashing when hitting play with a corrupt/invalid save file +# G.FUNCS.can_continue(e) +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = "if G.SAVED_GAME ~= nil then G.SAVED_GAME = STR_UNPACK(G.SAVED_GAME) end" +position = 'after' +match_indent = true +payload = """ +if G.SAVED_GAME == nil then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + return _can_continue +end +""" + +## Fix loading a blind with $0 reward +# Blind:load() +[[patches]] +[patches.regex] +target = 'blind.lua' +pattern = ''' +(?[\t ]*) G\.HUD_blind\.alignment\.offset\.y = 0 +[\t ]*end''' +position = 'at' +payload = ''' +end +if G.GAME.blind.name and G.GAME.blind.name ~= '' then + G.HUD_blind.alignment.offset.y = 0 +end''' +line_prepend = '$indent' + +## Remove incorrect check for Moveable alignment change +# Moveable:align_to_major() +[[patches]] +[patches.regex] +target = 'engine/moveable.lua' +pattern = ''' +(?[\t ]*)if +self\.alignment\.prev_offset\.x == self\.alignment\.offset\.x[\s\S]*?return end +''' +position = 'at' +payload = 'if not self.alignment.type_list then return end' +line_prepend = '$indent' + +## Prevent softlock if booster pack is empty +## Crashes the game when you skip too fast on this PR, along with being the culprit for allowing you to skip boosters early +# G.FUNCS.can_skip_booster() +# [[patches]] +# [patches.pattern] +# target = 'functions/button_callbacks.lua' +# pattern = 'if G.pack_cards and (G.pack_cards.cards[1]) and' +# position = 'at' +# payload = 'if G.pack_cards and' +# match_indent = true + +## Set `G.your_collection.config.collection` to true in all cases +# create_UIBox_your_collection_seals() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '''\{card_limit = 4, type = 'title', highlight_limit = 0\}''' +position = 'at' +payload = '''{card_limit = 4, type = 'title', highlight_limit = 0, collection = true}''' + +## Save and load Card.unique_val +# Card:save() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "bypass_lock = self.bypass_lock," +position = "after" +payload = """ +unique_val = self.unique_val, +unique_val__saved_ID = self.ID, +ignore_base_shader = self.ignore_base_shader, +ignore_shadow = self.ignore_shadow,""" +match_indent = true + +# Card:load() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "self.bypass_lock = cardTable.bypass_lock" +position = "after" +payload = """ +self.unique_val = cardTable.unique_val or self.unique_val +if cardTable.unique_val__saved_ID and G.ID <= cardTable.unique_val__saved_ID then + G.ID = cardTable.unique_val__saved_ID + 1 +end + +self.ignore_base_shader = cardTable.ignore_base_shader or {} +self.ignore_shadow = cardTable.ignore_shadow or {}""" +match_indent = true + +## Vars in card descriptions should use `card.ability` instead of `_c.config` where possible +## Allow passing in custom vars +# generate_card_ui() +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = 'function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end)' +position = 'at' +match_indent = true +payload = '''function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end, card) + if _c.specific_vars then specific_vars = _c.specific_vars end''' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if _c.set == 'Other' then" +position = 'before' +match_indent = true +payload = "local cfg = (card and card.ability) or _c['config']" # string index to make sure the next patch doesn't eat it + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if _c.name ~= 'Stone Card' and ((specific_vars and specific_vars.bonus_chips) or _c.config.bonus) then" +position = 'at' +match_indent = true +payload = "if _c.name ~= 'Stone Card' and ((specific_vars and specific_vars.bonus_chips) or (cfg.bonus ~= 0 and cfg.bonus)) then" + +[[patches]] +[patches.regex] +target = 'functions/common_events.lua' +pattern = '_c.config' +position = 'at' +payload = 'cfg' + +## When overriding with set_ability and card is added to deck, call add / remove effects +# Card:set_ability() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "self.config.center = center" +position = 'before' +match_indent = true +payload = ''' +if self.added_to_deck and old_center and not self.debuff then + self:remove_from_deck() + self.added_to_deck = true +end''' + +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "if G.consumeables and self.area == G.consumeables then" +position = 'before' +match_indent = true +payload = ''' +if self.added_to_deck and old_center and not self.debuff then + self.added_to_deck = false + self:add_to_deck() +end''' + +## set_ability() transfers over old fields +# special cases: +# extra_value should be transferred +# name, effect, set, extra should always be overwritten +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = ''' +(?[\t ]*)self\.ability = (\{[\s\S]*? +[\t ]*\}) +''' +position = 'at' +line_prepend = '$indent' +payload = ''' +local new_ability = $2 +self.ability = self.ability or {} +new_ability.extra_value = nil +self.ability.extra_value = self.ability.extra_value or 0 +for k, v in pairs(new_ability) do + self.ability[k] = v +end +-- reset keys do not persist an ability change +local reset_keys = {'name', 'effect', 'set', 'extra', 'played_this_ante'} +for _, mod in ipairs(SMODS.mod_list) do + if mod.set_ability_reset_keys then + local keys = mod.set_ability_reset_keys() + for _, v in pairs(keys) do table.insert(reset_keys, v) end + end +end +for _, k in ipairs(reset_keys) do + self.ability[k] = new_ability[k] +end +''' + +## Fix crash if self.config.card == nil for non-vanilla set_ability() calls +# Card:set_ability() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "self.label = center.label or self.config.card.label or self.ability.set" +position = 'at' +match_indent = true +payload = "self.label = center.label or self.config.card and self.config.card.label or self.ability.set" + +### Fix Matador + +# These patches have been removed for altering vanilla behavior. Git blame this line to see what they were + +### Fix Crimson Heart + +## Blind:drawn_to_hand() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if self.name == 'Crimson Heart' and self.prepped and G.jokers.cards[1] then" +position = 'after' +match_indent = true +payload = """ + local prev_chosen_set = {} + local fallback_jokers = {}""" +# Fix bad logic if not enough choices for debuff +[[patches]] +[patches.regex] +target = 'blind.lua' +pattern = ''' +(?[\t ]*)for i = 1, #G\.jokers\.cards do +[\t ]*if not G\.jokers\.cards\[i\]\.debuff or #G\.jokers\.cards < 2 then jokers\[#jokers\+1\] = ?G\.jokers\.cards\[i\] end +[\t ]*G\.jokers\.cards\[i\]:set_debuff\(false\) +[\t ]*end''' +position = 'at' +line_prepend = '$indent' +payload = """ +for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.crimson_heart_chosen then + prev_chosen_set[G.jokers.cards[i]] = true + G.jokers.cards[i].ability.crimson_heart_chosen = nil + if G.jokers.cards[i].debuff then SMODS.recalc_debuff(G.jokers.cards[i]) end + end +end +for i = 1, #G.jokers.cards do + if not G.jokers.cards[i].debuff then + if not prev_chosen_set[G.jokers.cards[i]] then + jokers[#jokers+1] = G.jokers.cards[i] + end + table.insert(fallback_jokers, G.jokers.cards[i]) + end +end +if #jokers == 0 then jokers = fallback_jokers end""" +# Add variable for Crimson Heart's choice +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "_card:set_debuff(true)" +position = "at" +match_indent = true +payload = """ +_card.ability.crimson_heart_chosen = true +SMODS.recalc_debuff(_card)""" + +## Blind:debuff_card() +[[patches]] +[patches.regex] +target = 'blind.lua' +pattern = ''' +if self\.name == 'Crimson Heart' and not self\.disabled and card\.area == G\.jokers then\s+ +((?[\t ]*)return)''' +root_capture = '$1' +position = "at" +line_prepend = '$indent' +payload = """ +if card.ability.crimson_heart_chosen then + card:set_debuff(true); + if card.debuff then card.debuffed_by_blind = true end + return +end""" + +## Blind:press_play() +# Shouldn't work with Matador +# yes it should + +## Blind:disable() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if self.name == 'The Water' then" +position = 'before' +payload = """ +if self.name == 'Crimson Heart' then + for _, v in ipairs(G.jokers.cards) do + v.ability.crimson_heart_chosen = nil + end +end""" +match_indent = true + +## Blind:defeat() +[[patches]] +[patches.pattern] +target = 'blind.lua' +pattern = "if self.name == 'The Manacle' and not self.disabled then" +position = 'before' +payload = """ +if self.name == 'Crimson Heart' then + for _, v in ipairs(G.jokers.cards) do + v.ability.crimson_heart_chosen = nil + end +end""" +match_indent = true + + +## Fix Manacle's unnecessary card draw after positive G.hand:change_size() +# Blind:disable() +[[patches]] +[patches.regex] +target = 'blind.lua' +pattern = 'G\.hand:change_size\(1\)(\s+G\.FUNCS\.draw_from_deck_to_hand\(1\))' +root_capture = '$1' +position = 'at' +payload = "" + +# +# Money scaling fix +# + +## create_UIBox_HUD +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = ''' +string = \{\{ref_table = G\.GAME\, ref_value = 'dollars'\, prefix = localize\('\$'\)\}\}\,''' +position = "after" +payload = ''' + + scale_function = function () + return scale_number(G.GAME.dollars, 2.2 * scale, 99999, 1000000) + end,''' + +## DynaText:update_text +[[patches]] +[patches.pattern] +target = "engine/text.lua" +pattern = 'self.config.H = 0' +position = "after" +payload = "self.scale = self.config.scale_function and self.config.scale_function() or self.scale" +match_indent = true + + +# +# Fix gold stake legendary infloop +# Do not try to roll for jokers that are not in_pool +# + +# generate_starting_seed() +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = '''if win_ante and (win_ante >= 8) then''' +match_indent = true +position = "at" +payload = '''if win_ante and (win_ante >= 8) or (v.in_pool and type(v.in_pool) == 'function' and not v:in_pool()) then''' + +# +# Fix G.GAME.blind:set_blind(nil, true, nil) +# being called when not in blind. +# + +# Card:add_to_deck +# Card:remove_from_deck +[[patches]] +[patches.regex] +target = "card.lua" +pattern = 'if G\.GAME\.blind then' +position = "at" +payload = "if G.GAME.blind and G.GAME.blind.in_blind then" + +# Blind:set_blind +[[patches]] +[patches.pattern] +target = "blind.lua" +match_indent = true +pattern = "if not reset then" +position = "after" +payload = ''' + if blind then + self.in_blind = true + end''' + +# end_round() +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local game_over = true" +position = "before" +payload = "G.GAME.blind.in_blind = false" +match_indent = true + + + +# Make sure new param is loaded +[[patches]] +[patches.pattern] +target = "blind.lua" +match_indent = true +pattern = "function Blind:load(blindTable)" +position = "after" +payload = ''' + self.in_blind = blindTable.in_blind''' + +# Make sure new param is saved +[[patches]] +[patches.pattern] +target = "blind.lua" +match_indent = true +pattern = "local blindTable = {" +position = "after" +payload = ''' + in_blind = self.in_blind,''' + +# Cartomancer and astronomer unlock when *actually all* Tarot/Planet cards are discovered +# check_for_unlock() +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +match_indent = true +pattern = "if card.unlock_condition.tarot_count <= args.tarot_count then" +position = "at" +payload = 'if #G.P_CENTER_POOLS.Tarot <= args.tarot_count then' + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +match_indent = true +pattern = "if card.unlock_condition.planet_count <= args.planet_count then" +position = "at" +payload = 'if #G.P_CENTER_POOLS.Planet <= args.planet_count then' + +# wtf +[[patches]] +[patches.pattern] +target = "engine/animatedsprite.lua" +match_indent = true +pattern = "for _, v in pairs(G.ANIMATIONS) do" +position = "at" +payload = 'for k, v in pairs(G.ANIMATIONS) do' + +[[patches]] +[patches.pattern] +target = "engine/animatedsprite.lua" +match_indent = true +pattern = "for _, v in pairs(G.I.SPRITE) do" +position = "at" +payload = 'for k, v in pairs(G.I.SPRITE) do' + +## +## Card:draw() - improved mod compatibility +## +# Add option for sprites to not be drawn +[[patches]] +[patches.pattern] +target = "card.lua" +match_indent = true +pattern = ''' +if k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end''' +position = "at" +payload = ''' +if not v.custom_draw and k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end''' + +# This check is not necessary? +[[patches]] +[patches.pattern] +target = "card.lua" +match_indent = true +pattern = ''' +if self.edition or self.seal or self.ability.eternal or self.ability.rental or self.ability.perishable or self.sticker or ((self.sticker_run and self.sticker_run ~= 'NONE') and G.SETTINGS.run_stake_stickers) or (self.ability.set == 'Spectral') or self.debuff or self.greyed or (self.ability.name == 'The Soul') or (self.ability.set == 'Voucher') or (self.ability.set == 'Booster') or self.config.center.soul_pos or self.config.center.demo then''' +position = "at" +payload = ''' +if true then''' + +# Basegame fix for the reroll vouchers redeem function when only the center but no card object exists + +# Card:apply_to_run +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = """G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost - self.ability.extra""" +position = 'at' +match_indent = true +payload = """G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost - center_table.extra""" + +# Card:apply_to_run +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = """G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost - self.ability.extra)""" +position = 'at' +match_indent = true +payload = """G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost - center_table.extra)""" + + +# Add h_chips as a viable hand effect +[[patches]] +[patches.pattern] +target = 'functions/state_events.lua' +pattern = '''card_eval_status_text(G.hand.cards[i], 'h_mult', effects[ii].h_mult, percent) + end''' +position = 'after' +match_indent = true +payload = ''' +if effects[ii].h_chips then + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips + effects[ii].h_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(effects[ii].card, 'chips', effects[ii].h_chips, percent) +end +''' \ No newline at end of file diff --git a/Steamodded/lovely/joker_retrigger.toml b/Steamodded/lovely/joker_retrigger.toml new file mode 100644 index 0000000..3066c7f --- /dev/null +++ b/Steamodded/lovely/joker_retrigger.toml @@ -0,0 +1,625 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# main joker retriggering +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local effects = eval_card(_card, {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, joker_main = true})" +position = "at" +payload = "local effects = eval_card(_card, {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, joker_main = true, callback = function(_card, ret) effects = {jokers = ret}" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "--Joker on Joker effects" +position = "before" +payload = "end})" +match_indent = true + +# End of round retriggering from jokers +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local eval = eval_card(G.jokers.cards[j], {cardarea = G.hand, other_card = G.hand.cards[i], repetition = true, end_of_round = true, card_effects = effects})" +position = "at" +payload = "local eval = eval_card(G.jokers.cards[j], {cardarea = G.hand, other_card = G.hand.cards[i], repetition = true, end_of_round = true, card_effects = effects, callback = function(card, ret) eval = {jokers = ret}" +match_indent = true + +# Played hand retriggering from jokers +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local eval = eval_card(G.jokers.cards[j], {cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = scoring_hand[i], repetition = true})" +position = "at" +payload = "local eval = eval_card(G.jokers.cards[j], {cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = scoring_hand[i], repetition = true, callback = function(card, ret) eval = {jokers = ret}" +match_indent = true + +# Held in hand retriggering from jokers +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local eval = eval_card(G.jokers.cards[j], {cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = G.hand.cards[i], repetition = true, card_effects = effects})" +position = "at" +payload = "local eval = eval_card(G.jokers.cards[j], {cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = G.hand.cards[i], repetition = true, card_effects = effects, callback = function(card, ret) eval = {jokers = ret}" +match_indent = true + +# I learned how to do regex patches for this (fixes syntax for last 3 patches) +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = '''for h[ ]*\= 1, eval\.jokers\.repetitions do +[ \t]*reps\[#reps\+1\] \= eval +[ \t]*end +[ \t]*end''' +position = "after" +payload = " end})" + +# Before hand effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, before = true})" +position = "at" +payload = "local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, before = true, callback = function(card, ret) effects = {jokers = ret}" +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = ''' +level_up_hand\(G\.jokers\.cards\[i\], text\) +(?[ \t]*end +[ \t]*end)''' +position = "at" +payload = ''' +level_up_hand(card, text) +$ends + end})''' + +# Joker debuff effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, debuffed_hand = true})" +position = "at" +payload = "local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, debuffed_hand = true, callback = function(card, ret) effects = {jokers = ret}" +match_indent = true + +# After hand effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, after = true})" +position = "at" +payload = "local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, after = true, callback = function(card, ret) effects = {jokers = ret}" +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = '''[ \t]*end +[ \t]*end +[ \t]*G\.E_MANAGER:add_event\(Event\(\{ +[ \t]*trigger = 'after',delay = 0.4,''' +position = "before" +payload = " end})" + +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = ''' +(?[\t ])*if effects\.jokers then +[^\n]* +[^\n]*_delta +[\t ]*end +[\t ]*end\n\n''' +position = "after" +line_prepend = "$indent" +payload = ''' }) end + +''' + +# End of round effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "eval = G.jokers.cards[i]:calculate_joker({end_of_round = true, game_over = game_over})" +position = "at" +payload = "eval = G.jokers.cards[i]:calculate_joker({end_of_round = true, game_over = game_over, callback = function(card, eval)" +match_indent = true + +# End of round held in hand effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.hand, other_card = G.hand.cards[i], individual = true, end_of_round = true})" +position = "at" +payload = "local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.hand, other_card = G.hand.cards[i], individual = true, end_of_round = true, callback = function(card, eval, retrigger)" +match_indent = true + +# Played hand effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = scoring_hand[i], individual = true})" +position = "at" +payload = "local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = scoring_hand[i], individual = true, callback = function(card, eval, retrigger)" +match_indent = true + +# Held in hand effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = G.hand.cards[i], individual = true})" +position = "at" +payload = "local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = G.hand.cards[i], individual = true, callback = function(card, eval, retrigger)" +match_indent = true + +# Fix syntax of last 3 patches +# Add retrigger info +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = '''table\.insert\(effects, eval\) +[ \t]*end''' +position = "at" +payload = ''' +table.insert(effects, eval) +effects[#effects].from_retrigger = retrigger +end end, no_retrigger_anim = true}) +''' + +# Use retrigger info when computing repetitions + +# Played hand effects +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = '''end +[ \t]*end +[ \t]*end +[ \t]*end +[ \t]* +[ \t]*delay\(0\.3\)''' +position = "before" +payload = ''' + if effects[ii].from_retrigger then + card_eval_status_text(effects[ii].from_retrigger.card, 'jokers', nil, nil, nil, effects[ii].from_retrigger) + end + +''' + +# Held in hand effects +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = '''end +[ \t]*j[ \t]*=[ \t]*j[ \t]\+1''' +position = "before" +payload = ''' + if effects[ii].from_retrigger then + card_eval_status_text(effects[ii].from_retrigger.card, 'jokers', nil, nil, nil, effects[ii].from_retrigger) + end + +''' + +# Discard effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "eval = G.jokers.cards[j]:calculate_joker({discard = true, other_card = G.hand.highlighted[i], full_hand = G.hand.highlighted})" +position = "at" +payload = "eval = G.jokers.cards[j]:calculate_joker({discard = true, other_card = G.hand.highlighted[i], full_hand = G.hand.highlighted, callback = function(card, eval)" +match_indent = true + +# Joker on Joker effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "local effect = v:calculate_joker{full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_joker = _card}" +position = "at" +payload = "local effect = v:calculate_joker({full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_joker = _card, callback = function(v, effect)" +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = '''end +[ \t]*end +[ \t]* +[ \t]*if edition_effects\.jokers''' +position = "at" +payload = "end end}) end if edition_effects.jokers" + +# Destroyed card effects +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "destroyed = G.jokers.cards[j]:calculate_joker({destroying_card = scoring_hand[i], full_hand = G.play.cards})" +position = "at" +payload = '''destroyed = G.jokers.cards[j]:calculate_joker({destroying_card = scoring_hand[i], full_hand = G.play.cards, callback = function(card, ret) if ret then destroyed=true end end})''' +match_indent = true + +# Blueprint/Brainstorm +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "local other_joker_ret = other_joker:calculate_joker(context)" +position = "at" +payload = '''local other_joker_ret, trig = other_joker:calculate_joker(context) +''' +match_indent = true + +# We don't need to return trig; Blueprint/Brainstorm cause callbacks to trigger twice +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if context.blueprint > #G.jokers.cards + 1 then return end" +position = "after" +payload = '''context.no_callback = true''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "context.blueprint = (context.blueprint and (context.blueprint + 1)) or 1" +position = "after" +payload = '''context.copy_depth = (context.copy_depth and (context.copy_depth + 1)) or 1''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "other_joker_ret.card = context.blueprint_card or self" +position = "after" +payload = '''context.no_callback = not (context.copy_depth <= 1) +context.copy_depth = context.copy_depth - 1;''' +match_indent = true + +# Luchador +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''[ \t]*G\.GAME\.blind:disable\(\) +(?[ \t]*)end''' +position = "at" +line_prepend = '$indent' +payload = ''' G.GAME.blind:disable() + return nil, true +end''' + +# Diet Cola +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*return true +[ \t]*end\) +[ \t]*\}\)\) +(?[ \t]*)end +''' +position = "at" +line_prepend = '$indent' +payload = ''' return true + end) + })) + return nil, true +end +''' + +# Invisible Joker +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''[ \t]*if card\.ability\.invis_rounds then card\.ability\.invis_rounds = 0 end +[ \t]*card:add_to_deck\(\) +(?[ \t]*)G\.jokers:emplace\(card\)''' +position = "after" +line_prepend = '$indent' +payload = "return nil, true" + +# Campfire +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''localize\('k_upgrade_ex'\)\}\); return true +[ \t]*end\}\)\) +[ \t]*end +(?[ \t]*)return''' +position = "at" +line_prepend = '$indent' +payload = '''localize('k_upgrade_ex')}); return true + end})) +end +if self.ability.name == 'Campfire' and not context.blueprint then return nil, true end''' + +# Flash Card +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''[ \t]*G\.C\.MULT\}\) +[ \t]*return true +[ \t]*end\)\}\)\) +(?[ \t]*)end''' +position = "at" +line_prepend = '$indent' +payload = '''G.C.MULT}) + return true + end)})) +end +if self.ability.name == 'Flash Card' and not context.blueprint then return nil, true end''' + +# Perkeo +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''[ \t]*card_eval_status_text\(context\.blueprint_card or self, 'extra', nil, nil, nil, \{message = localize\('k_duplicated_ex'\)\}\) +(?[ \t]*)end''' +position = "at" +line_prepend = '$indent' +payload = ''' card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_duplicated_ex')}) + return nil, true +end''' + +# Throwback +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +[ \t]*return +(?[ \t]*)elseif context\.skipping_booster''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Red Card +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +[ \t]*return +(?[ \t]*)elseif context\.playing_card_added''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Hologram +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)elseif context\.first_hand_drawn''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Certificate +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'DNA' and not context\.blueprint''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Chicot +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'Madness' ''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Madness +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'Burglar' ''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Burglar +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'Riff-raff' ''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Riff-raff +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'Cartomancer' ''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Cartomancer +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'Ceremonial Dagger' and not context.blueprint''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Ceremonial Dagger +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'Marble Joker' ''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Marble Joker +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +[ \t]*return +(?[ \t]*)elseif context.destroying_card''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Caino +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''[ \t]*func = function\(\) card_eval_status_text\(self, 'extra', nil, nil, nil, \{message = localize\{type = 'variable', key = 'a_xmult', vars = \{self\.ability\.caino_xmult\}\}\}\); return true +(?[ \t]*)end\}\)\)''' +position = "after" +line_prepend = '$indent' +payload = "return nil, true" + +# Glass Joker +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''glass_cards\}\}\}\) +[ \t]*return true +[ \t]*end +(?[ \t]*)\}\)\)''' +position = "after" +line_prepend = '$indent' +payload = "return nil, true" + +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +[ \t]*return +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'Fortune Teller' and not context\.blueprint''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Fortune Teller +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)if self\.ability\.name == 'Constellation' and not context\.blueprint''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Constellation +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +[ \t]*return +(?[ \t]*)elseif context.debuffed_hand''' +position = "before" +line_prepend = '$indent' +payload = "nil, true" + +# Burnt Joker +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +(?[ \t]*)elseif context.discard''' +position = "before" +line_prepend = '$indent' +payload = "return nil, true" + +# Faceless Joker +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +[ \t]*end +[ \t]*end +[ \t]*return +(?[ \t]*)elseif context.end_of_round''' +position = "before" +line_prepend = '$indent' +payload = "nil, true" + +# Yorick +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "self.ability.yorick_discards = self.ability.yorick_discards - 1" +position = "after" +match_indent = true +payload = "return nil, true" + +# eval_status_text fixes +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = ''' +card_eval_status_text\(G\.jokers\.cards\[i\]\, 'jokers'\, nil\, nil\, nil\, eval\) +[ \t]*end''' +position = "at" +payload = ''' +card_eval_status_text(card, 'jokers', nil, nil, nil, eval) + end + end}) +''' + +[[patches]] +[patches.regex] +target = "functions/state_events.lua" +pattern = ''' +card_eval_status_text\(G\.jokers\.cards\[j\]\, 'jokers'\, nil\, 1\, nil\, eval\) +[ \t]*end''' +position = "at" +payload = ''' +card_eval_status_text(card, 'jokers', nil, 1, nil, eval) + end + end}) +''' + + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "card_eval_status_text(G.jokers.cards[i], 'jokers', nil, percent, nil, effects.jokers)" +position = "at" +match_indent = true +payload = "card_eval_status_text(card, 'jokers', nil, percent, nil, effects.jokers)" + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "for h = 1, eval.jokers.repetitions do" +position = "before" +match_indent = true +payload = "if not eval.jokers.repetitions then eval.jokers.repetitions = 0 end" diff --git a/Steamodded/lovely/keybind.toml b/Steamodded/lovely/keybind.toml new file mode 100644 index 0000000..6001bb6 --- /dev/null +++ b/Steamodded/lovely/keybind.toml @@ -0,0 +1,47 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Check all registered keybinds +# inserted inside Controller:key_press_update + +[[patches]] +[patches.pattern] +target = 'engine/controller.lua' +pattern = "if not _RELEASE_MODE then" +position = "before" +payload = ''' +for _, keybind in pairs(SMODS.Keybinds) do + if keybind.action and keybind.key_pressed == key then + local execute = true + for _, other_key in pairs(keybind.held_keys) do + if not self.held_keys[other_key] then + execute = false + break + end + end + if execute then + keybind.action(self) + end + end +end +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.regex] +target = 'engine/controller.lua' +pattern = 'if key == "r"' +position = 'at' +line_prepend = '$indent' +payload = ''' +if key == 'm' then + if self.held_key_times[key] > 1.1 then + SMODS.save_all_config() + SMODS.restart_game() + else + self.held_key_times[key] = self.held_key_times[key] + dt + end +elseif key == "r"''' \ No newline at end of file diff --git a/Steamodded/lovely/language.toml b/Steamodded/lovely/language.toml new file mode 100644 index 0000000..2d218d1 --- /dev/null +++ b/Steamodded/lovely/language.toml @@ -0,0 +1,40 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Language API + +# Game:set_language() +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = "if not (love.filesystem.read('localization/'..G.SETTINGS.language..'.lua')) or G.F_ENGLISH_ONLY then" +position = 'at' +payload = 'if false then' +match_indent = true + +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = "local localization = love.filesystem.getInfo('localization/'..G.SETTINGS.language..'.lua')" +position = 'at' +payload = "local localization = love.filesystem.getInfo('localization/'..G.SETTINGS.language..'.lua') or love.filesystem.getInfo('localization/en-us.lua')" +match_indent = true + +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = "self.localization = assert(loadstring(love.filesystem.read('localization/'..G.SETTINGS.language..'.lua')))()" +position = 'at' +payload = "self.localization = assert(loadstring(love.filesystem.read('localization/'..G.SETTINGS.language..'.lua') or love.filesystem.read('localization/en-us.lua')))()" +match_indent = true + +# G.FUNCS.warn_lang (wtf) +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = 'if (_infotip_object.config.set ~= e.config.ref_table.label) and (not G.F_NO_ACHIEVEMENTS) then' +position = 'at' +payload = 'if (_infotip_object.config.set ~= e.config.ref_table.label) then' +match_indent = true \ No newline at end of file diff --git a/Steamodded/lovely/libs.toml b/Steamodded/lovely/libs.toml new file mode 100644 index 0000000..4e6d8a5 --- /dev/null +++ b/Steamodded/lovely/libs.toml @@ -0,0 +1,16 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +[[patches]] +[patches.module] +source = "libs/json/json.lua" +before = "main.lua" +name = "json" + +[[patches]] +[patches.module] +source = "libs/nativefs/nativefs.lua" +before = "main.lua" +name = "nativefs" \ No newline at end of file diff --git a/Steamodded/lovely/loader.toml b/Steamodded/lovely/loader.toml new file mode 100644 index 0000000..b829c59 --- /dev/null +++ b/Steamodded/lovely/loader.toml @@ -0,0 +1,27 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Supporting code for loader.lua + +## Save discovered, unlocked states +# Game:init_item_prototypes() +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = "meta.alerted = meta.alerted or {}" +position = 'after' +payload = ''' +for _, t in ipairs{ + G.P_CENTERS, + G.P_BLINDS, + G.P_TAGS, + G.P_SEALS, +} do + for k, v in pairs(t) do + SMODS._save_d_u(v) + v._discovered_unlocked_overwritten = true + end +end''' +match_indent = true \ No newline at end of file diff --git a/Steamodded/lovely/menu.toml b/Steamodded/lovely/menu.toml new file mode 100644 index 0000000..8b20151 --- /dev/null +++ b/Steamodded/lovely/menu.toml @@ -0,0 +1,60 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local main_menu = nil''' +position = "after" +payload = '''local mods = nil''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''main_menu = UIBox_button{ label = {localize('b_main_menu')}, button = "go_to_menu", minw = 5}''' +position = "after" +payload = '''mods = UIBox_button{ id = "mods_button", label = {localize('b_mods')}, button = "mods_button", minw = 5}''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = '''G.ARGS.set_alerts_alertables[11].should_alert = alert_booster''' +position = "after" +payload = '''table.insert(G.ARGS.set_alerts_alertables, {id = 'mods_button', alert_uibox_name = 'mods_button_alert', should_alert = SMODS.mod_button_alert})''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''main_menu,''' +position = "after" +payload = '''mods,''' +match_indent = true + + +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = '''self.ASSET_ATLAS[self.asset_atli[i].name].image = love.graphics.newImage(self.asset_atli[i].path, {mipmaps = true, dpiscale = self.SETTINGS.GRAPHICS.texture_scaling})''' +position = 'after' +payload = ''' +local mipmap_level = SMODS.config.graphics_mipmap_level_options[SMODS.config.graphics_mipmap_level] +if mipmap_level and mipmap_level > 0 then + self.ASSET_ATLAS[self.asset_atli[i].name].image:setMipmapFilter('linear', mipmap_level) +end''' +match_indent = true + + +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''create_option_cycle({w = 4,scale = 0.8, label = localize("b_set_CRT_bloom"),options = localize('ml_bloom_opt'), opt_callback = 'change_crt_bloom', current_option = G.SETTINGS.GRAPHICS.bloom}),''' +position = 'after' +payload = ''' +create_option_cycle({label = localize('b_graphics_mipmap_level'),scale = 0.8, options = SMODS.config.graphics_mipmap_level_options, opt_callback = 'SMODS_change_mipmap', current_option = SMODS.config.graphics_mipmap_level}),''' +match_indent = true + diff --git a/Steamodded/lovely/mod.toml b/Steamodded/lovely/mod.toml new file mode 100644 index 0000000..0065c12 --- /dev/null +++ b/Steamodded/lovely/mod.toml @@ -0,0 +1,70 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Per-mod functions + +# end_round() +[[patches]] +[patches.regex] +target = 'functions/state_events.lua' +pattern = '''(?[\t ]*)reset_castle_card\(\)''' +line_prepend = '$indent' +position = 'after' +payload = ''' +for _, mod in ipairs(SMODS.mod_list) do + if mod.reset_game_globals and type(mod.reset_game_globals) == 'function' then + mod.reset_game_globals(false) + end +end''' + +# Game:start_run() +[[patches]] +[patches.regex] +target = 'game.lua' +pattern = '''(?[\t ]*)reset_castle_card\(\)''' +line_prepend = '$indent' +position = 'after' +payload = ''' +for _, mod in ipairs(SMODS.mod_list) do + if mod.reset_game_globals and type(mod.reset_game_globals) == 'function' then + mod.reset_game_globals(true) + end +end''' + +# Card:set_debuff() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "function Card:set_debuff(should_debuff)" +position = 'after' +match_indent = true +payload = ''' + for _, mod in ipairs(SMODS.mod_list) do + if mod.set_debuff and type(mod.set_debuff) == 'function' then + local res = mod.set_debuff(self) + if res == 'prevent_debuff' then + if self.debuff then + self.debuff = false + if self.area == G.jokers then self:add_to_deck(true) end + self.debuffed_by_blind = false + end + return + end + should_debuff = should_debuff or res + end + end + for k, v in pairs(self.ability.debuff_sources or {}) do + if v == 'prevent_debuff' then + if self.debuff then + self.debuff = false + if self.area == G.jokers then self:add_to_deck(true) end + end + self.debuffed_by_blind = false + return + end + should_debuff = should_debuff or v + end + +''' \ No newline at end of file diff --git a/Steamodded/lovely/number_formatting.toml b/Steamodded/lovely/number_formatting.toml new file mode 100644 index 0000000..10675b3 --- /dev/null +++ b/Steamodded/lovely/number_formatting.toml @@ -0,0 +1,171 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# +# Use number_format for... +# + +# DynaText + +[[patches]] +[patches.regex] +target = "engine/text.lua" +pattern = 'tostring\((?v\.ref_table and v\.ref_table\[v\.ref_value\] or v\.string)\)' +position = "at" +payload = "format_ui_value($param)" + +# Cash Out + +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = ''' +localize\('\$'\)\.\.config\.dollars''' +position = "at" +payload = "localize('$')..format_ui_value(config.dollars)" + +# End of round money + +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = ''' +localize\('\$'\)\.\.num_dollars\}''' +position = "at" +payload = "localize('$')..format_ui_value(num_dollars)}" + +# Tooltip numbers + +[[patches]] +[patches.regex] +target = "functions/misc_functions.lua" +pattern = '(?args\.vars\[tonumber\(subpart\[1\]\)\])' +position = "at" +payload = 'format_ui_value($param)' + +# Poker Hand chips + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}," +position = "at" +payload = "{n=G.UIT.T, config={text = number_format(G.GAME.hands[handname].chips, 1000000), scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}," +match_indent = true + +# Poker Hand mult + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}" +position = "at" +payload = "{n=G.UIT.T, config={text = number_format(G.GAME.hands[handname].mult, 1000000), scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}" +match_indent = true + +# Continue Run - Money + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = 'tostring\(saved_game\.GAME\.dollars\)' +position = "at" +payload = "format_ui_value(saved_game.GAME.dollars)" + +# Continue Run - Best Hand - bigger size + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = 'scale_number\(saved_game\.GAME\.round_scores\.hand\.amt\, 0\.8\*scale\)' +position = "at" +payload = "scale_number(saved_game.GAME.round_scores.hand.amt, 0.8*scale, 100000000000)" + + +# +# Custom sci notation switch point +# + +## number_format +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'function number_format(num)' +position = "at" +payload = ''' +function number_format(num, e_switch_point) + if type(num) ~= 'number' then return num end + + local sign = (num >= 0 and "") or "-" + num = math.abs(num)''' +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/misc_functions.lua" +pattern = 'num >= G\.E_SWITCH_POINT' +position = "at" +payload = "num >= (e_switch_point or G.E_SWITCH_POINT)" + +# 1. Fix floating point error (1.000e92 instead of 10.000e91) +# 2. Lower precision with higher numbers +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = ''' +return string.format("%.3f",x/(10^fac))..'e'..fac''' +position = "at" +payload = ''' +if num == math.huge then + return sign.."naneinf" +end + +local mantissa = round_number(x/(10^fac), 3) +if mantissa >= 10 then + mantissa = mantissa / 10 + fac = fac + 1 +end +return sign..(string.format(fac >= 100 and "%.1fe%i" or fac >= 10 and "%.2fe%i" or "%.3fe%i", mantissa, fac))''' +match_indent = true + +# Remove trailing zeroes +# E.g. X1.5 being displayed as X1.50 +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = ''' +return string.format(num ~= math.floor(num) and (num >= 100 and "%.0f" or num >= 10 and "%.1f" or "%.2f") or "%.0f", num):reverse():gsub("(%d%d%d)", "%1,"):gsub(",$", ""):reverse()''' +position = "at" +payload = ''' +local formatted +if num ~= math.floor(num) and num < 100 then + formatted = string.format(num >= 10 and "%.1f" or "%.2f", num) + if formatted:sub(-1) == "0" then + formatted = formatted:gsub("%.?0+$", "") + end + -- Return already to avoid comas being added + if num < 0.01 then return tostring(num) end +else + formatted = string.format("%.0f", num) +end +return sign..(formatted:reverse():gsub("(%d%d%d)", "%1,"):gsub(",$", ""):reverse())''' +match_indent = true + +## scale_number +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = 'function scale_number(number, scale, max)' +position = "at" +payload = 'function scale_number(number, scale, max, e_switch_point)' +match_indent = true + +[[patches]] +[patches.regex] +target = "functions/button_callbacks.lua" +pattern = 'number >= G\.E_SWITCH_POINT' +position = "at" +payload = "math.abs(number) >= (e_switch_point or G.E_SWITCH_POINT)" + diff --git a/Steamodded/lovely/playing_card.toml b/Steamodded/lovely/playing_card.toml new file mode 100644 index 0000000..bd43d27 --- /dev/null +++ b/Steamodded/lovely/playing_card.toml @@ -0,0 +1,299 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Playing Card API + +# Game:init_game_object() +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = 'function Game:init_game_object()' +position = 'after' +match_indent = true +payload = ''' + local cards_played = {} + for _,v in ipairs(SMODS.Rank.obj_buffer) do + cards_played[v] = { suits = {}, total = 0 } + end''' + +[[patches]] +[patches.regex] +target = "game.lua" +pattern = '(?[\t ]*)cards_played = \{\n(.*\n){13}[\t ]*\},' +position = 'at' +line_prepend = '$indent' +payload = ''' + cards_played = cards_played, + disabled_suits = {}, + disabled_ranks = {},''' + +# Game:start_run() +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = 'local _ = nil' +position = 'before' +match_indent = true +payload = ''' +if type(SMODS.Ranks[v.value].in_pool) == 'function' and not SMODS.Ranks[v.value]:in_pool({initial_deck = true}) +or type(SMODS.Suits[v.suit].in_pool) == 'function' and not SMODS.Suits[v.suit]:in_pool({initial_deck = true}) then + goto continue +end''' + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if self.GAME.starting_params.erratic_suits_and_ranks then _, k = pseudorandom_element(G.P_CARDS, pseudoseed('erratic')) end" +position = 'at' +match_indent = true +payload = '''if self.GAME.starting_params.erratic_suits_and_ranks then + v, k = pseudorandom_element(G.P_CARDS, pseudoseed('erratic'), {starting_deck = true}) +end''' + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = 'local _r, _s = string.sub(k, 3, 3), string.sub(k, 1, 1)' +position = 'at' +match_indent = true +payload = 'local _r, _s = SMODS.Ranks[v.value].card_key, SMODS.Suits[v.suit].card_key' + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if self.GAME.starting_params.no_faces and (_r == 'K' or _r == 'Q' or _r == 'J') then keep = false end" +position = 'at' +match_indent = true +payload = ''' +if self.GAME.starting_params.no_faces and SMODS.Ranks[v.value].face then keep = false end''' + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if keep then card_protos[#card_protos+1] = {s=_s,r=_r,e=_e,d=_d,g=_g} end" +position = "after" +payload = "::continue::" +match_indent = true + +# loc_colour() +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = 'return G.ARGS.LOC_COLOURS[_c] or _default or G.C.UI.TEXT_DARK' +position = 'before' +match_indent = true +payload = ''' + for _, v in ipairs(SMODS.Rarity.obj_buffer) do + G.ARGS.LOC_COLOURS[v:lower()] = G.C.RARITY[v] + end + for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do + G.ARGS.LOC_COLOURS[v:lower()] = G.C.SECONDARY_SET[v] + end + for _, v in ipairs(SMODS.Suit.obj_buffer) do + G.ARGS.LOC_COLOURS[v:lower()] = G.C.SUITS[v] + end''' + +# get_flush() +[[patches]] +[patches.regex] +target = "functions/misc_functions.lua" +pattern = '(?[\t ]*)local suits = \{\n[\t ]*"Spades",\n[\t ]*"Hearts",\n[\t ]*"Clubs",\n[\t ]*"Diamonds"\n[\t ]*\}\n[\t ]*if #hand > 5 or (?#hand < \(5 - \(four_fingers and 1 or 0\)\) then return ret else)' +position = 'at' +line_prepend = '$indent' +payload = ''' +local suits = SMODS.Suit.obj_buffer +if $restcond''' + +# get_X_same() +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = 'local vals = {{},{},{},{},{},{},{},{},{},{},{},{},{},{}}' +position = 'at' +match_indent = true +payload = ''' +local vals = {} +for i = 1, SMODS.Rank.max_id.value do + vals[i] = {} +end''' + +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = 'function get_X_same(num, hand)' +position = 'at' +match_indent = true +payload = '''function get_X_same(num, hand, or_more)''' + +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = 'if #curr == num then' +position = 'at' +match_indent = true +payload = '''if or_more and (#curr >= num) or (#curr == num) then''' + +# Card:get_nominal() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = 'function Card:get_nominal\(mod\)\n([\t ]+.*\n)*end' +position = 'at' +payload = ''' +function Card:get_nominal(mod) + local mult = 1 + local rank_mult = 1 + if mod == 'suit' then mult = 10000 end + if self.ability.effect == 'Stone Card' or (self.config.center.no_suit and self.config.center.no_rank) then + mult = -10000 + elseif self.config.center.no_suit then + mult = 0 + elseif self.config.center.no_rank then + rank_mult = 0 + end + return 10*self.base.nominal*rank_mult + self.base.suit_nominal*mult + (self.base.suit_nominal_original or 0)*0.0001*mult + 10*self.base.face_nominal*rank_mult + 0.000001*self.unique_val +end''' + +# Card:set_base() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "(?[\t ]*)if self.base.value == '2' then self.base.nominal = 2; self.base.id = 2(\n[\t ]+elseif .*)*" +position = 'at' +line_prepend = '$indent' +payload = ''' +local rank = SMODS.Ranks[self.base.value] or {} +self.base.nominal = rank.nominal or 0 +self.base.face_nominal = rank.face_nominal or 0 +self.base.id = rank.id''' + +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "(?[\t ]*)if self.base.suit == 'Diamonds' then self.base.suit_nominal = 0.01; self.base.suit_nominal_original = suit_base_nominal_original or 0.001 (\n[\t ]+elseif .*)*" +position = 'at' +line_prepend = '$indent' +payload = ''' +local suit = SMODS.Suits[self.base.suit] or {} +self.base.suit_nominal = suit.suit_nominal or 0 +self.base.suit_nominal_original = suit_base_nominal_original or suit.suit_nominal or 0''' + +# Card:change_suit() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "(?[\t ]*)local new_code = [\\s\\S]*?local new_val = [\\s\\S]*?local new_card = G.P_CARDS\\[new_code..new_val\\]" +position = 'at' +line_prepend = '$indent' +payload = ''' +local new_code = SMODS.Suits[new_suit].card_key +local new_val = SMODS.Ranks[self.base.value].card_key +local new_card = G.P_CARDS[new_code..'_'..new_val]''' + +# Card:is_face() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = "(?[\t ]*)if id == 11 or id == 12 or id == 13 or next\\(find_joker\\(\"Pareidolia\"\\)\\) then" +position = 'at' +line_prepend = '$indent' +payload = ''' +local rank = SMODS.Ranks[self.base.value] +if not id then return end +if (id > 0 and rank and rank.face) or next(find_joker("Pareidolia")) then''' + + +# tally_sprite() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '(?[\t ]*local t_s = Sprite\(0,0,0.5,0.5,)G.ASSET_ATLAS\[.*?\](?.*?\))' +position = 'at' +payload = '$start G.ASSET_ATLAS[suit and SMODS.Suits[suit][G.SETTINGS.colourblind_option and "hc_ui_atlas" or "lc_ui_atlas"]] or G.ASSET_ATLAS[("ui_"..(G.SETTINGS.colourblind_option and "2" or "1"))]$rest' + +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = 'function tally_sprite(pos, value, tooltip)' +position = 'at' +match_indent = true +payload = 'function tally_sprite(pos, value, tooltip, suit)' + +# G.UIDEF.challenge_description_tab() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = "(?[\t ]*)local SUITS = \\{(\n.*){5}\n[\t ]*local suit_map = \\{'S', 'H', 'C', 'D'\\}" +position = 'at' +line_prepend = '$indent' +payload = ''' +local SUITS = {} +local suit_map = {} +for i = #SMODS.Suit.obj_buffer, 1, -1 do + local suit = SMODS.Suits[SMODS.Suit.obj_buffer[i]] + SUITS[suit.card_key] = {} + suit_map[#suit_map+1] = suit.card_key +end''' + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = 'local _r, _s = string.sub(k, 3, 3), string.sub(k, 1, 1)' +position = 'at' +match_indent = true +payload = 'local _r, _s = SMODS.Ranks[v.value].card_key, SMODS.Suits[v.suit].card_key' + +# TODO there may need to be a way to let in_pool know what challenge is being displayed +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = 'local keep, _e, _d, _g = true, nil, nil, nil' +position = 'after' +match_indent = true +payload = ''' +if type(SMODS.Ranks[v.value].in_pool) == 'function' and not SMODS.Ranks[v.value]:in_pool({initial_deck = true}) then + keep = false +end +if type(SMODS.Suits[v.suit].in_pool) == 'function' and not SMODS.Suits[v.suit]:in_pool({initial_deck = true}) then + keep = false +end''' + +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '(?[\t ]*)for j = 1, 4 do\n[\t ]*(?if SUITS\[suit_map\[j\]\]\[1\] then\n[\t ]*table.sort.*(\n.*)*?)\n[\t ]*0\.42\*G.CARD_H,' +position = 'at' +line_prepend = '$indent' +payload = ''' +local num_suits = 0 +for j = 1, #suit_map do + if SUITS[suit_map[j]][1] then num_suits = num_suits + 1 end +end +for j = 1, #suit_map do + $mid + (0.42 - (num_suits <= 4 and 0 or num_suits >= 8 and 0.28 or 0.07 * (num_suits - 4))) * G.CARD_H,''' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = '--Fill all remaining info if this is the main desc' +position = 'before' +match_indent = true +payload = '''if card_type == 'Default' or card_type == 'Enhanced' and not _c.replace_base_card and card and card.base then + if not _c.no_suit then + local suit = SMODS.Suits[card.base.suit] or {} + if suit.loc_vars and type(suit.loc_vars) == 'function' then + suit:loc_vars(info_queue, card) + end + end + if not _c.no_rank then + local rank = SMODS.Ranks[card.base.value] or {} + if rank.loc_vars and type(rank.loc_vars) == 'function' then + rank:loc_vars(info_queue, card) + end + end +end + +''' diff --git a/Steamodded/lovely/poker_hand.toml b/Steamodded/lovely/poker_hand.toml new file mode 100644 index 0000000..14e8c50 --- /dev/null +++ b/Steamodded/lovely/poker_hand.toml @@ -0,0 +1,60 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Poker Hand API + +# evaluate_poker_hand() +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "local parts = {" +position = 'before' +payload = ''' +for _,v in ipairs(SMODS.PokerHand.obj_buffer) do + results[v] = {} +end''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "if next(parts._5) and next(parts._flush) then" +position = 'before' +payload = ''' +for _,_hand in pairs(SMODS.PokerHands) do + if _hand.atomic_part and type(_hand.atomic_part) == 'function' then + parts[_hand.key] = _hand.atomic_part(hand) + end +end''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "return results" +position = 'before' +payload = ''' +for _,_hand in pairs(SMODS.PokerHands) do + if _hand.composite and type(_hand.composite) == 'function' then + local other_hands + results[_hand.key], other_hands = _hand.composite(parts) + results[_hand.key] = results[_hand.key] or {} + if other_hands and type(other_hands) == 'table' then + for k, v in pairs(other_hands) do + results[k] = v + end + end + else + results[_hand.key] = parts[_hand.key] + end +end +results.top = nil +for _, v in ipairs(G.handlist) do + if not results.top and results[v] then + results.top = results[v] + break + end +end''' +match_indent = true diff --git a/Steamodded/lovely/pool.toml b/Steamodded/lovely/pool.toml new file mode 100644 index 0000000..90cf268 --- /dev/null +++ b/Steamodded/lovely/pool.toml @@ -0,0 +1,185 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Functions that affect random selection from pools + +# pseudorandom_element() +# TODO special cases for now +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "function pseudorandom_element(_t, seed)" +position = "at" +payload = """function pseudorandom_element(_t, seed, args) + -- TODO special cases for now + -- Preserves reverse nominal order for Suits, nominal+face_nominal order for Ranks + -- for vanilla RNG + if _t == SMODS.Suits then + _t = SMODS.Suit:obj_list(true) + end + if _t == SMODS.Ranks then + _t = SMODS.Rank:obj_list() + end +""" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "keys[#keys+1] = {k = k,v = v}" +position = "at" +payload = """ +local keep = true +local in_pool_func = + args and args.in_pool + or type(v) == 'table' and type(v.in_pool) == 'function' and v.in_pool + or _t == G.P_CARDS and function(c) + --Handles special case for Erratic Deck + local initial_deck = args and args.starting_deck or false + + return not ( + type(SMODS.Ranks[c.value].in_pool) == 'function' and not SMODS.Ranks[c.value]:in_pool({initial_deck = initial_deck}) + or type(SMODS.Suits[c.suit].in_pool) == 'function' and not SMODS.Suits[c.suit]:in_pool({initial_deck = initial_deck}) + ) + end +if in_pool_func then + keep = in_pool_func(v, args) +end +if keep then + keys[#keys+1] = {k = k,v = v} +end""" +match_indent = true + +# fixes pseudorandom_element on an empty list +# nil, nil is returned in that case +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "local key = keys[math.random(#keys)].k" +position = "before" +payload = "if #keys == 0 then return nil, nil end" +match_indent = true + +## get_current_pool() + +# Centers + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "else _starting_pool, _pool_key = G.P_CENTER_POOLS[_type], _type..(_append or '')" +match_indent = true +position = 'before' +payload = ''' +elseif SMODS.ObjectTypes[_type] and SMODS.ObjectTypes[_type].rarities then + local rarities = SMODS.ObjectTypes[_type].rarities + local rarity + if _legendary and rarities.legendary then + rarity = rarities.legendary.key + else + rarity = _rarity or SMODS.poll_rarity(_type, 'rarity_'.._type..G.GAME.round_resets.ante..(_append or '')) + end + _starting_pool, _pool_key = SMODS.ObjectTypes[_type].rarity_pools[rarity], _type..rarity..(_append or '')''' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if _type == 'Tarot' or _type == 'Tarot_Planet' then _pool[#_pool + 1] = \"c_strength\"" +match_indent = true +position = 'at' +payload = ''' +if SMODS.ObjectTypes[_type] and SMODS.ObjectTypes[_type].default and G.P_CENTERS[SMODS.ObjectTypes[_type].default] then + _pool[#_pool+1] = SMODS.ObjectTypes[_type].default +elseif _type == 'Tarot' or _type == 'Tarot_Planet' then _pool[#_pool + 1] = "c_strength"''' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if v.name == 'Black Hole' or v.name == 'The Soul' then" +match_indent = true +position = 'at' +payload = "if v.name == 'Black Hole' or v.name == 'The Soul' or v.hidden then" + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if _type == 'Enhanced' then" +match_indent = true +position = 'before' +payload = ''' +local in_pool, pool_opts +if v.in_pool and type(v.in_pool) == 'function' then + in_pool, pool_opts = v:in_pool({ source = _append }) +end +pool_opts = pool_opts or {} +''' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = 'elseif not (G.GAME.used_jokers[v.key] and not next(find_joker("Showman"))) and' +match_indent = true +position = 'at' +payload = '''elseif not (G.GAME.used_jokers[v.key] and not pool_opts.allow_duplicates and not next(find_joker("Showman"))) and''' + +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if add and not G.GAME.banned_keys[v.key] then" +match_indent = true +position = 'before' +payload = ''' +if v.in_pool and type(v.in_pool) == 'function' then + add = in_pool and (add or pool_opts.override_base_checks) +end +''' + +## G.GAME.used_jokers now checks keys, not names +# Card:set_ability() +# Remove the old center from `used_jokers` if set_ability overrides +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "local old_center = self.config.center" +position = 'after' +payload = ''' +if old_center and not next(SMODS.find_card(old_center.key, true)) then + G.GAME.used_jokers[old_center.key] = nil +end''' +match_indent = true +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +(?[\t ]*)for k, v in pairs\(G\.P_CENTERS\) do +[\t ]*if v\.name == self\.ability\.name then +[\t ]*G\.GAME\.used_jokers\[k\] = true +[\t ]*end +[\t ]*end''' +position = "at" +payload = ''' +if self.config.center.key then + G.GAME.used_jokers[self.config.center.key] = true +end +''' +line_prepend = "$indent" +# Card:remove() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = ''' +(?[\t ]*)for k, v in pairs\(G\.P_CENTERS\) do +[\t ]*if v\.name == self\.ability\.name then +[\t ]*if not next\(find_joker\(self\.ability\.name, true\)\) then +[\t ]*G\.GAME\.used_jokers\[k\] = nil +[\t ]*end +[\t ]*end +[\t ]*end''' +position = "at" +payload = ''' +if not next(SMODS.find_card(self.config.center.key, true)) then + G.GAME.used_jokers[self.config.center.key] = nil +end''' +line_prepend = "$indent" diff --git a/Steamodded/lovely/rarity.toml b/Steamodded/lovely/rarity.toml new file mode 100644 index 0000000..4573811 --- /dev/null +++ b/Steamodded/lovely/rarity.toml @@ -0,0 +1,59 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Rarity API + +# get_badge_colour +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = 'return G.BADGE_COL[key] or {1, 0, 0, 1}' +position = 'before' +match_indent = true +payload = ''' +for k, v in pairs(SMODS.Rarity.obj_buffer) do + G.BADGE_COL[k] = G.C.RARITY[v] +end''' + +# G.UIDEF.card_h_popup +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "if AUT.card_type == 'Joker' or (AUT.badges and AUT.badges.force_rarity) then card_type = ({localize('k_common'), localize('k_uncommon'), localize('k_rare'), localize('k_legendary')})[card.config.center.rarity] end" +position = "at" +payload = "if AUT.card_type == 'Joker' or (AUT.badges and AUT.badges.force_rarity) then card_type = SMODS.Rarity:get_rarity_badge(card.config.center.rarity) end" +match_indent = true + +# Game:update +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "self.C.EDITION[2] = 0.7+0.2*(1+math.sin(self.TIMERS.REAL*1.5 + 6))" +position = "after" +payload = ''' +for k, v in pairs(SMODS.Rarities) do + if v.gradient and type(v.gradient) == "function" then v:gradient(dt) end +end''' +match_indent = true + +# get_current_pool +[[patches]] +[patches.regex] +target = "functions/common_events.lua" +pattern = '''(?[\t ]*)local rarity = _rarity or pseudorandom\('rarity'\.\.G\.GAME\.round_resets\.ante\.\.\(_append or ''\)\) \n[\s\S]{12}rarity = \(_legendary and 4\) or \(rarity > 0\.95 and 3\) or \(rarity > 0\.7 and 2\) or 1''' +position = "at" +payload = ''' +_rarity = (_legendary and 4) or (type(_rarity) == "number" and ((_rarity > 0.95 and 3) or (_rarity > 0.7 and 2) or 1)) or _rarity +local rarity = _rarity or SMODS.poll_rarity("Joker", 'rarity'..G.GAME.round_resets.ante..(_append or '')) +''' +## Ensure that other cards set to string rarity work the same as set for int rarity +# Card:calculate_joker +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.ability.name == 'Baseball Card' and context.other_joker.config.center.rarity == 2 and self ~= context.other_joker then" +position = "at" +payload = '''if self.ability.name == 'Baseball Card' and (context.other_joker.config.center.rarity == 2 or context.other_joker.config.center.rarity == "Uncommon") and self ~= context.other_joker then''' +match_indent = true \ No newline at end of file diff --git a/Steamodded/lovely/seal.toml b/Steamodded/lovely/seal.toml new file mode 100644 index 0000000..ad922d3 --- /dev/null +++ b/Steamodded/lovely/seal.toml @@ -0,0 +1,233 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Seal API +# Card:open() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = ''' +(?[\t ]*)local seal_rate = 10 +[\n\t ]*local seal_poll = pseudorandom\(pseudoseed\('stdseal'..G.GAME.round_resets.ante\)\) +[\n\t ]*if seal_poll > 1 - 0.02\*seal_rate then +[\n\t ]*local seal_type = pseudorandom\(pseudoseed\('stdsealtype'..G.GAME.round_resets.ante\)\) +[\n\t ]*if seal_type > 0.75 then card:set_seal\('Red'\) +[\n\t ]*elseif seal_type > 0.5 then card:set_seal\('Blue'\) +[\n\t ]*elseif seal_type > 0.25 then card:set_seal\('Gold'\) +[\n\t ]*else card:set_seal\('Purple'\) +[\n\t ]*end +[\n\t ]*end''' +position = 'at' +line_prepend = '$indent' +payload = ''' +card:set_seal(SMODS.poll_seal({mod = 10}))''' + +# Card:calculate_joker() +[[patches]] +[patches.regex] +target = 'card.lua' +pattern = ''' +(?[\t ]*)local seal_type = pseudorandom\(pseudoseed\('certsl'\)\) +[\n\t ]*if seal_type > 0.75 then _card:set_seal\('Red', true\) +[\n\t ]*elseif seal_type > 0.5 then _card:set_seal\('Blue', true\) +[\n\t ]*elseif seal_type > 0.25 then _card:set_seal\('Gold', true\) +[\n\t ]*else _card:set_seal\('Purple', true\) +[\n\t ]*end''' +position = 'at' +line_prepend = '$indent' +payload = '''_card:set_seal(SMODS.poll_seal({guaranteed = true, type_key = 'certsl'}))''' + +# get_badge_colour() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = 'return G.BADGE_COL[key] or {1, 0, 0, 1}' +position = 'before' +match_indent = true +payload = ''' +for k, v in pairs(SMODS.Seals) do + G.BADGE_COL[k:lower()..'_seal'] = v.badge_colour +end''' + +# Card:calculate_seal() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = 'function Card:calculate_seal\(context\)\n(?[\t ]*)if self.debuff then return nil end' +position = 'after' +line_prepend = '$indent' +payload = ''' +local obj = G.P_SEALS[self.seal] or {} +if obj.calculate and type(obj.calculate) == 'function' then + local o = obj:calculate(self, context) + if o then return o end +end''' + +# Card:update() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = 'if G.STAGE == G.STAGES.RUN then' +position = 'before' +match_indent = true +payload = ''' +local obj = G.P_SEALS[self.seal] or {} +if obj.update and type(obj.update) == 'function' then + obj:update(self, dt) +end''' + +# G.FUNCS.evaluate_play() +# Allow seals to return mult, chips and xmult like jokers. +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "--If x_mult added, do mult add event and mult the mult to the total" +match_indent = true +position = "before" +payload = ''' +if effects[ii].seals then + if effects[ii].seals.chips then + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips + effects[ii].seals.chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'chips', effects[ii].seals.chips, percent) + end + + if effects[ii].seals.mult then + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult + effects[ii].seals.mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'mult', effects[ii].seals.mult, percent) + end + + if effects[ii].seals.p_dollars then + if effects[ii].card then juice_card(effects[ii].card) end + ease_dollars(effects[ii].seals.p_dollars) + card_eval_status_text(scoring_hand[i], 'dollars', effects[ii].seals.p_dollars, percent) + end + + if effects[ii].seals.dollars then + if effects[ii].card then juice_card(effects[ii].card) end + ease_dollars(effects[ii].seals.dollars) + card_eval_status_text(scoring_hand[i], 'dollars', effects[ii].seals.dollars, percent) + end + + if effects[ii].seals.x_mult then + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult*effects[ii].seals.x_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'x_mult', effects[ii].seals.x_mult, percent) + end + + if effects[ii].seals.func then + effects[ii].seals.func() + end +end + +''' + +# Card:get_p_dollars() +[[patches]] +[patches.regex] +target = "card.lua" +pattern = '''(?[\t ]*)if (?self\.seal == 'Gold' then\n)''' +position = 'at' +line_prepend = '$indent' +payload = ''' +local obj = G.P_SEALS[self.seal] or {} +if obj.get_p_dollars and type(obj.get_p_dollars) == 'function' then + ret = ret + obj:get_p_dollars(self) +elseif $cond''' + +# generate_card_ui() +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if v == 'gold_seal'*" +match_indent = true +position = 'before' +payload = ''' +local seal = SMODS.Seals[v] or SMODS.Seal.badge_to_key[v] and SMODS.Seals[SMODS.Seal.badge_to_key[v]] +if seal and seal.generate_ui ~= 0 then + local t = { key = v, set = 'Other' } + info_queue[#info_queue+1] = t + if seal.loc_vars and type(seal.loc_vars) == 'function' then + local res = seal:loc_vars(info_queue, card) or {} + t.vars = res.vars + t.key = res.key or t.key + end +else''' +[[patches]] +[patches.pattern] +target = 'functions/common_events.lua' +pattern = "if v == 'purple_seal'*" +match_indent = true +position = 'after' +payload = 'end' + +# Card:update_alert() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "function Card:update_alert()" +match_indent = true +position = 'after' +payload = ''' + if self.ability.set == 'Default' and self.config.center and self.config.center.key == 'c_base' and self.seal then + if G.P_SEALS[self.seal].alerted and self.children.alert then + self.children.alert:remove() + self.children.alert = nil + elseif not G.P_SEALS[self.seal].alerted and not self.children.alert and G.P_SEALS[self.seal].discovered then + self.children.alert = UIBox{ + definition = create_UIBox_card_alert(), + config = {align="tli", + offset = {x = 0.1, y = 0.1}, + parent = self} + } + end + end''' + +# Card:hover() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = "G:save_progress()" +match_indent = false +position = "after" +payload = ''' + elseif self.children.alert and self.seal and not G.P_SEALS[self.seal].alerted then + G.P_SEALS[self.seal].alerted = true + G:save_progress()''' + +# Game:init_item_prototypes() +[[patches]] +[patches.regex] +target = 'game.lua' +pattern = '''(?[\t ]*)Gold =[ {A-z=1-4,"}\n]*},[\n\t ]*}''' +position = 'at' +line_prepend = '$indent' +payload = ''' +Red = {order = 1, discovered = false, set = "Seal"}, +Blue = {order = 2, discovered = false, set = "Seal"}, +Gold = {order = 3, discovered = false, set = "Seal"}, +Purple = {order = 4, discovered = false, set = "Seal"}, +} +''' + +# Card:set_seal() +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = '''G.CONTROLLER.locks.seal = true''' +position = 'after' +match_indent = true +payload = '''local sound = G.P_SEALS[_seal].sound or {sound = 'gold_seal', per = 1.2, vol = 0.4}''' +[[patches]] +[patches.pattern] +target = 'card.lua' +pattern = '''play_sound('gold_seal', 1.2, 0.4)''' +position = 'at' +match_indent = true +payload = '''play_sound(sound.sound, sound.per, sound.vol)''' diff --git a/Steamodded/lovely/sound.toml b/Steamodded/lovely/sound.toml new file mode 100644 index 0000000..cc77840 --- /dev/null +++ b/Steamodded/lovely/sound.toml @@ -0,0 +1,167 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +#modulate_sound() +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = 'G.SOUND_MANAGER.channel:push(G.ARGS.push)' +match_indent = true +position = 'after' +payload = ''' +SMODS.previous_track = SMODS.previous_track or '' +local in_sync = (SMODS.Sounds[desired_track] or {}).sync +local out_sync = (SMODS.Sounds[SMODS.previous_track] or {}).sync +local should_sync = true +if (type(in_sync) == 'table' and not in_sync[SMODS.previous_track]) or in_sync == false then should_sync = false end +if (type(out_sync) == 'table' and not out_sync[desired_track]) or out_sync == false then should_sync = false end +if + SMODS.previous_track and SMODS.previous_track ~= desired_track and + not should_sync +then + G.ARGS.push.type = 'restart_music' + G.SOUND_MANAGER.channel:push(G.ARGS.push) +end +SMODS.previous_track = desired_track''' + +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = 'G.ARGS.push.ambient_control = G.SETTINGS.ambient_control' +match_indent = true +position = 'after' +payload = ''' +if SMODS.remove_replace_sound and SMODS.remove_replace_sound ~= desired_track then + SMODS.Sound.replace_sounds[SMODS.remove_replace_sound] = nil + SMODS.remove_replace_sound = nil +end +local replace_sound = SMODS.Sound.replace_sounds[desired_track] +if replace_sound then + local replaced_track = desired_track + desired_track = replace_sound.key + G.ARGS.push.desired_track = desired_track + if SMODS.previous_track ~= desired_track then + if replace_sound.times > 0 then replace_sound.times = replace_sound.times - 1 end + if replace_sound.times == 0 then SMODS.remove_replace_sound = replaced_track end + end +end +local stop_sound = SMODS.Sound.stop_sounds[desired_track] +if SMODS.Sound.stop_sounds[desired_track] then + if SMODS.previous_track ~= '' and stop_sound > 0 then stop_sound = stop_sound - 1 end + SMODS.Sound.stop_sounds[desired_track] = stop_sound ~= 0 and stop_sound or nil + SMODS.previous_track = '' + return +end +''' + +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = "(G.STATE == G.STATES.SPLASH and '') or" +match_indent = true +position = 'after' +payload = 'SMODS.Sound:get_current_music() or' + +# PLAY_SOUND +[[patches]] +[patches.pattern] +target = 'engine/sound_manager.lua' +pattern = '''local s = {sound = love.audio.newSource("resources/sounds/"..args.sound_code..'.ogg', should_stream and "stream" or 'static')}''' +match_indent = true +position = 'at' +payload = ''' +local c = SMODS_Sounds[args.sound_code] +local s = c and +{sound = love.audio.newSource(love.sound.newDecoder(c.data), c.should_stream and 'stream' or 'static'), per = c.per, vol = c.vol } or +{sound = love.audio.newSource("resources/sounds/"..args.sound_code..'.ogg', should_stream and "stream" or 'static')}''' + +# pass in custom sounds +[[patches]] +[patches.pattern] +target = 'engine/sound_manager.lua' +pattern = "DISABLE_SFX = false" +match_indent = true +position = 'after' +payload = ''' +SMODS_Sounds = {} +''' +[[patches]] +[patches.pattern] +target = 'engine/sound_manager.lua' +pattern = "elseif request.type == 'stop' then" +match_indent = true +position = 'before' +payload = ''' +elseif request.type == 'sound_source' then + SMODS_Sounds[request.sound_code] = { + sound_code = request.sound_code, + data = request.data, + sound = sound, + per = request.per, + vol = request.vol, + } + SOURCES[request.sound_code] = {} +''' + +[[patches]] +[patches.pattern] +target = 'engine/sound_manager.lua' +pattern = "s.original_pitch = args.per or 1" +match_indent = true +position = 'at' +payload = 's.original_pitch = ((args.type ~= "sound") and s.per) or args.per or 1' + +[[patches]] +[patches.pattern] +target = 'engine/sound_manager.lua' +pattern = "s.original_volume = args.vol or 1" +match_indent = true +position = 'at' +payload = 's.original_volume = ((args.type ~= "sound") and s.vol) or args.vol or 1' + +# don't crash RESTART_MUSIC + +[[patches]] +[patches.pattern] +target = 'engine/sound_manager.lua' +pattern = "RESTART_MUSIC()" +match_indent = true +position = 'at' +payload = 'RESTART_MUSIC(request)' + +# fix looping for music of different length + +[[patches]] +[patches.regex] +target = 'engine/sound_manager.lua' +pattern = """(?[\t ]*)function MODULATE\\(args\\)(\n.*){9}""" +line_prepend = '$indent' +position = 'at' +payload = """function MODULATE(args) + if args.desired_track ~= '' then + local sound = ((SOURCES[current_track or {}] or {})[1] or {}).sound + if not sound or not sound:isPlaying() then + RESTART_MUSIC(args) + end + end +""" +[[patches]] +[patches.pattern] +target = 'engine/sound_manager.lua' +pattern = "for _, s in pairs(v) do" +match_indent = true +position = 'at' +payload = """current_track = args.desired_track +for _, s in pairs(v) do""" + +# [[patches]] +# [patches.pattern] +# target = 'engine/sound_manager.lua' +# pattern = 'if s.sound and not s.sound:isPlaying() then' +# match_indent = true +# position = 'at' +# payload = '''if s.sound and s.sound:isPlaying() then +# s.sound:stop() +# elseif s.sound and not s.sound:isPlaying() then''' \ No newline at end of file diff --git a/Steamodded/lovely/stake.toml b/Steamodded/lovely/stake.toml new file mode 100644 index 0000000..07a5486 --- /dev/null +++ b/Steamodded/lovely/stake.toml @@ -0,0 +1,190 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +# Fix areas where highest stake is hardcoded as Gold Stake +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "if G.PROFILES[G.SETTINGS.profile].all_unlocked then max_stake = 8 end" +position = "at" +payload = "if G.PROFILES[G.SETTINGS.profile].all_unlocked then max_stake = #G.P_CENTER_POOLS['Stake'] end" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "for i = 1, math.min(max_stake+1, 8) do" +position = "at" +payload = "for i = 1, math.min(max_stake+1, #G.P_CENTER_POOLS['Stake']) do" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "if G.GAME.stake >= 8 then" +position = "at" +payload = "if G.GAME.stake >= #G.P_CENTER_POOLS['Stake'] then" +match_indent = true + +# Stake modifier API +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if self.GAME.stake >= 2 then" +position = "before" +payload = "if false then" +match_indent = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if self.GAME.stake >= 8 then self.GAME.modifiers.enable_rentals_in_shop = true end" +position = "after" +payload = "end SMODS.setup_stake(self.GAME.stake)" +match_indent = true + +# Stake shininess API +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "if _stake == 8 then" +position = "at" +payload = "if G.P_CENTER_POOLS['Stake'][_stake].shiny then" +match_indent = true + +# Override stake listing to make room for our recursive version +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "for i = G.GAME.stake-1, 2, -1 do" +position = "before" +payload = "if false then" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = 'other_col = {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = G.C.L_BLACK}, nodes=stake_desc_rows}' +position = "before" +payload = "end SMODS.applied_stakes_UI(G.GAME.stake, stake_desc_rows)" +match_indent = true + +# Set win stake to that specified in unlocked stake +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'for i = 1, G.GAME.stake do' +position = "at" +payload = '''for i = 1, +(G.P_CENTER_POOLS["Stake"][G.GAME.stake].unlocked_stake) and +(G.P_STAKES[G.P_CENTER_POOLS["Stake"][G.GAME.stake].unlocked_stake].stake_level-1) or (G.GAME.stake-1) +do''' +match_indent = true + +# Stake Sprites +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'local stake_sprite = Sprite(0,0,_scale*1,_scale*1,G.ASSET_ATLAS["chips"], G.P_CENTER_POOLS.Stake[_stake].pos)' +position = "at" +payload = 'local stake_sprite = Sprite(0,0,_scale*1,_scale*1,G.ASSET_ATLAS[G.P_CENTER_POOLS.Stake[_stake].atlas], G.P_CENTER_POOLS.Stake[_stake].pos)' +match_indent = true + +# Achievements and unlocks +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = 'if highest_win >= 2 then' +position = "at" +payload = 'if highest_win >= G.P_STAKES["stake_red"].stake_level then' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = 'if highest_win >= 4 then' +position = "at" +payload = 'if highest_win >= G.P_STAKES["stake_black"].stake_level then' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = 'if highest_win >= 8 then' +position = "at" +payload = 'if highest_win >= G.P_STAKES["stake_gold"].stake_level then' +match_indent = true + +# get_blind_amount +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'function get_blind_amount(ante)' +position = "after" +payload = '''if G.GAME.modifiers.scaling and G.GAME.modifiers.scaling > 3 then return SMODS.get_blind_amount(ante) end''' +match_indent = true + +# set_joker_usage +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = {count = 1, order = v.config.center.order, wins = {}, losses = {}}' +position = "at" +payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = {count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}' +match_indent = true + +# set_joker_win +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] or {count = 1, order = v.config.center.order, wins = {}, losses = {}}' +position = "at" +payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] or {count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}' +match_indent = true + +#set_joker_win +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins[G.GAME.stake] or 0) + 1' +position = "after" +payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins_by_key[SMODS.stake_from_index(G.GAME.stake)] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1' +match_indent = true + +#set_joker_loss +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses[G.GAME.stake] or 0) + 1' +position = "after" +payload = 'G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses_by_key[SMODS.stake_from_index(G.GAME.stake)] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1' +match_indent = true + +# set_deck_usage +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}}' +position = "at" +payload = 'G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}}' +match_indent = true + +# set_deck_loss +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = 'if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}} end' +position = "at" +payload = 'if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} end' +match_indent = true + +# G.UIDEF.viewed_stake_option +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = 'G.viewed_stake = math.min(max_stake+1, G.viewed_stake)' +position = "after" +payload = '''if G.viewed_stake > #G.P_CENTER_POOLS.Stake then G.viewed_stake = #G.P_CENTER_POOLS.Stake end''' +match_indent = true diff --git a/Steamodded/lovely/sticker.toml b/Steamodded/lovely/sticker.toml new file mode 100644 index 0000000..d077e47 --- /dev/null +++ b/Steamodded/lovely/sticker.toml @@ -0,0 +1,148 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Sticker API + +# generate_UIBox_ability_table() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.sticker or ((self.sticker_run and self.sticker_run~='NONE') and G.SETTINGS.run_stake_stickers) then loc_vars = loc_vars or {}; loc_vars.sticker=(self.sticker or self.sticker_run) end" +position = "before" +match_indent = true +payload = ''' +for k, v in ipairs(SMODS.Sticker.obj_buffer) do + if self.ability[v] and not SMODS.Stickers[v].hide_badge then + badges[#badges+1] = v + end +end''' + +# generate_card_ui() +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if v == 'eternal' then*" +match_indent = true +position = "before" +payload = ''' +local sticker = SMODS.Stickers[v] +if sticker then + local t = { key = v, set = 'Other' } + local res = {} + if sticker.loc_vars and type(sticker.loc_vars) == 'function' then + res = sticker:loc_vars(info_queue, card) or {} + t.vars = res.vars or {} + t.key = res.key or t.key + end + info_queue[#info_queue+1] = t +else''' + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if v == 'rental' then*" +match_indent = true +position = "after" +payload = '''end''' + +# create_card() +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if card.ability.consumeable and not skip_materialize then card:start_materialize() end" +position = "after" +match_indent = true +payload = ''' +for k, v in ipairs(SMODS.Sticker.obj_buffer) do + local sticker = SMODS.Stickers[v] + if sticker.should_apply and type(sticker.should_apply) == 'function' and sticker:should_apply(card, center, area) then + sticker:apply(card, true) + end +end''' + +## Remove base game sticker rolls if one is added +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.7 then" +position = "at" +match_indent = true +payload = '''if G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.7 and not SMODS.Stickers["eternal"].should_apply then''' + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "elseif G.GAME.modifiers.enable_perishables_in_shop and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7)) then" +position = "at" +match_indent = true +payload = '''elseif G.GAME.modifiers.enable_perishables_in_shop and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7)) and not SMODS.Stickers["perishable"].should_apply then''' + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if G.GAME.modifiers.enable_rentals_in_shop and pseudorandom((area == G.pack_cards and 'packssjr' or 'ssjr')..G.GAME.round_resets.ante) > 0.7 then" +position = "at" +match_indent = true +payload = '''if G.GAME.modifiers.enable_rentals_in_shop and pseudorandom((area == G.pack_cards and 'packssjr' or 'ssjr')..G.GAME.round_resets.ante) > 0.7 and not SMODS.Stickers["rental"].should_apply then''' + +# Card:draw() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = '''elseif self.sprite_facing == 'back' then''' +match_indent = true +position = "before" +payload = ''' +for k, v in pairs(SMODS.Stickers) do + if self.ability[v.key] then + if v and v.draw and type(v.draw) == 'function' then + v:draw(self, layer) + else + G.shared_stickers[v.key].role.draw_major = self + G.shared_stickers[v.key]:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_stickers[v.key]:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) + end + end +end +''' + +# Card:calculate_joker() +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "function Card:calculate_joker(context)" +position = 'after' +payload = ''' +for k, v in pairs(SMODS.Stickers) do + if self.ability[v.key] then + if v.calculate and type(v.calculate) == 'function' then + local override_card = v:calculate(self, context) + if override_card then return override_card end + end + end +end''' +match_indent = true + +# get_badge_colour() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = 'return G.BADGE_COL[key] or {1, 0, 0, 1}' +position = 'before' +match_indent = true +payload = ''' +for k, v in pairs(SMODS.Stickers) do + G.BADGE_COL[k] = v.badge_colour +end''' + +## Remove Pinned effect when in Sticker collections +# CardArea:aling_cards +[[patches]] +[patches.pattern] +target = 'cardarea.lua' +pattern = '''table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end)''' +position = 'at' +match_indent = true +payload = '''table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*((a.pinned and not a.ignore_pinned) and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*((b.pinned and not b.ignore_pinned) and b.sort_id or 0) end)''' \ No newline at end of file diff --git a/Steamodded/lovely/tag.toml b/Steamodded/lovely/tag.toml new file mode 100644 index 0000000..de0c05b --- /dev/null +++ b/Steamodded/lovely/tag.toml @@ -0,0 +1,101 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Tag API +# Tag:apply_to_run() +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = "function Tag:apply_to_run(_context)" +position = 'after' +match_indent = true +payload = ''' + if self.triggered then return end + local obj = SMODS.Tags[self.key] + local res + if obj and obj.apply and type(obj.apply) == 'function' then + res = obj:apply(self, _context) + end + if res then return res end +''' + +# Tag:set_ability() +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = "function Tag:set_ability()" +position = 'after' +match_indent = true +payload = ''' + local obj = SMODS.Tags[self.key] + local res + if obj and obj.set_ability and type(obj.set_ability) == 'function' then + obj:set_ability(self) + end +''' + +# create_UIBox_your_collection_tags() +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = "(?[\t ]*)local tag_matrix = \\{(\n.*){6}" +position = 'at' +line_prepend = '$indent' +payload = ''' +local tag_matrix = {} +local counter = 0 +local tag_tab = {} +local tag_pool = {} +if G.ACTIVE_MOD_UI then + for k, v in pairs(G.P_TAGS) do + if v.mod and G.ACTIVE_MOD_UI.id == v.mod.id then tag_pool[k] = v end + end +else + tag_pool = G.P_TAGS +end +for k, v in pairs(tag_pool) do + counter = counter + 1 + tag_tab[#tag_tab+1] = v +end +for i = 1, math.ceil(counter / 6) do + table.insert(tag_matrix, {}) +end''' + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = '''(?[\t ]*)v\.children\.alert\.states\.collide\.can = false\n[\s\S]{8}end\n[\s\S]{8}return true\n[\s\S]{4}end\)\n[\s\S]{2}\}\)\)\n{3}''' +position = 'after' +line_prepend = '$indent' +payload = ''' +local table_nodes = {} +for i = 1, math.ceil(counter / 6) do + table.insert(table_nodes, {n=G.UIT.R, config={align = "cm"}, nodes=tag_matrix[i]}) +end''' + +[[patches]] +[patches.regex] +target = "functions/UI_definitions.lua" +pattern = '''(?[\t ]*)\{\n[\s\S]{10}\{n=G\.UIT\.R, config=\{align = "cm"\}, nodes=tag_matrix\[1\]},[\s\S]*tag_matrix\[4\]\},\n[\s\S]{8}\}''' +position = 'at' +line_prepend = '$indent' +payload = '''table_nodes''' + +# Tag:generate_UI() +[[patches]] +[patches.regex] +target = "tag.lua" +pattern = 'G.ASSET_ATLAS\["tags"\]' +position = 'at' +payload = 'G.ASSET_ATLAS[(not self.hide_ability) and G.P_TAGS[self.key].atlas or "tags"]' + +# Tag:get_uibox_table() +[[patches]] +[patches.pattern] +target = "tag.lua" +pattern = '''tag_sprite.ability_UIBox_table = generate_card_ui(G.P_TAGS[self.key], nil, loc_vars, (self.hide_ability) and 'Undiscovered' or 'Tag', nil, (self.hide_ability))''' +position = 'at' +match_indent = true +payload = '''tag_sprite.ability_UIBox_table = generate_card_ui(G.P_TAGS[self.key], nil, loc_vars, (self.hide_ability) and 'Undiscovered' or 'Tag', nil, (self.hide_ability), nil, nil, self)''' diff --git a/Steamodded/lovely/threads.toml b/Steamodded/lovely/threads.toml new file mode 100644 index 0000000..965673b --- /dev/null +++ b/Steamodded/lovely/threads.toml @@ -0,0 +1,30 @@ +# Necessary to kill threads which lets us restart the game. + +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +[[patches]] +[patches.pattern] +target = "engine/save_manager.lua" +pattern = "if request then" +position = "after" +payload = "if request.type == 'kill' then return end" +match_indent = true + +[[patches]] +[patches.pattern] +target = "engine/http_manager.lua" +pattern = "if request then" +position = "after" +payload = "if request.type == 'kill' then return end" +match_indent = true + +[[patches]] +[patches.pattern] +target = "engine/sound_manager.lua" +pattern = "if request then" +position = "after" +payload = "if request.type == 'kill' then return end" +match_indent = true \ No newline at end of file diff --git a/Steamodded/lovely/ui.toml b/Steamodded/lovely/ui.toml new file mode 100644 index 0000000..8c0c527 --- /dev/null +++ b/Steamodded/lovely/ui.toml @@ -0,0 +1,359 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +### Addition Tab + +## Jokers tab +# create_UIBox_your_collection_jokers() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local joker_options = {}''' +position = "before" +payload = ''' +local joker_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker)''' +match_indent = true + +# create_UIBox_your_collection_jokers() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '''(?[\t ]*)for i = 1, math\.ceil\(#G\.P_CENTER_POOLS\.Joker\/\(5\*#G\.your_collection\)\) do\n[\s\S]{4}table\.insert\(joker_options, localize\('k_page'\)..' '..tostring\(i\)..'\/'..tostring\(math\.ceil\(#G\.P_CENTER_POOLS\.Joker\/\(5\*#G\.your_collection\)\)\)\)''' +position = 'at' +payload = ''' +for i = 1, math.ceil(#joker_pool/(5*#G.your_collection)) do + table.insert(joker_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#joker_pool/(5*#G.your_collection))))''' +line_prepend = '$indent' + +# create_UIBox_your_collection_jokers() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local center = G.P_CENTER_POOLS["Joker"][i+(j-1)*5]''' +position = "at" +payload = ''' +local center = joker_pool[i+(j-1)*5] +if not center then break end''' +match_indent = true + +# create_UIBox_your_collection_jokers() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local t = create_UIBox_generic_options({ back_func = 'your_collection', contents = {''' +position = "at" +payload = '''local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = {''' +match_indent = true + +# G.FUNCS.your_collections_joker_page +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''for i = 1, 5 do''' +position = "before" +payload = ''' +local joker_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker) +''' +match_indent = true + +# G.FUNCS.your_collection_joker_page +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''local center = G.P_CENTER_POOLS["Joker"][i+(j-1)*5 + (5*#G.your_collection*(args.cycle_config.current_option - 1))]''' +position = "at" +payload = '''local center = joker_pool[i+(j-1)*5 + (5*#G.your_collection*(args.cycle_config.current_option - 1))]''' +match_indent = true + +## Decks tab +# create_UIBox_your_collection_decks() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''G.GAME.viewed_back = Back(G.P_CENTERS.b_red)''' +position = "at" +payload = ''' +local deck_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Back) +G.GAME.viewed_back = Back(G.ACTIVE_MOD_UI and deck_pool[1] or G.P_CENTERS.b_red)''' +match_indent = true + +# create_UIBox_your_collection_decks() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '''(?[\t ]*)for k, v in ipairs\(G\.P_CENTER_POOLS\.Back\) do\n[\s\S]{4}ordered_names\[#ordered_names\+1\] = v\.name\n[\s\S]{2}end''' +position = 'at' +payload = ''' +for k, v in ipairs(deck_pool) do + ordered_names[#ordered_names+1] = v.key +end''' +line_prepend = '$indent' + +## Note: This covers Decks, Boosters, and Tags +# create_UIBox_your_collection_decks() and create_UIBox_your_collection_boosters() and create_UIBox_your_collection_tags() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local t = create_UIBox_generic_options({ back_func = 'your_collection', contents = {''' +position = "at" +payload = '''local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = {''' +match_indent = true + +# G.FUNCS.your_collection_deck_page +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''G.GAME.viewed_back:change_to(G.P_CENTER_POOLS.Back[args.to_key])''' +position = "at" +payload = ''' +local deck_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Back) +G.GAME.viewed_back:change_to(deck_pool[args.to_key])''' +match_indent = true + +## Boosters Page +# create_UIBox_your_collection_boosters() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local booster_options = {}''' +position = "before" +payload = ''' +local booster_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Booster)''' +match_indent = true + +# create_UIBox_your_collection_boosters() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '''(?[\t ]*)for i = 1, math\.ceil\(#G\.P_CENTER_POOLS\.Booster\/8\) do\n[\s\S]{4}table\.insert\(booster_options, localize\('k_page'\)..' '..tostring\(i\)..'\/'..tostring\(math\.ceil\(#G\.P_CENTER_POOLS\.Booster\/8\)\)\)''' +position = 'at' +payload = ''' +for i = 1, math.ceil(#booster_pool/8) do + table.insert(booster_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#booster_pool/8)))''' +line_prepend = '$indent' + +# create_UIBox_your_collection_boosters() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''local center = G.P_CENTER_POOLS["Booster"][i+(j-1)*4]''' +position = 'at' +payload = ''' +local center = booster_pool[i+(j-1)*4] +if not center then break end''' +match_indent = true + +# G.FUNCS.your_collection_booster_page +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''G.FUNCS.your_collection_booster_page = function(args)''' +position = "after" +payload = ''' +local booster_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Booster)''' +match_indent = true + +# G.FUNCS.your_collection_booster_page +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''local center = G.P_CENTER_POOLS["Booster"][i+(j-1)*4 + (8*(args.cycle_config.current_option - 1))]''' +position = "at" +payload = '''local center = booster_pool[i+(j-1)*4 + (8*(args.cycle_config.current_option - 1))]''' +match_indent = true + +## Voucher Tabs +# create_UIBox_your_collection_vouchers() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local voucher_options = {}''' +position = "before" +payload = ''' +local voucher_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Voucher)''' +match_indent = true + +# create_UIBox_your_collection_vouchers() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '''(?[\t ]*)for i = 1, math\.ceil\(#G\.P_CENTER_POOLS\.Voucher\/\(4\*#G\.your_collection\)\) do\n[\s\S]{4}table\.insert\(voucher_options, localize\('k_page'\)..' '..tostring\(i\)..'\/'..tostring\(math\.ceil\(#G\.P_CENTER_POOLS\.Voucher\/\(4\*#G\.your_collection\)\)\)\)''' +position = 'at' +payload = ''' + for i = 1, math.ceil(#voucher_pool/(4*#G.your_collection)) do + table.insert(voucher_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#voucher_pool/(4*#G.your_collection))))''' +line_prepend = '$indent' + +# create_UIBox_your_collection_boosters() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''local center = G.P_CENTER_POOLS["Voucher"][i+(j-1)*4]''' +position = 'at' +payload = ''' +local center = voucher_pool[i+(j-1)*4] +if not center then break end''' +match_indent = true + +## Note: This covers Blinds and Vouchers +# create_UIBox_your_collection_vouchers() and create_UIBox_your_collection_blinds() +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = '''local t = create_UIBox_generic_options({ back_func = exit or 'your_collection', contents = {''' +position = "at" +payload = '''local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', contents = {''' +match_indent = true + +# G.FUNCS.your_collection_voucher_page +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''G.FUNCS.your_collection_voucher_page = function(args)''' +position = "after" +payload = ''' +local voucher_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Voucher)''' +match_indent = true + +# G.FUNCS.your_collection_voucher_page +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = '''local center = G.P_CENTER_POOLS["Voucher"][i+(j-1)*4 + (8*(args.cycle_config.current_option - 1))]''' +position = "at" +payload = '''local center = voucher_pool[i+(j-1)*4 + (8*(args.cycle_config.current_option - 1))]''' +match_indent = true + +# create_UIBox_your_collection() +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +pattern = '''(?[\t ]*)UIBox_button\(\{button = 'your_collection_blinds', label = \{localize\('b_blinds'\)\}, count = G\.DISCOVER_TALLIES\.blinds, minw = 5, minh = 2.0, id = 'your_collection_blinds', focus_args = \{snap_to = true\}\}\),''' +position = 'after' +payload = '''UIBox_button({button = 'your_collection_other_gameobjects', label = {localize('k_other')}, minw = 5, id = 'your_collection_other_gameobjects', focus_args = {snap_to = true}}),''' + +# Fix UIElement.config.chosen being overriden if choice=true is set +# UIElement:click() +[[patches]] +[patches.pattern] +target = "engine/ui.lua" +match_indent = true +position = "after" +pattern = "if self.config.choice then" +payload = " local chosen_temp = self.config.chosen" + +[[patches]] +[patches.pattern] +target = "engine/ui.lua" +match_indent = true +position = "at" +pattern = "self.config.chosen = true" +payload = "self.config.chosen = chosen_temp or true" + +# Escape from mod menu saves config +# Needs to be before all checks +[[patches]] +[patches.pattern] +target = 'engine/controller.lua' +pattern = "function Controller:key_press_update(key, dt)" +position = "after" +payload = ''' + if key == "escape" and G.ACTIVE_MOD_UI then + G.FUNCS.exit_mods() + end +''' +match_indent = true + +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +position = 'before' +line_prepend = '$indent' +pattern = ''' +(?[\t ]*)return \{n=G\.UIT\.ROOT, config = \{align = 'cm', colour = G\.C\.CLEAR\}, nodes=\{ +[\t ]*\{n=G\.UIT\.C,''' +payload = ''' +local cols +if #info_boxes <= 3 then + cols = 1 +elseif #info_boxes <= 10 then + cols = 2 +elseif #info_boxes <= 24 then + cols = 3 +else + cols = 4 +end +local nodes_per_col = math.ceil(#info_boxes/cols) +local info_cols = {} +for i = 0, cols-1 do + local col = {} + for j = 1, nodes_per_col do + local info_box = info_boxes[i*nodes_per_col+j] + if info_box then + table.insert(col, info_box) + else break end + end + table.insert(info_cols, {n=G.UIT.C, config = {align="cm"}, nodes = col}) +end +info_boxes = {{n=G.UIT.R, config = {align="cm", padding = 0.05, card_pos = card.T.x }, nodes = info_cols}} +''' + +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +match_indent = true +position = 'at' +pattern = "config = {offset = {x=-0.03,y=0}, align = 'cl', parent = e}" +payload = """config = (not e.config.ref_table or not e.config.ref_table[1].config.card_pos or e.config.ref_table[1].config.card_pos > G.ROOM.T.w*0.4) and + {offset = {x=-0.03,y=0}, align = 'cl', parent = e} or + {offset = {x=0.03,y=0}, align = 'cr', parent = e}""" + +[[patches]] +[patches.pattern] +target = 'tag.lua' +match_indent = true +position = 'at' +pattern = "_self.config.h_popup_config ={align = 'cl', offset = {x=-0.1,y=0},parent = _self}" +payload = """_self.config.h_popup_config = (_self.T.x > G.ROOM.T.w*0.4) and + {align = 'cl', offset = {x=-0.1,y=0},parent = _self} or + {align = 'cr', offset = {x=0.1,y=0},parent = _self}""" + +# desc_from_rows +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +position = 'at' +pattern = 'colour = empty and G\.C\.CLEAR or G\.C\.UI\.BACKGROUND_WHITE' +payload = 'colour = desc_nodes.background_colour or empty and G.C.CLEAR or G.C.UI.BACKGROUND_WHITE' + +# info_tip_from_rows +[[patches]] +[patches.regex] +target = 'functions/UI_definitions.lua' +position = 'at' +pattern = 'padding = 0\.05, colour = G\.C\.WHITE\}' +payload = 'padding = 0.05, colour = desc_nodes.background_colour or G.C.WHITE}' + +# localize +[[patches]] +[patches.regex] +target = 'functions/misc_functions.lua' +position = 'after' +pattern = '\(part\.control\.C and loc_colour\(part\.control\.C\)\)' +payload = ' or args.text_colour' + +[[patches]] +[patches.regex] +target = 'functions/misc_functions.lua' +position = 'at' +pattern = 'loc_colour\(part\.control\.C or nil, args\.default_col\)' +payload = 'not part.control.C and args.text_colour or loc_colour(part.control.C or nil, args.default_col)' + +[[patches]] +[patches.regex] +target = 'functions/misc_functions.lua' +position = 'after' +pattern = 'part\.control\.s and tonumber\(part\.control\.s\)' +payload = ' or args.scale ' \ No newline at end of file diff --git a/Steamodded/lovely/ui_elements.toml b/Steamodded/lovely/ui_elements.toml new file mode 100644 index 0000000..a665507 --- /dev/null +++ b/Steamodded/lovely/ui_elements.toml @@ -0,0 +1,121 @@ +[manifest] +version = "1.0.0" +dump_lua = true +priority = 0 + +## colour argument fix +# create_tabs() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''tab_buttons[#tab_buttons+1] = UIBox_button({id = 'tab_but_'..(v.label or ''), ref_table = v, button = 'change_tab', label = {v.label}, minh = 0.8*args.scale, minw = 2.5*args.scale, col = true, choice = true, scale = args.text_scale, chosen = v.chosen, func = v.func, focus_args = {type = 'none'}})''' +position = 'at' +payload = '''tab_buttons[#tab_buttons+1] = UIBox_button({id = 'tab_but_'..(v.label or ''), ref_table = v, button = 'change_tab', label = {v.label}, minh = 0.8*args.scale, minw = 2.5*args.scale, col = true, choice = true, scale = args.text_scale, chosen = v.chosen, func = v.func, colour = args.colour, focus_args = {type = 'none'}})''' +match_indent = true + +# UIElement:draw_self() +[[patches]] +[patches.pattern] +target = 'engine/ui.lua' +pattern = '''love.graphics.polygon("fill", get_chosen_triangle_from_rect(self.layered_parallax.x, self.layered_parallax.y, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE, self.config.chosen == 'vert'))''' +position = 'before' +payload = '''love.graphics.setColor(self.config.colour)''' +match_indent = true + + +## multiple text input fix +# create_text_input() +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''args.current_prompt_text = ''''' +position = 'after' +payload = '''args.id = args.id or "text_input"''' +match_indent = true +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''ui_letters[#ui_letters+1] = {n=G.UIT.T, config={ref_table = args, ref_value = 'current_prompt_text', scale = args.text_scale, colour = lighten(copy_table(args.colour), 0.4), id = 'prompt'}}''' +position = 'at' +payload = '''ui_letters[#ui_letters+1] = {n=G.UIT.T, config={ref_table = args, ref_value = 'current_prompt_text', scale = args.text_scale, colour = lighten(copy_table(args.colour), 0.4), id = args.id..'_prompt'}}''' +match_indent = true +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''ui_letters[i] = {n=G.UIT.T, config={ref_table = text.letters, ref_value = i, scale = args.text_scale, colour = G.C.UI.TEXT_LIGHT, id = 'letter_'..i}}''' +position = 'at' +payload = '''ui_letters[i] = {n=G.UIT.T, config={ref_table = text.letters, ref_value = i, scale = args.text_scale, colour = G.C.UI.TEXT_LIGHT, id = args.id..'_letter_'..i}}''' +match_indent = true +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''ui_letters[#ui_letters+1] = {n=G.UIT.B, config={r = 0.03,w=0.1, h=0.4, colour = position_text_colour, id = 'position', func = 'flash'}}''' +position = 'at' +payload = '''ui_letters[#ui_letters+1] = {n=G.UIT.B, config={r = 0.03,w=0.1, h=0.4, colour = position_text_colour, id = args.id..'_position', func = 'flash'}}''' +match_indent = true +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''{n=G.UIT.C, config={align = "cm", draw_layer = 1, colour = G.C.CLEAR}, nodes = {''' +position = 'at' +payload = '''{n=G.UIT.C, config={align = "cm", colour = G.C.CLEAR}, nodes = {''' +match_indent = true +[[patches]] +[patches.pattern] +target = 'functions/UI_definitions.lua' +pattern = '''{n=G.UIT.C, config={id = 'text_input', align = "cm", padding = 0.05, r = 0.1, draw_layer = 2, hover = true, colour = args.colour,minw = args.w, min_h = args.h, button = 'select_text_input', shadow = true}, nodes={''' +position = 'at' +payload = '''{n=G.UIT.C, config={id = args.id, align = "cm", padding = 0.05, r = 0.1, hover = true, colour = args.colour,minw = args.w, min_h = args.h, button = 'select_text_input', shadow = true}, nodes={''' +match_indent = true + +# G.FUNCS.select_text_input +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = '''G.CONTROLLER.text_input_hook = e.children[1].children[1]''' +position = 'after' +payload = '''G.CONTROLLER.text_input_id = e.config.id''' +match_indent = true + +# G.FUNCS.paste_seed +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = '''G.CONTROLLER.text_input_hook = e.UIBox:get_UIE_by_ID('text_input').children[1].children[1]''' +position = 'after' +payload = """G.CONTROLLER.text_input_id = 'text_input'""" +match_indent = true + +# G.FUNCS.flash +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = '''if G.CONTROLLER.text_input_hook then''' +position = 'at' +payload = '''if G.CONTROLLER.text_input_hook and G.CONTROLLER.text_input_id == e.config.id:sub(1,string.len(G.CONTROLLER.text_input_id)) then''' +match_indent = true + +# TRANSPOSE_TEXT_INPUT() +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = '''if hook.children[i].config.id == 'position' then''' +position = 'at' +payload = '''if hook.children[i].config.id == G.CONTROLLER.text_input_id..'_position' then''' +match_indent = true +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = '''local real_letter = hook.children[position_child+dir].config.id:sub(1, 7) == 'letter_' and hook.children[position_child+dir].config.text ~= ''''' +position = 'at' +payload = '''local real_letter = hook.children[position_child+dir].config.id:sub(1, 8+string.len(G.CONTROLLER.text_input_id)) == G.CONTROLLER.text_input_id..'_letter_' and hook.children[position_child+dir].config.text ~= ''''' +match_indent = true + +# GET_TEXT_FROM_INPUT() +[[patches]] +[patches.pattern] +target = 'functions/button_callbacks.lua' +pattern = '''if hook.children[i].config and hook.children[i].config.id:sub(1, 7) == 'letter_' and hook.children[i].config.text ~= '' then''' +position = 'at' +payload = '''if hook.children[i].config and hook.children[i].config.id:sub(1, 8+string.len(G.CONTROLLER.text_input_id)) == G.CONTROLLER.text_input_id..'_letter_' and hook.children[i].config.text ~= '' then''' +match_indent = true \ No newline at end of file diff --git a/Steamodded/manifest.json b/Steamodded/manifest.json new file mode 100644 index 0000000..0b06fff --- /dev/null +++ b/Steamodded/manifest.json @@ -0,0 +1,10 @@ +{ + "name": "Steamodded", + "version_number": "0.9.8", + "website_url": "https://github.com/Steamopollys/Steamodded", + "description": "A Balatro ModLoader", + "dependencies": [ + "Thunderstore-lovely-0.3.1", + "metherul-nativefs-1.0.0" + ] +} diff --git a/Steamodded/src/compat_0_9_8.lua b/Steamodded/src/compat_0_9_8.lua new file mode 100644 index 0000000..7875d1c --- /dev/null +++ b/Steamodded/src/compat_0_9_8.lua @@ -0,0 +1,536 @@ +SMODS.compat_0_9_8 = {} +SMODS.compat_0_9_8.load_done = false + +function SMODS.compat_0_9_8.load() + if SMODS.compat_0_9_8.load_done then + return + end + + function SMODS.compat_0_9_8.delay_register(cls, self) + if self.delay_register then + self.delay_register = nil + return + end + cls.super.register(self) + end + + function SMODS.compat_0_9_8.joker_loc_vars(self, info_queue, card) + local vars, main_end + if self.loc_def and type(self.loc_def) == 'function' then + vars, main_end = self.loc_def(card, info_queue) + end + if self.tooltip and type(self.tooltip) == 'function' then + self.tooltip(self, info_queue) + end + if vars then + return { + vars = vars, + main_end = main_end + } + else + return {} + end + end + -- Applies to Tarot, Planet, Spectral and Voucher + function SMODS.compat_0_9_8.tarot_loc_vars(self, info_queue, card) + local vars, main_end + if self.loc_def and type(self.loc_def) == 'function' then + vars, main_end = self.loc_def(self, info_queue) + end + if self.tooltip and type(self.tooltip) == 'function' then + self.tooltip(self, info_queue) + end + if vars then + return { + vars = vars, + main_end = main_end + } + else + return {} + end + end + + SMODS.compat_0_9_8.init_queue = {} + SMODS.INIT = setmetatable({}, { + __newindex = function(t, k, v) + SMODS.compat_0_9_8.init_queue[k] = v + rawset(t, k, v) + end + }) + function SMODS.findModByID(id) + return SMODS.Mods[id] + end + function SMODS.end_calculate_context(c) + return c.joker_main + end + function SMODS.LOAD_LOC() + init_localization() + end + + SMODS.SOUND_SOURCES = SMODS.Sounds + function register_sound(name, path, filename) + SMODS.Sound { + key = name, + path = filename, + } + end + function modded_play_sound(sound_code, stop_previous_instance, volume, pitch) + return SMODS.Sound.play(nil, pitch, volume, stop_previous_instance, sound_code) + end + + SMODS.Card = { + SUITS = SMODS.Suits, + RANKS = SMODS.Ranks, + SUIT_LIST = SMODS.Suit.obj_buffer, + RANK_LIST = SMODS.Rank.obj_buffer, + } + + SMODS.compat_0_9_8.Deck_new = SMODS.Back:extend { + register = function(self) + SMODS.compat_0_9_8.delay_register(SMODS.compat_0_9_8.Deck_new, self) + end, + __index = function(t, k) + if k == 'slug' then return t.key + elseif k == 'spritePos' then return t.pos + end + return getmetatable(t)[k] + end, + __newindex = function(t, k, v) + if k == 'slug' then t.key = v; return + elseif k == 'spritePos' then t.pos = v; return + end + rawset(t, k, v) + end, + } + SMODS.Deck = {} + function SMODS.Deck.new(self, name, slug, config, spritePos, loc_txt, unlocked, discovered) + return SMODS.compat_0_9_8.Deck_new { + name = name, + key = slug, + config = config, + pos = spritePos, + loc_txt = loc_txt, + unlocked = unlocked, + discovered = discovered, + atlas = config and config.atlas, + delay_register = true + } + end + SMODS.Decks = SMODS.Centers + + SMODS.Sprites = {} + SMODS.compat_0_9_8.Sprite_new = SMODS.Atlas:extend { + register = function(self) + if self.delay_register then + self.delay_register = nil + return + end + if self.registered then + sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set) + return + end + SMODS.compat_0_9_8.Sprite_new.super.register(self) + table.insert(SMODS.Sprites, self) + end, + __index = function(t, k) + if k == 'name' then return t.key + end + return getmetatable(t)[k] + end, + __newindex = function(t, k, v) + if k == 'name' then t.key = v; return + end + rawset(t, k, v) + end, + } + SMODS.Sprite = {} + function SMODS.Sprite.new(self, name, top_lpath, path, px, py, type, frames) + local atlas_table + if type == 'animation_atli' then + atlas_table = 'ANIMATION_ATLAS' + else + atlas_table = 'ASSET_ATLAS' + end + return SMODS.compat_0_9_8.Sprite_new { + key = name, + path = path, + atlas_table = atlas_table, + px = px, + py = py, + frames = frames, + delay_register = true + } + end + + SMODS.compat_0_9_8.Joker_new = SMODS.Joker:extend { + loc_vars = SMODS.compat_0_9_8.joker_loc_vars, + register = function(self) + SMODS.compat_0_9_8.delay_register(SMODS.compat_0_9_8.Joker_new, self) + end, + __index = function(t, k) + if k == 'slug' then return t.key + elseif k == 'atlas' and SMODS.Atlases[t.key] then return t.key + elseif k == 'spritePos' then return t.pos + end + return getmetatable(t)[k] + end, + __newindex = function(t, k, v) + if k == 'slug' then t.key = v; return + elseif k == 'spritePos' then t.pos = v; return + end + if k == 'calculate' or k == 'set_ability' or k == 'set_badges' or k == 'update' then + local v_ref = v + v = function(self, ...) + return v_ref(...) + end + end + rawset(t, k, v) + end, + } + function SMODS.Joker.new(self, name, slug, config, spritePos, loc_txt, rarity, cost, unlocked, discovered, + blueprint_compat, eternal_compat, effect, atlas, soul_pos) + local x = SMODS.compat_0_9_8.Joker_new { + name = name, + key = slug, + config = config, + pos = spritePos, + loc_txt = loc_txt, + rarity = rarity, + cost = cost, + unlocked = unlocked, + discovered = discovered, + blueprint_compat = blueprint_compat, + eternal_compat = eternal_compat, + effect = effect, + atlas = atlas, + soul_pos = soul_pos, + delay_register = true + } + return x + end + SMODS.Jokers = SMODS.Centers + + function SMODS.compat_0_9_8.extend_consumable_class(SMODS_cls) + local cls + cls = SMODS_cls:extend { + loc_vars = SMODS.compat_0_9_8.tarot_loc_vars, + register = function(self) + SMODS.compat_0_9_8.delay_register(cls, self) + end, + __index = function(t, k) + if k == 'slug' then + return t.key + elseif k == 'atlas' and SMODS.Atlases[t.key] then + return t.key + end + return getmetatable(t)[k] + end, + __newindex = function(t, k, v) + if k == 'slug' then + t.key = v; return + elseif k == 'spritePos' then + t.pos = v; return + end + if k == 'set_badges' or k == 'use' or k == 'can_use' or k == 'update' then + local v_ref = v + v = function(self, ...) + return v_ref(...) + end + end + rawset(t, k, v) + end + } + return cls + end + + SMODS.compat_0_9_8.Tarot_new = SMODS.compat_0_9_8.extend_consumable_class(SMODS.Tarot) + function SMODS.Tarot.new(self, name, slug, config, pos, loc_txt, cost, cost_mult, effect, consumeable, discovered, + atlas) + return SMODS.compat_0_9_8.Tarot_new { + name = name, + key = slug, + config = config, + pos = pos, + loc_txt = loc_txt, + cost = cost, + cost_mult = cost_mult, + effect = effect, + consumeable = consumeable, + discovered = discovered, + atlas = atlas, + delay_register = true + } + end + SMODS.Tarots = SMODS.Centers + + SMODS.compat_0_9_8.Planet_new = SMODS.compat_0_9_8.extend_consumable_class(SMODS.Planet) + function SMODS.Planet.new(self, name, slug, config, pos, loc_txt, cost, cost_mult, effect, freq, consumeable, + discovered, atlas) + return SMODS.compat_0_9_8.Planet_new { + name = name, + key = slug, + config = config, + pos = pos, + loc_txt = loc_txt, + cost = cost, + cost_mult = cost_mult, + effect = effect, + freq = freq, + consumeable = consumeable, + discovered = discovered, + atlas = atlas, + delay_register = true + } + end + SMODS.Planets = SMODS.Centers + + SMODS.compat_0_9_8.Spectral_new = SMODS.compat_0_9_8.extend_consumable_class(SMODS.Spectral) + function SMODS.Spectral.new(self, name, slug, config, pos, loc_txt, cost, consumeable, discovered, atlas) + return SMODS.compat_0_9_8.Spectral_new { + name = name, + key = slug, + config = config, + pos = pos, + loc_txt = loc_txt, + cost = cost, + consumeable = consumeable, + discovered = discovered, + atlas = atlas, + delay_register = true + } + end + SMODS.Spectrals = SMODS.Centers + + SMODS.compat_0_9_8.Seal_new = SMODS.Seal:extend { + class_prefix = false, + register = function(self) + if self.delay_register then + self.delay_register = nil + return + end + if self.registered then + sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set) + return + end + if self:check_dependencies() and not self.obj_table[self.label] then + self.obj_table[self.label] = self + self.obj_buffer[#self.obj_buffer + 1] = self.label + self.registered = true + end + end, + __index = function(t, k) + if k == 'name' then return t.key + end + return getmetatable(t)[k] + end, + __newindex = function(t, k, v) + if k == 'name' then t.key = v; return + end + rawset(t, k, v) + end, + } + function SMODS.Seal.new(self, name, label, full_name, pos, loc_txt, atlas, discovered, color) + return SMODS.compat_0_9_8.Seal_new { + key = name, + label = label, + full_name = full_name, + pos = pos, + loc_txt = { + description = loc_txt, + label = full_name + }, + atlas = atlas, + discovered = discovered, + colour = color, + delay_register = true + } + end + + SMODS.compat_0_9_8.Voucher_new = SMODS.Voucher:extend { + loc_vars = SMODS.compat_0_9_8.tarot_loc_vars, + register = function(self) + SMODS.compat_0_9_8.delay_register(SMODS.compat_0_9_8.Voucher_new, self) + end, + __index = function(t, k) + if k == 'slug' then return t.key + elseif k == 'atlas' and SMODS.Atlases[t.key] then return t.key + end + return getmetatable(t)[k] + end, + __newindex = function(t, k, v) + if k == 'slug' then t.key = v; return + end + if k == 'update' then + local v_ref = v + v = function(self, ...) + return v_ref(...) + end + elseif k == 'redeem' then + local v_ref = v + v = function(center, card) + local center_table = { + name = center and center.name or card and card.ability.name, + extra = center and center.config.extra or card and card.ability.extra + } + return v_ref(center_table) + end + end + rawset(t, k, v) + end + } + function SMODS.Voucher.new(self, name, slug, config, pos, loc_txt, cost, unlocked, discovered, available, requires, + atlas) + return SMODS.compat_0_9_8.Voucher_new { + name = name, + key = slug, + config = config, + pos = pos, + loc_txt = loc_txt, + cost = cost, + unlocked = unlocked, + discovered = discovered, + available = available, + requires = requires, + atlas = atlas, + delay_register = true + } + end + SMODS.Vouchers = SMODS.Centers + + SMODS.compat_0_9_8.Blind_new = SMODS.Blind:extend { + register = function(self) + SMODS.compat_0_9_8.delay_register(SMODS.compat_0_9_8.Blind_new, self) + end, + __index = function(t, k) + if k == 'slug' then return t.key + end + return getmetatable(t)[k] + end, + __newindex = function(t, k, v) + if k == 'slug' then t.key = v; return + end + if k == 'set_blind' + or k == 'disable' + or k == 'defeat' + or k == 'debuff_card' + or k == 'stay_flipped' + or k == 'drawn_to_hand' + or k == 'debuff_hand' + or k == 'modify_hand' + or k == 'press_play' + or k == 'get_loc_debuff_text' then + local v_ref = v + v = function(self, ...) + return v_ref(G.GAME.blind, ...) + end + end + rawset(t, k, v) + end + } + function SMODS.Blind.new(self, name, slug, loc_txt, dollars, mult, vars, debuff, pos, boss, boss_colour, defeated, + atlas) + return SMODS.compat_0_9_8.Blind_new { + name = name, + key = slug, + loc_txt = loc_txt, + dollars = dollars, + mult = mult, + loc_vars = { + vars = vars, + }, + debuff = debuff, + pos = pos, + boss = boss, + boss_colour = boss_colour, + defeated = defeated, + atlas = atlas, + delay_register = true + } + end + + SMODS.compat_0_9_8.loc_proxies = setmetatable({}, {__mode = 'k'}) + -- Indexing a table `t` that has this metatable instead indexes `t.capture_table`. + -- Handles nested indices by instead indexing `t.capture_table` with the + -- concatenation of all indices, separated by dots. + SMODS.compat_0_9_8.loc_proxy_mt = { + __index = function(t, k) + if rawget(t, 'stop_capture') then + return t.orig_t[k] + end + local new_idx_str = t.idx_str .. "." .. k + -- first check capture_table + if t.capture_table[new_idx_str] ~= nil then + return t.capture_table[new_idx_str] + end + -- then fall back to orig_t + local orig_v = t.orig_t[k] + if type(orig_v) ~= 'table' then + -- reached a non-table value, stop proxying + return orig_v + end + local ret = setmetatable({ + -- concatenation of all indexes, starting from G.localization + -- separated by dots and preceded by a dot + idx_str = new_idx_str, + -- table we would be indexing + orig_t = orig_v, + capture_table = t.capture_table, + }, SMODS.compat_0_9_8.loc_proxy_mt) + SMODS.compat_0_9_8.loc_proxies[ret] = true + return ret + end, + __newindex = function(t, k, v) + if rawget(t, 'stop_capture') then + t.orig_t[k] = v; return + end + local new_idx_str = t.idx_str .. "." .. k + t.capture_table[new_idx_str] = v + end + } + -- Drop-in replacement for G.localization. Captures changes in `capture_table` + function SMODS.compat_0_9_8.loc_proxy(capture_table) + local ret = setmetatable({ + idx_str = '', + orig_t = G.localization, + capture_table = capture_table, + }, SMODS.compat_0_9_8.loc_proxy_mt) + SMODS.compat_0_9_8.loc_proxies[ret] = true + return ret + end + function SMODS.compat_0_9_8.stop_loc_proxies() + collectgarbage() + for proxy, _ in pairs(SMODS.compat_0_9_8.loc_proxies) do + rawset(proxy, 'stop_capture', true) + SMODS.compat_0_9_8.loc_proxies[proxy] = nil + end + end + + SMODS.compat_0_9_8.load_done = true +end + +function SMODS.compat_0_9_8.with_compat(func) + SMODS.compat_0_9_8.load() + local localization_ref = G.localization + init_localization_ref = init_localization + local captured_loc = {} + G.localization = SMODS.compat_0_9_8.loc_proxy(captured_loc) + function init_localization() + G.localization = localization_ref + init_localization_ref() + G.localization = SMODS.compat_0_9_8.loc_proxy(captured_loc) + end + func() + G.localization = localization_ref + init_localization = init_localization_ref + SMODS.compat_0_9_8.stop_loc_proxies() + function SMODS.current_mod.process_loc_text() + for idx_str, v in pairs(captured_loc) do + local t = G + local k = 'localization' + for cur_k in idx_str:gmatch("[^%.]+") do + t, k = t[k], cur_k + end + t[k] = v + end + end +end diff --git a/Steamodded/src/core.lua b/Steamodded/src/core.lua new file mode 100644 index 0000000..4ebd125 --- /dev/null +++ b/Steamodded/src/core.lua @@ -0,0 +1,81 @@ +--- STEAMODDED CORE +--- MODULE CORE + +SMODS = {} +MODDED_VERSION = require'SMODS.version' +SMODS.id = 'Steamodded' +SMODS.version = MODDED_VERSION:gsub('%-STEAMODDED', '') +SMODS.can_load = true +SMODS.meta_mod = true + +-- Include lovely and nativefs modules +local nativefs = require "nativefs" +local lovely = require "lovely" +local json = require "json" + +local lovely_mod_dir = lovely.mod_dir:gsub("/$", "") +NFS = nativefs +-- make lovely_mod_dir an absolute path. +-- respects symlink/.. combos +NFS.setWorkingDirectory(lovely_mod_dir) +lovely_mod_dir = NFS.getWorkingDirectory() +-- make sure NFS behaves the same as love.filesystem +NFS.setWorkingDirectory(love.filesystem.getSaveDirectory()) + +JSON = json + +local function set_mods_dir() + local love_dirs = { + love.filesystem.getSaveDirectory(), + love.filesystem.getSourceBaseDirectory() + } + for _, love_dir in ipairs(love_dirs) do + if lovely_mod_dir:sub(1, #love_dir) == love_dir then + -- relative path from love_dir + SMODS.MODS_DIR = lovely_mod_dir:sub(#love_dir+2) + if nfs_success then + -- make sure NFS behaves the same as love.filesystem. + -- not perfect: NFS won't read from both getSaveDirectory() + -- and getSourceBaseDirectory() + NFS.setWorkingDirectory(love_dir) + end + return + end + end + SMODS.MODS_DIR = lovely_mod_dir +end +set_mods_dir() + +local function find_self(directory, target_filename, target_line, depth) + depth = depth or 1 + if depth > 3 then return end + for _, filename in ipairs(NFS.getDirectoryItems(directory)) do + local file_path = directory .. "/" .. filename + local file_type = NFS.getInfo(file_path).type + if file_type == 'directory' or file_type == 'symlink' then + local f = find_self(file_path, target_filename, target_line, depth+1) + if f then return f end + elseif filename == target_filename then + local first_line = NFS.read(file_path):match('^(.-)\n') + if first_line == target_line then + -- use parent directory + return directory:match('^(.+/)') + end + end + end +end + +SMODS.path = find_self(SMODS.MODS_DIR, 'core.lua', '--- STEAMODDED CORE') + +for _, path in ipairs { + "src/ui.lua", + "src/index.lua", + "src/utils.lua", + "src/overrides.lua", + "src/game_object.lua", + "src/logging.lua", + "src/compat_0_9_8.lua", + "src/loader.lua", +} do + assert(load(NFS.read(SMODS.path..path), ('=[SMODS _ "%s"]'):format(path)))() +end diff --git a/Steamodded/src/crash_handler.lua b/Steamodded/src/crash_handler.lua new file mode 100644 index 0000000..f43825e --- /dev/null +++ b/Steamodded/src/crash_handler.lua @@ -0,0 +1,855 @@ +--- STEAMODDED CORE +--- MODULE STACKTRACE +-- NOTE: This is a modifed version of https://github.com/ignacio/StackTracePlus/blob/master/src/StackTracePlus.lua +-- Licensed under the MIT License. See https://github.com/ignacio/StackTracePlus/blob/master/LICENSE +-- The MIT License +-- Copyright (c) 2010 Ignacio Burgueño +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +-- tables +function loadStackTracePlus() + local _G = _G + local string, io, debug, coroutine = string, io, debug, coroutine + + -- functions + local tostring, print, require = tostring, print, require + local next, assert = next, assert + local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs + local error = error + + assert(debug, "debug table must be available at this point") + + local io_open = io.open + local string_gmatch = string.gmatch + local string_sub = string.sub + local table_concat = table.concat + + local _M = { + max_tb_output_len = 70 -- controls the maximum length of the 'stringified' table before cutting with ' (more...)' + } + + -- this tables should be weak so the elements in them won't become uncollectable + local m_known_tables = { + [_G] = "_G (global table)" + } + local function add_known_module(name, desc) + local ok, mod = pcall(require, name) + if ok then + m_known_tables[mod] = desc + end + end + + add_known_module("string", "string module") + add_known_module("io", "io module") + add_known_module("os", "os module") + add_known_module("table", "table module") + add_known_module("math", "math module") + add_known_module("package", "package module") + add_known_module("debug", "debug module") + add_known_module("coroutine", "coroutine module") + + -- lua5.2 + add_known_module("bit32", "bit32 module") + -- luajit + add_known_module("bit", "bit module") + add_known_module("jit", "jit module") + -- lua5.3 + if _VERSION >= "Lua 5.3" then + add_known_module("utf8", "utf8 module") + end + + local m_user_known_tables = {} + + local m_known_functions = {} + for _, name in ipairs { -- Lua 5.2, 5.1 + "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "load", "loadfile", "next", "pairs", + "pcall", "print", "rawequal", "rawget", "rawlen", "rawset", "require", "select", "setmetatable", "tonumber", + "tostring", "type", "xpcall", -- Lua 5.1 + "gcinfo", "getfenv", "loadstring", "module", "newproxy", "setfenv", "unpack" -- TODO: add table.* etc functions + } do + if _G[name] then + m_known_functions[_G[name]] = name + end + end + + local m_user_known_functions = {} + + local function safe_tostring(value) + local ok, err = pcall(tostring, value) + if ok then + return err + else + return (": '%s'"):format(err) + end + end + + -- Private: + -- Parses a line, looking for possible function definitions (in a very naïve way) + -- Returns '(anonymous)' if no function name was found in the line + local function ParseLine(line) + assert(type(line) == "string") + -- print(line) + local match = line:match("^%s*function%s+(%w+)") + if match then + -- print("+++++++++++++function", match) + return match + end + match = line:match("^%s*local%s+function%s+(%w+)") + if match then + -- print("++++++++++++local", match) + return match + end + match = line:match("^%s*local%s+(%w+)%s+=%s+function") + if match then + -- print("++++++++++++local func", match) + return match + end + match = line:match("%s*function%s*%(") -- this is an anonymous function + if match then + -- print("+++++++++++++function2", match) + return "(anonymous)" + end + return "(anonymous)" + end + + -- Private: + -- Tries to guess a function's name when the debug info structure does not have it. + -- It parses either the file or the string where the function is defined. + -- Returns '?' if the line where the function is defined is not found + local function GuessFunctionName(info) + -- print("guessing function name") + if type(info.source) == "string" and info.source:sub(1, 1) == "@" then + local file, err = io_open(info.source:sub(2), "r") + if not file then + print("file not found: " .. tostring(err)) -- whoops! + return "?" + end + local line + for _ = 1, info.linedefined do + line = file:read("*l") + end + if not line then + print("line not found") -- whoops! + return "?" + end + return ParseLine(line) + elseif type(info.source) == "string" and info.source:sub(1, 6) == "=[love" then + return "(LÖVE Function)" + else + local line + local lineNumber = 0 + for l in string_gmatch(info.source, "([^\n]+)\n-") do + lineNumber = lineNumber + 1 + if lineNumber == info.linedefined then + line = l + break + end + end + if not line then + print("line not found") -- whoops! + return "?" + end + return ParseLine(line) + end + end + + --- + -- Dumper instances are used to analyze stacks and collect its information. + -- + local Dumper = {} + + Dumper.new = function(thread) + local t = { + lines = {} + } + for k, v in pairs(Dumper) do + t[k] = v + end + + t.dumping_same_thread = (thread == coroutine.running()) + + -- if a thread was supplied, bind it to debug.info and debug.get + -- we also need to skip this additional level we are introducing in the callstack (only if we are running + -- in the same thread we're inspecting) + if type(thread) == "thread" then + t.getinfo = function(level, what) + if t.dumping_same_thread and type(level) == "number" then + level = level + 1 + end + return debug.getinfo(thread, level, what) + end + t.getlocal = function(level, loc) + if t.dumping_same_thread then + level = level + 1 + end + return debug.getlocal(thread, level, loc) + end + else + t.getinfo = debug.getinfo + t.getlocal = debug.getlocal + end + + return t + end + + -- helpers for collecting strings to be used when assembling the final trace + function Dumper:add(text) + self.lines[#self.lines + 1] = text + end + function Dumper:add_f(fmt, ...) + self:add(fmt:format(...)) + end + function Dumper:concat_lines() + return table_concat(self.lines) + end + + --- + -- Private: + -- Iterates over the local variables of a given function. + -- + -- @param level The stack level where the function is. + -- + function Dumper:DumpLocals(level) + local prefix = "\t " + local i = 1 + + if self.dumping_same_thread then + level = level + 1 + end + + local name, value = self.getlocal(level, i) + if not name then + return + end + self:add("\tLocal variables:\r\n") + while name do + if type(value) == "number" then + self:add_f("%s%s = number: %g\r\n", prefix, name, value) + elseif type(value) == "boolean" then + self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value)) + elseif type(value) == "string" then + self:add_f("%s%s = string: %q\r\n", prefix, name, value) + elseif type(value) == "userdata" then + self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value)) + elseif type(value) == "nil" then + self:add_f("%s%s = nil\r\n", prefix, name) + elseif type(value) == "table" then + if m_known_tables[value] then + self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value]) + elseif m_user_known_tables[value] then + self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value]) + else + local txt = "{" + for k, v in pairs(value) do + txt = txt .. safe_tostring(k) .. ":" .. safe_tostring(v) + if #txt > _M.max_tb_output_len then + txt = txt .. " (more...)" + break + end + if next(value, k) then + txt = txt .. ", " + end + end + self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt .. "}") + end + elseif type(value) == "function" then + local info = self.getinfo(value, "nS") + local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value] + if info.what == "C" then + self:add_f("%s%s = C %s\r\n", prefix, name, + (fun_name and ("function: " .. fun_name) or tostring(value))) + else + local source = info.short_src + if source:sub(2, 7) == "string" then + source = source:sub(9) -- uno más, por el espacio que viene (string "Baragent.Main", por ejemplo) + end + -- for k,v in pairs(info) do print(k,v) end + fun_name = fun_name or GuessFunctionName(info) + self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, + info.linedefined, source) + end + elseif type(value) == "thread" then + self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value)) + end + i = i + 1 + name, value = self.getlocal(level, i) + end + end + + --- + -- Public: + -- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc. + -- This function is suitable to be used as an error handler with pcall or xpcall + -- + -- @param thread An optional thread whose stack is to be inspected (defaul is the current thread) + -- @param message An optional error string or object. + -- @param level An optional number telling at which level to start the traceback (default is 1) + -- + -- Returns a string with the stack trace and a string with the original error. + -- + function _M.stacktrace(thread, message, level) + if type(thread) ~= "thread" then + -- shift parameters left + thread, message, level = nil, thread, message + end + + thread = thread or coroutine.running() + + level = level or 1 + + local dumper = Dumper.new(thread) + + local original_error + + if type(message) == "table" then + dumper:add("an error object {\r\n") + local first = true + for k, v in pairs(message) do + if first then + dumper:add(" ") + first = false + else + dumper:add(",\r\n ") + end + dumper:add(safe_tostring(k)) + dumper:add(": ") + dumper:add(safe_tostring(v)) + end + dumper:add("\r\n}") + original_error = dumper:concat_lines() + elseif type(message) == "string" then + dumper:add(message) + original_error = message + end + + dumper:add("\r\n") + dumper:add [[ +Stack Traceback +=============== +]] + -- print(error_message) + + local level_to_show = level + if dumper.dumping_same_thread then + level = level + 1 + end + + local info = dumper.getinfo(level, "nSlf") + while info do + if info.what == "main" then + if string_sub(info.source, 1, 1) == "@" then + dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, + string_sub(info.source, 2), info.currentline) + elseif info.source and info.source:sub(1, 1) == "=" then + local str = info.source:sub(3, -2) + local props = {} + -- Split by space + for v in string.gmatch(str, "[^%s]+") do + table.insert(props, v) + end + local source = table.remove(props, 1) + if source == "love" then + dumper:add_f("(%d) main chunk of LÖVE file '%s' at line %d\r\n", level_to_show, + table.concat(props, " "):sub(2, -2), info.currentline) + elseif source == "SMODS" then + local modID = table.remove(props, 1) + local fileName = table.concat(props, " ") + if modID == '_' then + dumper:add_f("(%d) main chunk of Steamodded file '%s' at line %d\r\n", level_to_show, + fileName:sub(2, -2), info.currentline) + else + dumper:add_f("(%d) main chunk of file '%s' at line %d (from mod with id %s)\r\n", + level_to_show, fileName:sub(2, -2), info.currentline, modID) + end + elseif source == "lovely" then + local module = table.remove(props, 1) + local fileName = table.concat(props, " ") + dumper:add_f("(%d) main chunk of file '%s' at line %d (from lovely module %s)\r\n", + level_to_show, fileName:sub(2, -2), info.currentline, module) + else + dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.source, + info.currentline) + end + else + dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.source, info.currentline) + end + elseif info.what == "C" then + -- print(info.namewhat, info.name) + -- for k,v in pairs(info) do print(k,v, type(v)) end + local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or + tostring(info.func) + dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name) + -- dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value))) + elseif info.what == "tail" then + -- print("tail") + -- for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name) + dumper:add_f("(%d) tail call\r\n", level_to_show) + dumper:DumpLocals(level) + elseif info.what == "Lua" then + local source = info.short_src + local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name + if source:sub(2, 7) == "string" then + source = source:sub(9) + end + local was_guessed = false + if not function_name or function_name == "?" then + -- for k,v in pairs(info) do print(k,v, type(v)) end + function_name = GuessFunctionName(info) + was_guessed = true + end + -- test if we have a file name + local function_type = (info.namewhat == "") and "function" or info.namewhat + if info.source and info.source:sub(1, 1) == "@" then + dumper:add_f("(%d) Lua %s '%s' at file '%s:%d'%s\r\n", level_to_show, function_type, function_name, + info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") + elseif info.source and info.source:sub(1, 1) == '#' then + dumper:add_f("(%d) Lua %s '%s' at template '%s:%d'%s\r\n", level_to_show, function_type, + function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") + elseif info.source and info.source:sub(1, 1) == "=" then + local str = info.source:sub(3, -2) + local props = {} + -- Split by space + for v in string.gmatch(str, "[^%s]+") do + table.insert(props, v) + end + local source = table.remove(props, 1) + if source == "love" then + dumper:add_f("(%d) LÖVE %s at file '%s:%d'%s\r\n", level_to_show, function_type, + table.concat(props, " "):sub(2, -2), info.currentline, was_guessed and " (best guess)" or "") + elseif source == "SMODS" then + local modID = table.remove(props, 1) + local fileName = table.concat(props, " ") + if modID == '_' then + dumper:add_f("(%d) Lua %s '%s' at Steamodded file '%s:%d' %s\r\n", level_to_show, + function_type, function_name, fileName:sub(2, -2), info.currentline, + was_guessed and " (best guess)" or "") + else + dumper:add_f("(%d) Lua %s '%s' at file '%s:%d' (from mod with id %s)%s\r\n", level_to_show, + function_type, function_name, fileName:sub(2, -2), info.currentline, modID, + was_guessed and " (best guess)" or "") + end + elseif source == "lovely" then + local module = table.remove(props, 1) + local fileName = table.concat(props, " ") + dumper:add_f("(%d) Lua %s '%s' at file '%s:%d' (from lovely module %s)%s\r\n", level_to_show, + function_type, function_name, fileName:sub(2, -2), info.currentline, module, + was_guessed and " (best guess)" or "") + else + dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type, + function_name, info.currentline, source) + end + else + dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type, + function_name, info.currentline, source) + end + dumper:DumpLocals(level) + else + dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what) + end + + level = level + 1 + level_to_show = level_to_show + 1 + info = dumper.getinfo(level, "nSlf") + end + + return dumper:concat_lines(), original_error + end + + -- + -- Adds a table to the list of known tables + function _M.add_known_table(tab, description) + if m_known_tables[tab] then + error("Cannot override an already known table") + end + m_user_known_tables[tab] = description + end + + -- + -- Adds a function to the list of known functions + function _M.add_known_function(fun, description) + if m_known_functions[fun] then + error("Cannot override an already known function") + end + m_user_known_functions[fun] = description + end + + return _M +end + +-- Note: The below code is not from the original StackTracePlus.lua +local stackTraceAlreadyInjected = false + +function getDebugInfoForCrash() + local version = VERSION + if not version or type(version) ~= "string" then + local versionFile = love.filesystem.read("version.jkr") + if versionFile then + version = versionFile:match("[^\n]*") .. " (best guess)" + else + version = "???" + end + end + local modded_version = MODDED_VERSION + if not modded_version or type(modded_version) ~= "string" then + local moddedSuccess, reqVersion = pcall(require, "SMODS.version") + if moddedSuccess and type(reqVersion) == "string" then + modded_version = reqVersion + else + modded_version = "???" + end + end + + local info = "Additional Context:\nBalatro Version: " .. version .. "\nModded Version: " .. + (modded_version) + local major, minor, revision, codename = love.getVersion() + info = info .. string.format("\nLÖVE Version: %d.%d.%d", major, minor, revision) + + local lovely_success, lovely = pcall(require, "lovely") + if lovely_success then + info = info .. "\nLovely Version: " .. lovely.version + end + if SMODS and SMODS.Mods then + local mod_strings = "" + local lovely_strings = "" + local i = 1 + local lovely_i = 1 + for _, v in pairs(SMODS.Mods) do + if (v.can_load and (not v.meta_mod or v.lovely_only)) or (v.lovely and not v.can_load and not v.disabled) then + if v.lovely_only or (v.lovely and not v.can_load) then + lovely_strings = lovely_strings .. "\n " .. lovely_i .. ": " .. v.name + lovely_i = lovely_i + 1 + if not v.can_load then + lovely_strings = lovely_strings .. "\n Has Steamodded mod that failed to load." + if #v.load_issues.dependencies > 0 then + lovely_strings = lovely_strings .. "\n Missing Dependencies:" + for k, v in ipairs(v.load_issues.dependencies) do + lovely_strings = lovely_strings .. "\n " .. k .. ". " .. v + end + end + if #v.load_issues.conflicts > 0 then + lovely_strings = lovely_strings .. "\n Conflicts:" + for k, v in ipairs(v.load_issues.conflicts) do + lovely_strings = lovely_strings .. "\n " .. k .. ". " .. v + end + end + if v.load_issues.outdated then + lovely_strings = lovely_strings .. "\n Outdated Mod." + end + if v.load_issues.main_file_not_found then + lovely_strings = lovely_strings .. "\n Main file not found. (" .. v.main_file ..")" + end + end + else + mod_strings = mod_strings .. "\n " .. i .. ": " .. v.name .. " by " .. + table.concat(v.author, ", ") .. " [ID: " .. v.id .. + (v.priority ~= 0 and (", Priority: " .. v.priority) or "") .. + (v.version and v.version ~= '0.0.0' and (", Version: " .. v.version) or "") .. + (v.lovely and (", Uses Lovely") or "") .. "]" + i = i + 1 + local debugInfo = v.debug_info + if debugInfo then + if type(debugInfo) == "string" then + if #debugInfo ~= 0 then + mod_strings = mod_strings .. "\n " .. debugInfo + end + elseif type(debugInfo) == "table" then + for kk, vv in pairs(debugInfo) do + if type(vv) ~= 'nil' then + vv = tostring(vv) + end + if #vv ~= 0 then + mod_strings = mod_strings .. "\n " .. kk .. ": " .. vv + end + end + end + end + end + end + end + info = info .. "\nSteamodded Mods:" .. mod_strings .. "\nLovely Mods:" .. lovely_strings + end + return info +end + +function injectStackTrace() + if (stackTraceAlreadyInjected) then + return + end + stackTraceAlreadyInjected = true + local STP = loadStackTracePlus() + local utf8 = require("utf8") + + -- Modifed from https://love2d.org/wiki/love.errorhandler + function love.errorhandler(msg) + msg = tostring(msg) + + if not sendErrorMessage then + function sendErrorMessage(msg) + print(msg) + end + end + if not sendInfoMessage then + function sendInfoMessage(msg) + print(msg) + end + end + + sendErrorMessage("Oops! The game crashed\n" .. STP.stacktrace(msg), 'StackTrace') + + if not love.window or not love.graphics or not love.event then + return + end + + if not love.graphics.isCreated() or not love.window.isOpen() then + local success, status = pcall(love.window.setMode, 800, 600) + if not success or not status then + return + end + end + + -- Reset state. + if love.mouse then + love.mouse.setVisible(true) + love.mouse.setGrabbed(false) + love.mouse.setRelativeMode(false) + if love.mouse.isCursorSupported() then + love.mouse.setCursor() + end + end + if love.joystick then + -- Stop all joystick vibrations. + for i, v in ipairs(love.joystick.getJoysticks()) do + v:setVibration() + end + end + if love.audio then + love.audio.stop() + end + + love.graphics.reset() + local font = love.graphics.setNewFont("resources/fonts/m6x11plus.ttf", 20) + + local background = {0, 0, 1} + if G and G.C and G.C.BLACK then + background = G.C.BLACK + end + love.graphics.clear(background) + love.graphics.origin() + + local trace = STP.stacktrace("", 3) + + local sanitizedmsg = {} + for char in msg:gmatch(utf8.charpattern) do + table.insert(sanitizedmsg, char) + end + sanitizedmsg = table.concat(sanitizedmsg) + + local err = {} + + table.insert(err, "Oops! The game crashed:") + if sanitizedmsg:find("Syntax error: game.lua:4: '=' expected near 'Game'") then + table.insert(err, + 'Duplicate installation of Steamodded detected! Please clean your installation: Steam Library > Balatro > Properties > Installed Files > Verify integrity of game files.') + else + table.insert(err, sanitizedmsg) + end + if #sanitizedmsg ~= #msg then + table.insert(err, "Invalid UTF-8 string in error message.") + end + + local success, msg = pcall(getDebugInfoForCrash) + if success and msg then + table.insert(err, '\n' .. msg) + sendInfoMessage(msg, 'StackTrace') + else + table.insert(err, "\n" .. "Failed to get additional context :/") + sendErrorMessage("Failed to get additional context :/\n" .. msg, 'StackTrace') + end + + for l in trace:gmatch("(.-)\n") do + table.insert(err, l) + end + + local p = table.concat(err, "\n") + + p = p:gsub("\t", "") + p = p:gsub("%[string \"(.-)\"%]", "%1") + + local scrollOffset = 0 + local endHeight = 0 + love.keyboard.setKeyRepeat(true) + + local function scrollDown(amt) + if amt == nil then + amt = 18 + end + scrollOffset = scrollOffset + amt + if scrollOffset > endHeight then + scrollOffset = endHeight + end + end + + local function scrollUp(amt) + if amt == nil then + amt = 18 + end + scrollOffset = scrollOffset - amt + if scrollOffset < 0 then + scrollOffset = 0 + end + end + + local pos = 70 + local arrowSize = 20 + + local function calcEndHeight() + local font = love.graphics.getFont() + local rw, lines = font:getWrap(p, love.graphics.getWidth() - pos * 2) + local lineHeight = font:getHeight() + local atBottom = scrollOffset == endHeight and scrollOffset ~= 0 + endHeight = #lines * lineHeight - love.graphics.getHeight() + pos * 2 + if (endHeight < 0) then + endHeight = 0 + end + if scrollOffset > endHeight or atBottom then + scrollOffset = endHeight + end + end + + local function draw() + if not love.graphics.isActive() then + return + end + love.graphics.clear(background) + calcEndHeight() + love.graphics.printf(p, pos, pos - scrollOffset, love.graphics.getWidth() - pos * 2) + if scrollOffset ~= endHeight then + love.graphics.polygon("fill", love.graphics.getWidth() - (pos / 2), + love.graphics.getHeight() - arrowSize, love.graphics.getWidth() - (pos / 2) + arrowSize, + love.graphics.getHeight() - (arrowSize * 2), love.graphics.getWidth() - (pos / 2) - arrowSize, + love.graphics.getHeight() - (arrowSize * 2)) + end + if scrollOffset ~= 0 then + love.graphics.polygon("fill", love.graphics.getWidth() - (pos / 2), arrowSize, + love.graphics.getWidth() - (pos / 2) + arrowSize, arrowSize * 2, + love.graphics.getWidth() - (pos / 2) - arrowSize, arrowSize * 2) + end + love.graphics.present() + end + + local fullErrorText = p + local function copyToClipboard() + if not love.system then + return + end + love.system.setClipboardText(fullErrorText) + p = p .. "\nCopied to clipboard!" + end + + p = p .. "\n\nPress ESC to exit\nPress R to restart the game" + if love.system then + p = p .. "\nPress Ctrl+C or tap to copy this error" + end + + if G then + -- Kill threads (makes restarting possible) + if G.SOUND_MANAGER and G.SOUND_MANAGER.channel then + G.SOUND_MANAGER.channel:push({ + type = 'kill' + }) + end + if G.SAVE_MANAGER and G.SAVE_MANAGER.channel then + G.SAVE_MANAGER.channel:push({ + type = 'kill' + }) + end + if G.HTTP_MANAGER and G.HTTP_MANAGER.channel then + G.HTTP_MANAGER.channel:push({ + type = 'kill' + }) + end + end + + return function() + love.event.pump() + + for e, a, b, c in love.event.poll() do + if e == "quit" then + return 1 + elseif e == "keypressed" and a == "escape" then + return 1 + elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then + copyToClipboard() + elseif e == "keypressed" and a == "r" then + SMODS.restart_game() + elseif e == "keypressed" and a == "down" then + scrollDown() + elseif e == "keypressed" and a == "up" then + scrollUp() + elseif e == "keypressed" and a == "pagedown" then + scrollDown(love.graphics.getHeight()) + elseif e == "keypressed" and a == "pageup" then + scrollUp(love.graphics.getHeight()) + elseif e == "keypressed" and a == "home" then + scrollOffset = 0 + elseif e == "keypressed" and a == "end" then + scrollOffset = endHeight + elseif e == "wheelmoved" then + scrollUp(b * 20) + elseif e == "gamepadpressed" and b == "dpdown" then + scrollDown() + elseif e == "gamepadpressed" and b == "dpup" then + scrollUp() + elseif e == "gamepadpressed" and b == "a" then + return "restart" + elseif e == "gamepadpressed" and b == "x" then + copyToClipboard() + elseif e == "gamepadpressed" and (b == "b" or b == "back" or b == "start") then + return 1 + elseif e == "touchpressed" then + local name = love.window.getTitle() + if #name == 0 or name == "Untitled" then + name = "Game" + end + local buttons = {"OK", "Cancel", "Restart"} + if love.system then + buttons[4] = "Copy to clipboard" + end + local pressed = love.window.showMessageBox("Quit " .. name .. "?", "", buttons) + if pressed == 1 then + return 1 + elseif pressed == 3 then + return "restart" + elseif pressed == 4 then + copyToClipboard() + end + end + end + + draw() + + if love.timer then + love.timer.sleep(0.1) + end + end + + end +end + +injectStackTrace() + +-- ---------------------------------------------- +-- --------MOD CORE API STACKTRACE END----------- diff --git a/Steamodded/src/game_object.lua b/Steamodded/src/game_object.lua new file mode 100644 index 0000000..3fe86d8 --- /dev/null +++ b/Steamodded/src/game_object.lua @@ -0,0 +1,3117 @@ +--- STEAMODDED CORE +--- MODULE API + +function loadAPIs() + ------------------------------------------------------------------------------------------------- + --- API CODE GameObject + ------------------------------------------------------------------------------------------------- + + --- GameObject base class. You should always use the appropriate subclass to register your object. + SMODS.GameObject = Object:extend() + SMODS.GameObject.subclasses = {} + function SMODS.GameObject:extend(o) + local cls = Object.extend(self) + for k, v in pairs(o or {}) do + cls[k] = v + end + self.subclasses[#self.subclasses + 1] = cls + cls.subclasses = {} + return cls + end + + function SMODS.GameObject:__call(o) + o = o or {} + assert(o.mod == nil) + o.mod = SMODS.current_mod + setmetatable(o, self) + for _, v in ipairs(o.required_params or {}) do + assert(not (o[v] == nil), ('Missing required parameter for %s declaration: %s'):format(o.set, v)) + end + if o:check_duplicate_register() then return end + -- also updates o.prefix_config + SMODS.add_prefixes(self, o) + if o:check_duplicate_key() then return end + o:register() + return o + end + + function SMODS.modify_key(obj, prefix, condition, key) + key = key or 'key' + -- condition == nil counts as true + if condition ~= false and obj[key] and prefix then + if string.sub(obj[key], 1, #prefix + 1) == prefix..'_' then + -- this happens within steamodded itself and I don't want to spam the logs with warnings, leaving this disabled for now + -- sendWarnMessage(("Attempted to prefix field %s=%s on object %s, already prefixed"):format(key, obj[key], obj.key), obj.set) + return + end + obj[key] = prefix .. '_' .. obj[key] + end + end + + function SMODS.add_prefixes(cls, obj, from_take_ownership) + if obj.prefix_config == false then return end + obj.prefix_config = obj.prefix_config or {} + if obj.raw_key then + sendWarnMessage(([[The field `raw_key` on %s is deprecated. +Set `prefix_config.key = false` on your object instead.]]):format(obj.key), obj.set) + obj.prefix_config.key = false + end + -- keep class defaults for unmodified keys in prefix_config + obj.prefix_config = SMODS.merge_defaults(obj.prefix_config, cls.prefix_config) + local mod = SMODS.current_mod + obj.prefix_config = SMODS.merge_defaults(obj.prefix_config, mod and mod.prefix_config) + obj.original_key = obj.key + local key_cfg = obj.prefix_config.key + if key_cfg ~= false then + if type(key_cfg) ~= 'table' then key_cfg = {} end + if not from_take_ownership then + SMODS.modify_key(obj, mod and mod.prefix, key_cfg.mod) + end + SMODS.modify_key(obj, cls.class_prefix, key_cfg.class) + end + local atlas_cfg = obj.prefix_config.atlas + if atlas_cfg ~= false then + if type(atlas_cfg) ~= 'table' then atlas_cfg = {} end + for _, v in ipairs({ 'atlas', 'hc_atlas', 'lc_atlas', 'hc_ui_atlas', 'lc_ui_atlas', 'sticker_atlas' }) do + if rawget(obj, v) then SMODS.modify_key(obj, mod and mod.prefix, atlas_cfg[v], v) end + end + end + local shader_cfg = obj.prefix_config.shader + SMODS.modify_key(obj, mod and mod.prefix, shader_cfg, 'shader') + local card_key_cfg = obj.prefix_config.card_key + SMODS.modify_key(obj, mod and mod.prefix, card_key_cfg, 'card_key') + local above_stake_cfg = obj.prefix_config.above_stake + if above_stake_cfg ~= false then + if type(above_stake_cfg) ~= 'table' then above_stake_cfg = {} end + SMODS.modify_key(obj, mod and mod.prefix, above_stake_cfg.mod, 'above_stake') + SMODS.modify_key(obj, cls.class_prefix, above_stake_cfg.class, 'above_stake') + end + local applied_stakes_cfg = obj.prefix_config.applied_stakes + if applied_stakes_cfg ~= false and obj.applied_stakes then + if type(applied_stakes_cfg) ~= 'table' then applied_stakes_cfg = {} end + for k,v in pairs(obj.applied_stakes) do + SMODS.modify_key(obj.applied_stakes, mod and mod.prefix, (applied_stakes_cfg[k] or {}).mod or applied_stakes_cfg.mod, k) + SMODS.modify_key(obj.applied_stakes, cls.class_prefix, (applied_stakes_cfg[k] or {}).class or applied_stakes_cfg.class, k) + end + end + local unlocked_stake_cfg = obj.prefix_config.unlocked_stake + if unlocked_stake_cfg ~= false then + if type(unlocked_stake_cfg) ~= 'table' then unlocked_stake_cfg = {} end + SMODS.modify_key(obj, mod and mod.prefix, unlocked_stake_cfg.mod, 'unlocked_stake') + SMODS.modify_key(obj, cls.class_prefix, unlocked_stake_cfg.class, 'unlocked_stake') + end + end + + function SMODS.GameObject:check_duplicate_register() + if self.registered then + sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set) + return true + end + return false + end + + -- Checked on __call but not take_ownership. For take_ownership, the key must exist + function SMODS.GameObject:check_duplicate_key() + if self.obj_table[self.key] or (self.get_obj and self:get_obj(self.key)) then + sendWarnMessage(('Object %s has the same key as an existing object, not registering.'):format(self.key), self.set) + sendWarnMessage('If you want to modify an existing object, use take_ownership()', self.set) + return true + end + return false + end + + function SMODS.GameObject:register() + if self:check_dependencies() then + self.obj_table[self.key] = self + self.obj_buffer[#self.obj_buffer + 1] = self.key + self.registered = true + end + end + + function SMODS.GameObject:check_dependencies() + local keep = true + if self.dependencies then + -- ensure dependencies are a table + if type(self.dependencies) == 'string' then self.dependencies = { self.dependencies } end + for _, v in ipairs(self.dependencies) do + self.mod.optional_dependencies[v] = true + if not SMODS.Mods[v] or not SMODS.Mods[v].can_load then keep = false end + end + end + return keep + end + + function SMODS.GameObject:process_loc_text() + SMODS.process_loc_text(G.localization.descriptions[self.set], self.key, self.loc_txt) + end + + --- Starting from this class, recursively searches for + --- functions with the given key on all subordinate classes + --- and run all found functions with the given arguments + function SMODS.GameObject:send_to_subclasses(func, ...) + if rawget(self, func) and type(self[func]) == 'function' then self[func](self, ...) end + for _, cls in ipairs(self.subclasses) do + cls:send_to_subclasses(func, ...) + end + end + + + -- Inject all direct instances `o` of the class by calling `o:inject()`. + -- Also inject anything necessary for the class itself. + function SMODS.GameObject:inject_class() + local inject_time = 0 + local start_time = love.timer.getTime() + self:send_to_subclasses('pre_inject_class') + local end_time = love.timer.getTime() + inject_time = end_time - start_time + start_time = end_time + local o = nil + for i, key in ipairs(self.obj_buffer) do + o = self.obj_table[key] + o.atlas = o.atlas or o.set + + if o._discovered_unlocked_overwritten then + assert(o._saved_d_u) + o.discovered, o.unlocked = o._d, o._u + o._discovered_unlocked_overwritten = false + else + SMODS._save_d_u(o) + end + + -- Add centers to pools + o:inject(i) + + -- Setup Localize text + o:process_loc_text() + if self.log_interval and i%(self.log_interval) == 0 then + end_time = love.timer.getTime() + inject_time = inject_time + end_time - start_time + start_time = end_time + local alert = ('[%s] Injecting %s: %.3f ms'):format(string.rep('0', 4-#tostring(i))..i, self.set, inject_time*1000) + sendTraceMessage(alert, 'TIMER') + boot_print_stage(alert) + end + end + self:send_to_subclasses('post_inject_class') + end_time = love.timer.getTime() + inject_time = inject_time + end_time - start_time + local n = #self.obj_buffer + local alert = ('[%s] Injected %s in %.3f ms'):format(string.rep('0',4-#tostring(n))..n, self.set, inject_time*1000) + sendInfoMessage(alert, 'TIMER') + boot_print_stage(alert) + end + + --- Takes control of vanilla objects. Child class must implement get_obj for this to function. + function SMODS.GameObject:take_ownership(key, obj, silent) + if self.check_duplicate_register(obj) then return end + obj.key = key + obj.mod = nil + SMODS.add_prefixes(self, obj, true) + key = obj.key + local orig_o = self.obj_table[key] or (self.get_obj and self:get_obj(key)) + if not orig_o then + sendWarnMessage( + ('Cannot take ownership of %s: Does not exist.'):format(key), self.set + ) + return + end + local is_loc_modified = obj.loc_txt or obj.loc_vars or obj.generate_ui + if is_loc_modified then orig_o.is_loc_modified = true end + if not orig_o.is_loc_modified then + -- Setting generate_ui to this sentinel value + -- makes vanilla localization code run instead of SMODS's code + orig_o.generate_ui = 0 + end + -- TODO + -- it's unclear how much we should modify `obj` on a failed take_ownership call. + -- do we make sure the metatable is set early, or wait until the end? + setmetatable(orig_o, self) + if orig_o.mod then + orig_o.dependencies = orig_o.dependencies or {} + if not silent then table.insert(orig_o.dependencies, SMODS.current_mod.id) end + else + orig_o.mod = SMODS.current_mod + if silent then orig_o.no_main_mod_badge = true end + orig_o.rarity_original = orig_o.rarity + end + if orig_o._saved_d_u then + orig_o.discovered, orig_o.unlocked = orig_o._d, orig_o._u + orig_o._saved_d_u = false + orig_o._discovered_unlocked_overwritten = false + end + for k, v in pairs(obj) do orig_o[k] = v end + SMODS._save_d_u(orig_o) + orig_o.taken_ownership = true + orig_o:register() + return orig_o + end + + -- Inject all SMODS Objects that are part of this class or a subclass. + function SMODS.injectObjects(class) + if class.obj_table and class.obj_buffer then + class:inject_class() + else + for _, subclass in ipairs(class.subclasses) do SMODS.injectObjects(subclass) end + end + end + + -- Internal function + -- Creates a list of objects from a list of keys. + -- Currently used for a special case when selecting a random suit/rank. + function SMODS.GameObject:obj_list(reversed) + local lb, ub, step = 1, #self.obj_buffer, 1 + if reversed then lb, ub, step = ub, lb, -1 end + local res = {} + for i = lb, ub, step do + res[#res+1] = self.obj_table[self.obj_buffer[i]] + end + return res + end + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Language + ------------------------------------------------------------------------------------------------- + + SMODS.Languages = {} + SMODS.Language = SMODS.GameObject:extend { + obj_table = SMODS.Languages, + set = 'Language', + obj_buffer = {}, + required_params = { + 'key', + 'label', + }, + prefix_config = { key = false }, + process_loc_text = function() end, + inject = function(self) + self.font = self.font or 1 + if type(self.font) == 'table' and not self.font.FONT and self.font.file and self.font.render_scale then + local data = assert(NFS.newFileData(self.mod.path .. 'assets/fonts/' .. self.font.file), ('Failed to collect file data for font of language %s'):format(self.key)) + self.font.FONT = love.graphics.newFont(data, self.font.render_scale) + elseif type(self.font) ~= 'table' then + self.font = G.FONTS[type(self.font) == 'number' and self.font or 1] or G.FONTS[1] + end + G.LANGUAGES[self.key] = self + if self.key == G.SETTINGS.language then G.LANG = self end + end, + } + + ------------------------------------------------------------------------------------------------- + ----- INTERNAL API CODE GameObject._Loc_Pre + ------------------------------------------------------------------------------------------------- + + SMODS._Loc_Pre = SMODS.GameObject:extend { + obj_table = {}, + obj_buffer = {}, + silent = true, + set = '[INTERNAL]', + register = function() error('INTERNAL CLASS, DO NOT CALL') end, + pre_inject_class = function() + SMODS.handle_loc_file(SMODS.path) + if SMODS.dump_loc then SMODS.dump_loc.pre_inject = copy_table(G.localization) end + for _, mod in ipairs(SMODS.mod_list) do + if mod.process_loc_text and type(mod.process_loc_text) == 'function' then + mod.process_loc_text() + end + end + end + } + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Atlas + ------------------------------------------------------------------------------------------------- + + SMODS.Atlases = {} + SMODS.Atlas = SMODS.GameObject:extend { + obj_table = SMODS.Atlases, + obj_buffer = {}, + disable_mipmap = false, + required_params = { + 'key', + 'path', + 'px', + 'py' + }, + atlas_table = 'ASSET_ATLAS', + set = 'Atlas', + register = function(self) + if self.registered then + sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set) + return + end + if self.language then + self.key_noloc = self.key + self.key = ('%s_%s'):format(self.key, self.language) + end + -- needed for changing high contrast settings, apparently + self.name = self.key + SMODS.Atlas.super.register(self) + end, + inject = function(self) + local file_path = type(self.path) == 'table' and + (self.path[G.SETTINGS.language] or self.path['default'] or self.path['en-us']) or self.path + if file_path == 'DEFAULT' then return end + -- language specific sprites override fully defined sprites only if that language is set + if self.language and not (G.SETTINGS.language == self.language) then return end + if not self.language and self.obj_table[('%s_%s'):format(self.key, G.SETTINGS.language)] then return end + self.full_path = (self.mod and self.mod.path or SMODS.path) .. + 'assets/' .. G.SETTINGS.GRAPHICS.texture_scaling .. 'x/' .. file_path + local file_data = assert(NFS.newFileData(self.full_path), + ('Failed to collect file data for Atlas %s'):format(self.key)) + self.image_data = assert(love.image.newImageData(file_data), + ('Failed to initialize image data for Atlas %s'):format(self.key)) + self.image = love.graphics.newImage(self.image_data, + { mipmaps = true, dpiscale = G.SETTINGS.GRAPHICS.texture_scaling }) + G[self.atlas_table][self.key_noloc or self.key] = self + + local mipmap_level = SMODS.config.graphics_mipmap_level_options[SMODS.config.graphics_mipmap_level] + if not self.disable_mipmap and mipmap_level and mipmap_level > 0 then + self.image:setMipmapFilter('linear', mipmap_level) + end + end, + process_loc_text = function() end, + pre_inject_class = function(self) + G:set_render_settings() -- restore originals first in case a texture pack was disabled + end + } + + SMODS.Atlas { + key = 'mod_tags', + path = 'mod_tags.png', + px = 34, + py = 34, + } + SMODS.Atlas { + key = 'achievements', + path = 'default_achievements.png', + px = 66, + py = 66, + } + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Sound + ------------------------------------------------------------------------------------------------- + + SMODS.Sounds = {} + SMODS.Sound = SMODS.GameObject:extend { + obj_buffer = {}, + set = 'Sound', + obj_table = SMODS.Sounds, + stop_sounds = {}, + replace_sounds = {}, + required_params = { + 'key', + 'path' + }, + process_loc_text = function() end, + register = function(self) + if self.registered then + sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set) + return + end + self.sound_code = self.key + if self.replace then + local replace, times, args + if type(self.replace) == 'table' then + replace, times, args = self.replace.key, self.replace.times or -1, self.replace.args + else + replace, times = self.replace, -1 + end + self.replace_sounds[replace] = { key = self.key, times = times, args = args } + end + -- TODO detect music state based on if select_music_track exists + assert(not self.select_music_track or self.key:find('music')) + SMODS.Sound.super.register(self) + end, + inject = function(self) + local file_path = type(self.path) == 'table' and + (self.path[G.SETTINGS.language] or self.path['default'] or self.path['en-us']) or self.path + if file_path == 'DEFAULT' then return end + self.full_path = (self.mod and self.mod.path or SMODS.path) .. + 'assets/sounds/' .. file_path + self.data = NFS.read('data', self.full_path) + self.decoder = love.sound.newDecoder(self.data) + self.should_stream = string.find(self.key, 'music') or string.find(self.key, 'stream') or string.find(self.key, 'ambient') + self.sound = love.audio.newSource(self.decoder, self.should_stream and 'stream' or 'static') + G.SOUND_MANAGER.channel:push({ type = 'sound_source', sound_code = self.sound_code, data = self.data, should_stream = self.should_stream, per = self.pitch, vol = self.volume }) + end, + register_global = function(self) + local mod = SMODS.current_mod + if not mod then return end + for _, filename in ipairs(NFS.getDirectoryItems(mod.path .. 'assets/sounds/')) do + local extension = string.sub(filename, -4) + if extension == '.ogg' or extension == '.mp3' or extension == '.wav' then -- please use .ogg or .wav files + local sound_code = string.sub(filename, 1, -5) + self { + key = sound_code, + path = filename, + } + end + end + end, + -- retaining this function for mod compat + play = function(self, pitch, volume, stop_previous_instance, key) + return play_sound(key or self.sound_code, pitch, volume) + end, + create_stop_sound = function(self, key, times) + times = times or -1 + self.stop_sounds[key] = times + end, + create_replace_sound = function(self, replace_sound) + self.replace = replace_sound + local replace, times, args + if type(self.replace) == 'table' then + replace, times, args = self.replace.key, self.replace.times or -1, self.replace.args + else + replace, times = self.replace, -1 + end + self.replace_sounds[replace] = { key = self.key, times = times, args = args } + end, + get_current_music = function(self) + local track + local maxp = -math.huge + for _, v in ipairs(self.obj_buffer) do + local s = self.obj_table[v] + if type(s.select_music_track) == 'function' then + local res = s:select_music_track() + if res then + if type(res) ~= 'number' then res = 0 end + if res > maxp then track, maxp = v, res end + end + end + end + return track + end + } + + local play_sound_ref = play_sound + function play_sound(sound_code, per, vol) + local replace_sound = SMODS.Sound.replace_sounds[sound_code] + if replace_sound then + local sound = SMODS.Sounds[replace_sound.key] + local rt + if replace_sound.args then + local args = replace_sound.args + if type(args) == 'function' then args = args(sound, { pitch = per, volume = vol }) end + play_sound(sound.sound_code, args.pitch, args.volume) + if not args.continue_base_sound then rt = true end + else + play_sound(sound.sound_code, per, vol) + rt = true + end + if replace_sound.times > 0 then replace_sound.times = replace_sound.times - 1 end + if replace_sound.times == 0 then SMODS.Sound.replace_sounds[sound_code] = nil end + if rt then return end + end + local stop_sound = SMODS.Sound.stop_sounds[sound_code] + if stop_sound then + if stop_sound > 0 then + SMODS.Sound.stop_sounds[sound_code] = stop_sound - 1 + end + return + end + + return play_sound_ref(sound_code, per, vol) + end + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Stake + ------------------------------------------------------------------------------------------------- + + SMODS.Stakes = {} + SMODS.Stake = SMODS.GameObject:extend { + obj_table = SMODS.Stakes, + obj_buffer = {}, + class_prefix = 'stake', + unlocked = false, + set = 'Stake', + atlas = 'chips', + pos = { x = 0, y = 0 }, + injected = false, + required_params = { + 'key', + 'pos', + 'applied_stakes' + }, + pre_inject_class = function(self) + G.P_CENTER_POOLS[self.set] = {} + G.P_STAKES = {} + end, + inject = function(self) + if not self.injected then + -- Inject stake in the correct spot + self.count = #G.P_CENTER_POOLS[self.set] + 1 + self.order = self.count + if self.above_stake and G.P_STAKES[self.above_stake] then + self.order = G.P_STAKES[self.above_stake].order + 1 + end + for _, v in pairs(G.P_STAKES) do + if v.order >= self.order then + v.order = v.order + 1 + end + end + G.P_STAKES[self.key] = self + table.insert(G.P_CENTER_POOLS.Stake, self) + -- Sticker sprites (stake_ prefix is removed for vanilla compatiblity) + if self.sticker_pos ~= nil then + G.shared_stickers[self.key:sub(7)] = Sprite(0, 0, G.CARD_W, G.CARD_H, + G.ASSET_ATLAS[self.sticker_atlas] or G.ASSET_ATLAS["stickers"], self.sticker_pos) + G.sticker_map[self.key] = self.key:sub(7) + else + G.sticker_map[self.key] = nil + end + else + G.P_STAKES[self.key] = self + SMODS.insert_pool(G.P_CENTER_POOLS.Stake, self) + end + self.injected = true + -- should only need to do this once per injection routine + end, + post_inject_class = function(self) + table.sort(G.P_CENTER_POOLS[self.set], function(a, b) return a.order < b.order end) + for _,stake in pairs(G.P_CENTER_POOLS.Stake) do + local applied = SMODS.build_stake_chain(stake) + stake.stake_level = 0 + for i,_ in ipairs(G.P_CENTER_POOLS.Stake) do + if applied[i] then stake.stake_level = stake.stake_level+1 end + end + end + G.C.STAKES = {} + for i = 1, #G.P_CENTER_POOLS[self.set] do + G.C.STAKES[i] = G.P_CENTER_POOLS[self.set][i].colour or G.C.WHITE + end + end, + process_loc_text = function(self) + -- empty loc_txt indicates there are existing values that shouldn't be changed or it isn't necessary + if not self.loc_txt or not next(self.loc_txt) then return end + local target = self.loc_txt[G.SETTINGS.language] or self.loc_txt['default'] or self.loc_txt['en-us'] or + self.loc_txt + local applied_text = "{s:0.8}" .. localize('b_applies_stakes_1') + local any_applied + for _, v in pairs(self.applied_stakes) do + any_applied = true + applied_text = applied_text .. + localize { set = self.set, key = v, type = 'name_text' } .. ', ' + end + applied_text = applied_text:sub(1, -3) + if not any_applied then + applied_text = "{s:0.8}" + else + applied_text = applied_text .. localize('b_applies_stakes_2') + end + local desc_target = copy_table(target) + table.insert(desc_target.text, applied_text) + G.localization.descriptions[self.set][self.key] = desc_target + SMODS.process_loc_text(G.localization.descriptions["Other"], self.key:sub(7) .. "_sticker", self.loc_txt, + 'sticker') + end, + get_obj = function(self, key) return G.P_STAKES[key] end + } + + function SMODS.build_stake_chain(stake, applied) + if not applied then applied = {} end + if not stake or applied[stake.order] then return end + applied[stake.order] = stake.order + if not stake.applied_stakes then + return applied + end + for _, s in pairs(stake.applied_stakes) do + SMODS.build_stake_chain(G.P_STAKES[s], applied) + end + return applied + end + + function SMODS.setup_stake(i) + local applied_stakes = SMODS.build_stake_chain(G.P_CENTER_POOLS.Stake[i]) + for stake, _ in pairs(applied_stakes) do + if G.P_CENTER_POOLS['Stake'][stake].modifiers then + G.P_CENTER_POOLS['Stake'][stake].modifiers() + end + end + end + + --Register vanilla stakes + G.P_STAKES = {} + SMODS.Stake { + name = "White Stake", + key = "white", + unlocked_stake = "red", + unlocked = true, + applied_stakes = {}, + pos = { x = 0, y = 0 }, + sticker_pos = { x = 1, y = 0 }, + colour = G.C.WHITE, + loc_txt = {} + } + SMODS.Stake { + name = "Red Stake", + key = "red", + unlocked_stake = "green", + applied_stakes = { "white" }, + pos = { x = 1, y = 0 }, + sticker_pos = { x = 2, y = 0 }, + modifiers = function() + G.GAME.modifiers.no_blind_reward = G.GAME.modifiers.no_blind_reward or {} + G.GAME.modifiers.no_blind_reward.Small = true + end, + colour = G.C.RED, + loc_txt = {} + } + SMODS.Stake { + name = "Green Stake", + key = "green", + unlocked_stake = "black", + applied_stakes = { "red" }, + pos = { x = 2, y = 0 }, + sticker_pos = { x = 3, y = 0 }, + modifiers = function() + G.GAME.modifiers.scaling = (G.GAME.modifiers.scaling or 1) + 1 + end, + colour = G.C.GREEN, + loc_txt = {} + } + SMODS.Stake { + name = "Black Stake", + key = "black", + unlocked_stake = "blue", + applied_stakes = { "green" }, + pos = { x = 4, y = 0 }, + sticker_pos = { x = 0, y = 1 }, + modifiers = function() + G.GAME.modifiers.enable_eternals_in_shop = true + end, + colour = G.C.BLACK, + loc_txt = {} + } + SMODS.Stake { + name = "Blue Stake", + key = "blue", + unlocked_stake = "purple", + applied_stakes = { "black" }, + pos = { x = 3, y = 0 }, + sticker_pos = { x = 4, y = 0 }, + modifiers = function() + G.GAME.starting_params.discards = G.GAME.starting_params.discards - 1 + end, + colour = G.C.BLUE, + loc_txt = {} + } + SMODS.Stake { + name = "Purple Stake", + key = "purple", + unlocked_stake = "orange", + applied_stakes = { "blue" }, + pos = { x = 0, y = 1 }, + sticker_pos = { x = 1, y = 1 }, + modifiers = function() + G.GAME.modifiers.scaling = (G.GAME.modifiers.scaling or 1) + 1 + end, + colour = G.C.PURPLE, + loc_txt = {} + } + SMODS.Stake { + name = "Orange Stake", + key = "orange", + unlocked_stake = "gold", + applied_stakes = { "purple" }, + pos = { x = 1, y = 1 }, + sticker_pos = { x = 2, y = 1 }, + modifiers = function() + G.GAME.modifiers.enable_perishables_in_shop = true + end, + colour = G.C.ORANGE, + loc_txt = {} + } + SMODS.Stake { + name = "Gold Stake", + key = "gold", + applied_stakes = { "orange" }, + pos = { x = 2, y = 1 }, + sticker_pos = { x = 3, y = 1 }, + modifiers = function() + G.GAME.modifiers.enable_rentals_in_shop = true + end, + colour = G.C.GOLD, + shiny = true, + loc_txt = {} + } + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.Rarity + ------------------------------------------------------------------------------------------------- + + SMODS.Rarities = {} + SMODS.Rarity = SMODS.GameObject:extend { + obj_table = SMODS.Rarities, + obj_buffer = {}, + set = 'Rarity', + required_params = { + 'key', + }, + badge_colour = HEX 'FFFFFF', + default_weight = 0, + inject = function(self) + G.P_JOKER_RARITY_POOLS[self.key] = {} + G.C.RARITY[self.key] = self.badge_colour + end, + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.misc.labels, "k_"..self.key:lower(), self.loc_txt, 'name') + SMODS.process_loc_text(G.localization.misc.dictionary, "k_"..self.key:lower(), self.loc_txt, 'name') + end, + get_rarity_badge = function(self, rarity) + local vanilla_rarity_keys = {localize('k_common'), localize('k_uncommon'), localize('k_rare'), localize('k_legendary')} + if (vanilla_rarity_keys)[rarity] then + return vanilla_rarity_keys[rarity] --compat layer in case function gets the int of the rarity + else + return localize("k_"..rarity:lower()) + end + end, + } + + function SMODS.inject_rarity(object_type, rarity) + if not object_type.rarities then + object_type.rarities = {} + object_type.rarity_pools = {} + end + object_type.rarities[#object_type.rarities+1] = { + key = rarity.key, + weight = type(rarity.pools[object_type.key]) == "table" and rarity.pools[object_type.key].weight or rarity.default_weight + } + for _, vv in ipairs(object_type.rarities) do + local default_rarity_check = {["Common"] = 1, ["Uncommon"] = 2, ["Rare"] = 3, ["Legendary"] = 4} + if default_rarity_check[vv.key] then + object_type.rarity_pools[default_rarity_check[vv.key]] = {} + else + object_type.rarity_pools[vv.key] = {} + end + end + end + + local game_init_game_object_ref = Game.init_game_object + function Game:init_game_object() + local t = game_init_game_object_ref(self) + for _, v in pairs(SMODS.Rarities) do + local key = v.key:lower() .. '_mod' + t[key] = t[key] or 1 + end + return t + end + + SMODS.Rarity{ + key = "Common", + loc_txt = {}, + default_weight = 0.7, + badge_colour = HEX('009dff'), + get_weight = function(self, weight, object_type) + return weight + end, + } + + SMODS.Rarity{ + key = "Uncommon", + loc_txt = {}, + default_weight = 0.25, + badge_colour = HEX("4BC292"), + get_weight = function(self, weight, object_type) + return weight + end, + } + + SMODS.Rarity{ + key = "Rare", + loc_txt = {}, + default_weight = 0.05, + badge_colour = HEX('fe5f55'), + get_weight = function(self, weight, object_type) + return weight + end, + } + + SMODS.Rarity{ + key = "Legendary", + loc_txt = {}, + default_weight = 0, + badge_colour = HEX("b26cbb"), + get_weight = function(self, weight, object_type) + return weight + end, + } + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.ObjectType + ------------------------------------------------------------------------------------------------- + + SMODS.ObjectTypes = {} + SMODS.ObjectType = SMODS.GameObject:extend { + obj_table = SMODS.ObjectTypes, + obj_buffer = {}, + set = 'ObjectType', + required_params = { + 'key', + }, + prefix_config = { key = false }, + inject = function(self) + G.P_CENTER_POOLS[self.key] = G.P_CENTER_POOLS[self.key] or {} + local injected_rarities = {} + if self.rarities then + self.rarity_pools = {} + for _, v in ipairs(self.rarities) do + if not v.weight then v.weight = SMODS.Rarities[v.key].default_weight end + local default_rarity_check = {["Common"] = 1, ["Uncommon"] = 2, ["Rare"] = 3, ["Legendary"] = 4} + if default_rarity_check[v.key] then + self.rarity_pools[default_rarity_check[v.key]] = {} + else + self.rarity_pools[v.key] = {} + end + injected_rarities[v.key] = true + end + end + for _, v in pairs(SMODS.Rarities) do + if v.pools and v.pools[self.key] and not injected_rarities[v.key] then SMODS.inject_rarity(self, v) end + end + end, + inject_card = function(self, center) + if center.set ~= self.key then SMODS.insert_pool(G.P_CENTER_POOLS[self.key], center) end + local default_rarity_check = {["Common"] = 1, ["Uncommon"] = 2, ["Rare"] = 3, ["Legendary"] = 4} + if self.rarities and center.rarity and self.rarity_pools[default_rarity_check[center.rarity] or center.rarity] then + SMODS.insert_pool(self.rarity_pools[default_rarity_check[center.rarity] or center.rarity], center) + end + end, + delete_card = function(self, center) + if center.set ~= self.key then SMODS.remove_pool(G.P_CENTER_POOLS[self.key], center.key) end + local default_rarity_check = {["Common"] = 1, ["Uncommon"] = 2, ["Rare"] = 3, ["Legendary"] = 4} + if self.rarities and center.rarity and self.rarity_pools[default_rarity_check[center.rarity] or center.rarity] then + SMODS.remove_pool(self.rarity_pools[default_rarity_check[center.rarity] or center.rarity], center.key) + end + end, + } + + SMODS.ObjectType{ + key = "Joker", + rarities = { + { key = "Common" }, + { key = "Uncommon" }, + { key = "Rare" }, + }, + } + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.ConsumableType + ------------------------------------------------------------------------------------------------- + + SMODS.ConsumableTypes = {} + SMODS.ConsumableType = SMODS.ObjectType:extend { + ctype_buffer = {}, + set = 'ConsumableType', + required_params = { + 'key', + 'primary_colour', + 'secondary_colour', + }, + prefix_config = { key = false }, + collection_rows = { 6, 6 }, + create_UIBox_your_collection = function(self) + local deck_tables = {} + + G.your_collection = {} + for j = 1, #self.collection_rows do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2 * G.ROOM.T.w / 2, G.ROOM.T.h, + (self.collection_rows[j] + 0.25) * G.CARD_W, + 1 * G.CARD_H, + { card_limit = self.collection_rows[j], type = 'title', highlight_limit = 0, collection = true }) + table.insert(deck_tables, + { + n = G.UIT.R, + config = { align = "cm", padding = 0, no_fill = true }, + nodes = { + { n = G.UIT.O, config = { object = G.your_collection[j] } } + } + } + ) + end + + local consumable_pool = SMODS.collection_pool(G.P_CENTER_POOLS[self.key]) + + local sum = 0 + for j = 1, #G.your_collection do + for i = 1, self.collection_rows[j] do + sum = sum + 1 + local center = consumable_pool[sum] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, + G.CARD_W, G.CARD_H, nil, center) + card:start_materialize(nil, i > 1 or j > 1) + G.your_collection[j]:emplace(card) + end + end + + local center_options = {} + for i = 1, math.ceil(#consumable_pool / sum) do + table.insert(center_options, + localize('k_page') .. + ' ' .. tostring(i) .. '/' .. tostring(math.ceil(#consumable_pool / sum))) + end + + INIT_COLLECTION_CARD_ALERTS() + local option_nodes = { create_option_cycle({ + options = center_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_collection_' .. string.lower(self.key) .. '_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = 1, + colour = G.C.RED, + no_pips = true + }) } + local type_buf = {} + if G.ACTIVE_MOD_UI then + for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do + if modsCollectionTally(G.P_CENTER_POOLS[v]).of > 0 then type_buf[#type_buf + 1] = v end + end + else + type_buf = SMODS.ConsumableType.ctype_buffer + end + local t = create_UIBox_generic_options({ + back_func = #type_buf>3 and 'your_collection_consumables' or G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', + contents = { + { n = G.UIT.R, config = { align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05 }, nodes = deck_tables }, + { n = G.UIT.R, config = { align = "cm", padding = 0 }, nodes = option_nodes }, + } + }) + return t + end, + register = function(self) + SMODS.ConsumableType.super.register(self) + if self:check_dependencies() then + SMODS.ConsumableType.ctype_buffer[#SMODS.ConsumableType.ctype_buffer+1] = self.key + end + end, + inject = function(self) + SMODS.ObjectType.inject(self) + SMODS.ConsumableTypes[self.key] = self + G.localization.descriptions[self.key] = G.localization.descriptions[self.key] or {} + G.C.SET[self.key] = self.primary_colour + G.C.SECONDARY_SET[self.key] = self.secondary_colour + G.FUNCS['your_collection_' .. string.lower(self.key) .. 's'] = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu { + definition = self:create_UIBox_your_collection(), + } + end + G.FUNCS['your_collection_' .. string.lower(self.key) .. '_page'] = function(args) + if not args or not args.cycle_config then return end + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards, 1, -1 do + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + local sum = 0 + for j = 1, #G.your_collection do + sum = sum + self.collection_rows[j] + end + + local consumable_pool = {} + if G.ACTIVE_MOD_UI then + for _, v in ipairs(G.P_CENTER_POOLS[self.key]) do + if v.mod and G.ACTIVE_MOD_UI.id == v.mod.id then consumable_pool[#consumable_pool+1] = v end + end + else + consumable_pool = G.P_CENTER_POOLS[self.key] + end + + sum = sum * (args.cycle_config.current_option - 1) + for j = 1, #G.your_collection do + for i = 1, self.collection_rows[j] do + sum = sum + 1 + local center = consumable_pool[sum] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, + G.your_collection[j].T.y, G + .CARD_W, G.CARD_H, G.P_CARDS.empty, center) + card:start_materialize(nil, i > 1 or j > 1) + G.your_collection[j]:emplace(card) + end + end + INIT_COLLECTION_CARD_ALERTS() + end + end, + process_loc_text = function(self) + if not next(self.loc_txt) then return end + SMODS.process_loc_text(G.localization.misc.dictionary, 'k_' .. string.lower(self.key), self.loc_txt, 'name') + SMODS.process_loc_text(G.localization.misc.dictionary, 'b_' .. string.lower(self.key) .. '_cards', + self.loc_txt, 'collection') + -- SMODS.process_loc_text(G.localization.misc.labels, string.lower(self.key), self.loc_txt, 'label') -- redundant + SMODS.process_loc_text(G.localization.descriptions.Other, 'undiscovered_' .. string.lower(self.key), + self.loc_txt, 'undiscovered') + end, + } + + SMODS.ConsumableType { + key = 'Tarot', + collection_rows = { 5, 6 }, + primary_colour = G.C.SET.Tarot, + secondary_colour = G.C.SECONDARY_SET.Tarot, + inject_card = function(self, center) + SMODS.ObjectType.inject_card(self, center) + SMODS.insert_pool(G.P_CENTER_POOLS['Tarot_Planet'], center) + end, + delete_card = function(self, center) + SMODS.ObjectType.delete_card(self, center) + SMODS.remove_pool(G.P_CENTER_POOLS['Tarot_Planet'], center.key) + end, + loc_txt = {}, + } + SMODS.ConsumableType { + key = 'Planet', + collection_rows = { 6, 6 }, + primary_colour = G.C.SET.Planet, + secondary_colour = G.C.SECONDARY_SET.Planet, + inject_card = function(self, center) + SMODS.ObjectType.inject_card(self, center) + SMODS.insert_pool(G.P_CENTER_POOLS['Tarot_Planet'], center) + end, + delete_card = function(self, center) + SMODS.ObjectType.delete_card(self, center) + SMODS.remove_pool(G.P_CENTER_POOLS['Tarot_Planet'], center.key) + end, + loc_txt = {}, + } + SMODS.ConsumableType { + key = 'Spectral', + collection_rows = { 4, 5 }, + primary_colour = G.C.SET.Spectral, + secondary_colour = G.C.SECONDARY_SET.Spectral, + loc_txt = {}, + } + + local game_init_game_object_ref = Game.init_game_object + function Game:init_game_object() + local t = game_init_game_object_ref(self) + for _, v in pairs(SMODS.ConsumableTypes) do + local key = v.key:lower() .. '_rate' + t[key] = v.shop_rate or t[key] or 0 + end + return t + end + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Center + ------------------------------------------------------------------------------------------------- + + SMODS.Centers = {} + --- Shared class for center objects. Holds no default values; only register an object directly using this if it doesn't fit any subclass, creating one isn't justified and you know what you're doing. + SMODS.Center = SMODS.GameObject:extend { + obj_table = SMODS.Centers, + obj_buffer = {}, + set = 'Center', -- For logging purposes | Subclasses should change this + get_obj = function(self, key) return G.P_CENTERS[key] end, + register = function(self) + -- 0.9.8 defense + self.name = self.name or self.key + SMODS.Center.super.register(self) + end, + inject = function(self) + G.P_CENTERS[self.key] = self + if not self.omit then SMODS.insert_pool(G.P_CENTER_POOLS[self.set], self) end + for k, v in pairs(SMODS.ObjectTypes) do + -- Should "cards" be formatted as `{[
    ] = true}` or {
    }? + -- Changing "cards" and "pools" wouldn't be hard to do, just depends on preferred format + if ((self.pools and self.pools[k]) or (v.cards and v.cards[self.key])) then + SMODS.ObjectTypes[k]:inject_card(self) + end + end + end, + delete = function(self) + G.P_CENTERS[self.key] = nil + SMODS.remove_pool(G.P_CENTER_POOLS[self.set], self.key) + for k, v in pairs(SMODS.ObjectTypes) do + if ((self.pools and self.pools[k]) or (v.cards and v.cards[self.key])) then + SMODS.ObjectTypes[k]:remove_card(self) + end + end + local j + for i, v in ipairs(self.obj_buffer) do + if v == self.key then j = i end + end + if j then table.remove(self.obj_buffer, j) end + self = nil + return true + end, + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + local target = { + type = 'descriptions', + key = self.key, + set = self.set, + nodes = desc_nodes, + vars = + specific_vars or {} + } + local res = {} + if self.loc_vars and type(self.loc_vars) == 'function' then + res = self:loc_vars(info_queue, card) or {} + target.vars = res.vars or target.vars + target.key = res.key or target.key + target.set = res.set or target.set + target.scale = res.scale + target.text_colour = res.text_colour + end + if desc_nodes == full_UI_table.main and not full_UI_table.name and self.set ~= 'Enhanced' then + full_UI_table.name = localize { type = 'name', set = target.set, key = target.key, nodes = full_UI_table.name } + elseif desc_nodes ~= full_UI_table.main and not desc_nodes.name and self.set ~= 'Enhanced' then + desc_nodes.name = localize{type = 'name_text', key = target.key, set = target.set } + end + if specific_vars and specific_vars.debuffed and not res.replace_debuff then + target = { type = 'other', key = 'debuffed_' .. + (specific_vars.playing_card and 'playing_card' or 'default'), nodes = desc_nodes } + end + if res.main_start then + desc_nodes[#desc_nodes + 1] = res.main_start + end + localize(target) + if res.main_end then + desc_nodes[#desc_nodes + 1] = res.main_end + end + desc_nodes.background_colour = res.background_colour + end + } + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.Center.Joker + ------------------------------------------------------------------------------------------------- + + SMODS.Joker = SMODS.Center:extend { + rarity = 1, + unlocked = true, + discovered = false, + blueprint_compat = false, + perishable_compat = true, + eternal_compat = true, + pos = { x = 0, y = 0 }, + cost = 3, + config = {}, + set = 'Joker', + atlas = 'Joker', + class_prefix = 'j', + required_params = { + 'key', + }, + inject = function(self) + -- call the parent function to ensure all pools are set + SMODS.Center.inject(self) + if self.taken_ownership and self.rarity_original == self.rarity then + SMODS.remove_pool(G.P_JOKER_RARITY_POOLS[self.rarity_original], self.key) + SMODS.insert_pool(G.P_JOKER_RARITY_POOLS[self.rarity], self, false) + else + SMODS.insert_pool(G.P_JOKER_RARITY_POOLS[self.rarity], self) + local vanilla_rarities = {["Common"] = 1, ["Uncommon"] = 2, ["Rare"] = 3, ["Legendary"] = 4} + if vanilla_rarities[self.rarity] then + SMODS.insert_pool(G.P_JOKER_RARITY_POOLS[vanilla_rarities[self.rarity]], self) + end + end + end + } + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.Center.Consumable + ------------------------------------------------------------------------------------------------- + + SMODS.Consumable = SMODS.Center:extend { + unlocked = true, + discovered = false, + consumeable = true, + pos = { x = 0, y = 0 }, + atlas = 'Tarot', + legendaries = {}, + cost = 3, + config = {}, + class_prefix = 'c', + required_params = { + 'set', + 'key', + }, + inject = function(self) + SMODS.Center.inject(self) + SMODS.insert_pool(G.P_CENTER_POOLS['Consumeables'], self) + self.type = SMODS.ConsumableTypes[self.set] + if self.hidden then + self.soul_set = self.soul_set or 'Spectral' + self.soul_rate = self.soul_rate or 0.003 + table.insert(self.legendaries, self) + end + if self.type and self.type.inject_card and type(self.type.inject_card) == 'function' then + self.type:inject_card(self) + end + end, + delete = function(self) + if self.type and self.type.delete_card and type(self.type.delete_card) == 'function' then + self.type:delete_card(self) + end + SMODS.remove_pool(G.P_CENTER_POOLS['Consumeables'], self.key) + SMODS.Consumable.super.delete(self) + end, + loc_vars = function(self, info_queue) + return {} + end + } + + SMODS.Tarot = SMODS.Consumable:extend { + set = 'Tarot', + } + SMODS.Planet = SMODS.Consumable:extend { + set = 'Planet', + atlas = 'Planet', + } + SMODS.Spectral = SMODS.Consumable:extend { + set = 'Spectral', + atlas = 'Spectral', + cost = 4, + } + + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.Center.Voucher + ------------------------------------------------------------------------------------------------- + + SMODS.Voucher = SMODS.Center:extend { + set = 'Voucher', + cost = 10, + atlas = 'Voucher', + discovered = false, + unlocked = true, + available = true, + pos = { x = 0, y = 0 }, + config = {}, + class_prefix = 'v', + required_params = { + 'key', + } + } + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.Center.Back + ------------------------------------------------------------------------------------------------- + + SMODS.Back = SMODS.Center:extend { + set = 'Back', + discovered = false, + unlocked = true, + atlas = 'centers', + pos = { x = 0, y = 0 }, + config = {}, + omit = false, + unlock_condition = {}, + stake = 1, + class_prefix = 'b', + required_params = { + 'key', + }, + register = function(self) + -- game expects a name, so ensure it's set + self.name = self.name or self.key + SMODS.Back.super.register(self) + end + } + + -- set the correct stake level for unlocks when injected (spares me from completely overwriting the unlock checks) + local function stake_mod(stake) + return { + inject = function(self) + self.unlock_condition.stake = SMODS.Stakes[stake].stake_level + SMODS.Back.inject(self) + end + } + end + SMODS.Back:take_ownership('zodiac', stake_mod('stake_red')) + SMODS.Back:take_ownership('painted', stake_mod('stake_green')) + SMODS.Back:take_ownership('anaglyph', stake_mod('stake_black')) + SMODS.Back:take_ownership('plasma', stake_mod('stake_blue')) + SMODS.Back:take_ownership('erratic', stake_mod('stake_orange')) + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Center.Booster + ------------------------------------------------------------------------------------------------- + + SMODS.OPENED_BOOSTER = nil + SMODS.Booster = SMODS.Center:extend { + required_params = { + 'key', + }, + class_prefix = 'p', + set = "Booster", + atlas = "Booster", + pos = {x = 0, y = 0}, + loc_txt = {}, + discovered = false, + weight = 1, + cost = 4, + config = {extra = 3, choose = 1}, + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.descriptions.Other, self.key, self.loc_txt) + SMODS.process_loc_text(G.localization.misc.dictionary, 'k_booster_group_'..self.key, self.loc_txt, 'group_name') + end, + loc_vars = function(self, info_queue, card) + return { vars = {card.ability.choose, card.ability.extra} } + end, + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + local target = { + type = 'other', + key = self.key, + nodes = desc_nodes, + vars = {} + } + local res = {} + if self.loc_vars and type(self.loc_vars) == 'function' then + res = self:loc_vars(info_queue, card) or {} + target.vars = res.vars or target.vars + target.key = res.key or target.key + target.scale = res.scale + target.text_colour = res.text_colour + end + if desc_nodes == full_UI_table.main and not full_UI_table.name then + full_UI_table.name = localize{type = 'name', set = 'Other', key = target.key, nodes = full_UI_table.name} + elseif desc_nodes ~= full_UI_table.main and not desc_nodes.name then + desc_nodes.name = localize{type = 'name_text', key = target.key, set = 'Other' } + end + localize(target) + desc_nodes.background_colour = res.background_colour + end, + --[[ + create_card = function(self, card) + -- Example + -- return {set = "Joker", area = G.pack_cards, skip_materialize = true, soulable = true, key_append = "buf"} + end, + --]] + update_pack = function(self, dt) + if G.buttons then self.buttons:remove(); G.buttons = nil end + if G.shop then G.shop.alignment.offset.y = G.ROOM.T.y+11 end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + G.CONTROLLER.interrupt.focus = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + if self.particles and type(self.particles) == "function" then self:particles() end + G.booster_pack = UIBox{ + definition = self:create_UIBox(), + config = {align="tmi", offset = {x=0,y=G.ROOM.T.y + 9}, major = G.hand, bond = 'Weak'} + } + G.booster_pack.alignment.offset.y = -2.2 + G.ROOM.jiggle = G.ROOM.jiggle + 3 + self:ease_background_colour() + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + if self.draw_hand == true then G.FUNCS.draw_from_deck_to_hand() end + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.5, + func = function() + G.CONTROLLER:recall_cardarea_focus('pack_cards') + return true + end})) + return true + end + })) + return true + end + })) + end + end, + ease_background_colour = function(self) + ease_colour(G.C.DYN_UI.MAIN, G.C.FILTER) + ease_background_colour{new_colour = G.C.FILTER, special_colour = G.C.BLACK, contrast = 2} + end, + create_UIBox = function(self) + local _size = SMODS.OPENED_BOOSTER.ability.extra + G.pack_cards = CardArea( + G.ROOM.T.x + 9 + G.hand.T.x, G.hand.T.y, + math.max(1,math.min(_size,5))*G.CARD_W*1.1, + 1.05*G.CARD_H, + {card_limit = _size, type = 'consumeable', highlight_limit = 1}) + + local t = {n=G.UIT.ROOT, config = {align = 'tm', r = 0.15, colour = G.C.CLEAR, padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cl", colour = G.C.CLEAR,r=0.15, padding = 0.1, minh = 2, shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", r=0.2, colour = G.C.CLEAR, shadow = true}, nodes={ + {n=G.UIT.O, config={object = G.pack_cards}},}}}}}}, + {n=G.UIT.R, config={align = "cm"}, nodes={}}, + {n=G.UIT.R, config={align = "tm"}, nodes={ + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={}}, + {n=G.UIT.C,config={align = "tm", padding = 0.05}, nodes={ + UIBox_dyn_container({ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 4}, nodes={ + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize(self.group_key or ('k_booster_group_'..self.key)), colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.7, maxw = 4, pop_in = 0.5})}}}}, + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('k_choose')..' '}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}}, + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME, ref_value = 'pack_choices'}}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}}}},}} + }),}}, + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={ + {n=G.UIT.R,config={minh =0.2}, nodes={}}, + {n=G.UIT.R,config={align = "tm",padding = 0.2, minh = 1.2, minw = 1.8, r=0.15,colour = G.C.GREY, one_press = true, button = 'skip_booster', hover = true,shadow = true, func = 'can_skip_booster'}, nodes = { + {n=G.UIT.T, config={text = localize('b_skip'), scale = 0.5, colour = G.C.WHITE, shadow = true, focus_args = {button = 'y', orientation = 'bm'}, func = 'set_button_pip'}}}}}}}}}}}} + return t + end, + take_ownership_by_kind = function(self, kind, obj, silent) + for k, v in ipairs(G.P_CENTER_POOLS.Booster) do + if v.set == self.set and v.kind and v.kind == kind then + self:take_ownership(v.key, obj, silent) + end + end + end + } + + local pack_loc_vars = function(self, info_queue, card) + local cfg = (card and card.ability) or self.config + return { + vars = { cfg.choose, cfg.extra }, + key = self.key:sub(1, -3), + } + end + SMODS.Booster:take_ownership_by_kind('Arcana', { + group_key = "k_arcana_pack", + draw_hand = true, + update_pack = SMODS.Booster.update_pack, + ease_background_colour = function(self) ease_background_colour_blind(G.STATES.TAROT_PACK) end, + create_UIBox = function(self) return create_UIBox_arcana_pack() end, + particles = function(self) + G.booster_pack_sparkles = Particles(1, 1, 0,0, { + timer = 0.015, + scale = 0.2, + initialize = true, + lifespan = 1, + speed = 1.1, + padding = -1, + attach = G.ROOM_ATTACH, + colours = {G.C.WHITE, lighten(G.C.PURPLE, 0.4), lighten(G.C.PURPLE, 0.2), lighten(G.C.GOLD, 0.2)}, + fill = true + }) + G.booster_pack_sparkles.fade_alpha = 1 + G.booster_pack_sparkles:fade(1, 0) + end, + create_card = function(self, card, i) + local _card + if G.GAME.used_vouchers.v_omen_globe and pseudorandom('omen_globe') > 0.8 then + _card = {set = "Spectral", area = G.pack_cards, skip_materialize = true, soulable = true, key_append = "ar2"} + else + _card = {set = "Tarot", area = G.pack_cards, skip_materialize = true, soulable = true, key_append = "ar1"} + end + return _card + end, + loc_vars = pack_loc_vars, + }) + + SMODS.Booster:take_ownership_by_kind('Celestial', { + group_key = "k_celestial_pack", + update_pack = SMODS.Booster.update_pack, + ease_background_colour = function(self) ease_background_colour_blind(G.STATES.PLANET_PACK) end, + create_UIBox = function(self) return create_UIBox_celestial_pack() end, + particles = function(self) + G.booster_pack_stars = Particles(1, 1, 0,0, { + timer = 0.07, + scale = 0.1, + initialize = true, + lifespan = 15, + speed = 0.1, + padding = -4, + attach = G.ROOM_ATTACH, + colours = {G.C.WHITE, HEX('a7d6e0'), HEX('fddca0')}, + fill = true + }) + G.booster_pack_meteors = Particles(1, 1, 0,0, { + timer = 2, + scale = 0.05, + lifespan = 1.5, + speed = 4, + attach = G.ROOM_ATTACH, + colours = {G.C.WHITE}, + fill = true + }) + end, + create_card = function(self, card, i) + local _card + if G.GAME.used_vouchers.v_telescope and i == 1 then + local _planet, _hand, _tally = nil, nil, 0 + for k, v in ipairs(G.handlist) do + if G.GAME.hands[v].visible and G.GAME.hands[v].played > _tally then + _hand = v + _tally = G.GAME.hands[v].played + end + end + if _hand then + for k, v in pairs(G.P_CENTER_POOLS.Planet) do + if v.config.hand_type == _hand then + _planet = v.key + end + end + end + _card = {set = "Planet", area = G.pack_cards, skip_materialize = true, soulable = true, key = _planet, key_append = "pl1"} + else + _card = {set = "Planet", area = G.pack_cards, skip_materialize = true, soulable = true, key_append = "pl1"} + end + return _card + end, + loc_vars = pack_loc_vars, + }) + + SMODS.Booster:take_ownership_by_kind('Spectral', { + group_key = "k_spectral_pack", + draw_hand = true, + update_pack = SMODS.Booster.update_pack, + ease_background_colour = function(self) ease_background_colour_blind(G.STATES.SPECTRAL_PACK) end, + create_UIBox = function(self) return create_UIBox_spectral_pack() end, + particles = function(self) + G.booster_pack_sparkles = Particles(1, 1, 0,0, { + timer = 0.015, + scale = 0.1, + initialize = true, + lifespan = 3, + speed = 0.2, + padding = -1, + attach = G.ROOM_ATTACH, + colours = {G.C.WHITE, lighten(G.C.GOLD, 0.2)}, + fill = true + }) + G.booster_pack_sparkles.fade_alpha = 1 + G.booster_pack_sparkles:fade(1, 0) + end, + create_card = function(self, card, i) + return {set = "Spectral", area = G.pack_cards, skip_materialize = true, soulable = true, key_append = "spe"} + end, + loc_vars = pack_loc_vars, + }) + + SMODS.Booster:take_ownership_by_kind('Standard', { + group_key = "k_standard_pack", + update_pack = SMODS.Booster.update_pack, + ease_background_colour = function(self) ease_background_colour_blind(G.STATES.STANDARD_PACK) end, + create_UIBox = function(self) return create_UIBox_standard_pack() end, + particles = function(self) + G.booster_pack_sparkles = Particles(1, 1, 0,0, { + timer = 0.015, + scale = 0.3, + initialize = true, + lifespan = 3, + speed = 0.2, + padding = -1, + attach = G.ROOM_ATTACH, + colours = {G.C.BLACK, G.C.RED}, + fill = true + }) + G.booster_pack_sparkles.fade_alpha = 1 + G.booster_pack_sparkles:fade(1, 0) + end, + create_card = function(self, card, i) + local _edition = poll_edition('standard_edition'..G.GAME.round_resets.ante, 2, true) + local _seal = SMODS.poll_seal({mod = 10}) + return {set = (pseudorandom(pseudoseed('stdset'..G.GAME.round_resets.ante)) > 0.6) and "Enhanced" or "Base", edition = _edition, seal = _seal, area = G.pack_cards, skip_materialize = true, soulable = true, key_append = "sta"} + end, + loc_vars = pack_loc_vars, + }) + + SMODS.Booster:take_ownership_by_kind('Buffoon', { + group_key = "k_buffoon_pack", + update_pack = SMODS.Booster.update_pack, + ease_background_colour = function(self) ease_background_colour_blind(G.STATES.BUFFOON_PACK) end, + create_UIBox = function(self) return create_UIBox_buffoon_pack() end, + create_card = function(self, card) + return {set = "Joker", area = G.pack_cards, skip_materialize = true, soulable = true, key_append = "buf"} + end, + loc_vars = pack_loc_vars, + }) + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.UndiscoveredSprite + ------------------------------------------------------------------------------------------------- + + SMODS.UndiscoveredSprites = {} + SMODS.UndiscoveredSprite = SMODS.GameObject:extend { + obj_buffer = {}, + obj_table = SMODS.UndiscoveredSprites, + set = 'Undiscovered Sprite', + -- this is more consistent and allows for extension + process_loc_text = function() end, + inject = function() end, + prefix_config = { key = false }, + required_params = { + 'key', + 'atlas', + 'pos', + } + } + SMODS.UndiscoveredSprite { key = 'Joker', atlas = 'Joker', pos = G.j_undiscovered.pos } + SMODS.UndiscoveredSprite { key = 'Edition', atlas = 'Joker', pos = G.j_undiscovered.pos } + SMODS.UndiscoveredSprite { key = 'Tarot', atlas = 'Tarot', pos = G.t_undiscovered.pos } + SMODS.UndiscoveredSprite { key = 'Planet', atlas = 'Tarot', pos = G.p_undiscovered.pos } + SMODS.UndiscoveredSprite { key = 'Spectral', atlas = 'Tarot', pos = G.s_undiscovered.pos } + SMODS.UndiscoveredSprite { key = 'Voucher', atlas = 'Voucher', pos = G.v_undiscovered.pos } + SMODS.UndiscoveredSprite { key = 'Booster', atlas = 'Booster', pos = G.booster_undiscovered.pos } + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Blind + ------------------------------------------------------------------------------------------------- + + SMODS.Blinds = {} + SMODS.Blind = SMODS.GameObject:extend { + obj_table = SMODS.Blinds, + obj_buffer = {}, + class_prefix = 'bl', + debuff = {}, + vars = {}, + dollars = 5, + mult = 2, + atlas = 'blind_chips', + discovered = false, + pos = { x = 0, y = 0 }, + required_params = { + 'key', + }, + set = 'Blind', + get_obj = function(self, key) return G.P_BLINDS[key] end, + register = function(self) + self.name = self.name or self.key + SMODS.Blind.super.register(self) + end, + inject = function(self, i) + -- no pools to query length of, so we assign order manually + if not self.taken_ownership then + self.order = 30 + i + end + G.P_BLINDS[self.key] = self + end + } + SMODS.Blind:take_ownership('eye', { + set_blind = function(self, reset, silent) + if not reset then + G.GAME.blind.hands = {} + for _, v in ipairs(G.handlist) do + G.GAME.blind.hands[v] = false + end + end + end + }) + SMODS.Blind:take_ownership('wheel', { + loc_vars = function(self) + return { vars = { G.GAME.probabilities.normal } } + end, + collection_loc_vars = function(self) + return { vars = { '1' }} + end, + process_loc_text = function(self) + local text = G.localization.descriptions.Blind[self.key].text[1] + if string.sub(text, 1, 3) ~= '#1#' then + G.localization.descriptions.Blind[self.key].text[1] = "#1#"..text + end + SMODS.Blind.process_loc_text(self) + end, + get_loc_debuff_text = function() return G.GAME.blind.loc_debuff_text end, + }) + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Seal + ------------------------------------------------------------------------------------------------- + + SMODS.Seals = {} + SMODS.Seal = SMODS.GameObject:extend { + obj_table = SMODS.Seals, + obj_buffer = {}, + rng_buffer = { 'Purple', 'Gold', 'Blue', 'Red' }, + badge_to_key = {}, + set = 'Seal', + atlas = 'centers', + pos = { x = 0, y = 0 }, + discovered = false, + badge_colour = HEX('FFFFFF'), + required_params = { + 'key', + 'pos', + }, + inject = function(self) + G.P_SEALS[self.key] = self + G.shared_seals[self.key] = Sprite(0, 0, G.CARD_W, G.CARD_H, + G.ASSET_ATLAS[self.atlas] or G.ASSET_ATLAS['centers'], self.pos) + self.badge_to_key[self.key:lower() .. '_seal'] = self.key + SMODS.insert_pool(G.P_CENTER_POOLS[self.set], self) + self.rng_buffer[#self.rng_buffer + 1] = self.key + end, + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.descriptions.Other, self.key:lower() .. '_seal', self.loc_txt) + SMODS.process_loc_text(G.localization.misc.labels, self.key:lower() .. '_seal', self.loc_txt, 'label') + end, + get_obj = function(self, key) return G.P_SEALS[key] end + } + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Suit + ------------------------------------------------------------------------------------------------- + + SMODS.inject_p_card = function(suit, rank) + G.P_CARDS[suit.card_key .. '_' .. rank.card_key] = { + name = rank.key .. ' of ' .. suit.key, + value = rank.key, + suit = suit.key, + pos = { x = rank.pos.x, y = rank.suit_map[suit.key] or suit.pos.y }, + lc_atlas = rank.suit_map[suit.key] and rank.lc_atlas or suit.lc_atlas, + hc_atlas = rank.suit_map[suit.key] and rank.hc_atlas or suit.hc_atlas, + } + end + SMODS.remove_p_card = function(suit, rank) + G.P_CARDS[suit.card_key .. '_' .. rank.card_key] = nil + end + + SMODS.Suits = {} + SMODS.Suit = SMODS.GameObject:extend { + obj_table = SMODS.Suits, + obj_buffer = {}, + used_card_keys = {}, + set = 'Suit', + required_params = { + 'key', + 'card_key', + 'pos', + 'ui_pos', + }, + hc_atlas = 'cards_2', + lc_atlas = 'cards_1', + hc_ui_atlas = 'ui_2', + lc_ui_atlas = 'ui_1', + hc_colour = HEX '000000', + lc_colour = HEX '000000', + max_nominal = { + value = 0, + }, + register = function(self) + -- 0.9.8 compat + self.name = self.name or self.key + if self.used_card_keys[self.card_key] then + sendWarnMessage(('Tried to use duplicate card key %s, aborting registration'):format(self.card_key), self.set) + return + end + self.used_card_keys[self.card_key] = true + self.max_nominal.value = self.max_nominal.value + 0.01 + self.suit_nominal = self.max_nominal.value + SMODS.Suit.super.register(self) + end, + inject = function(self) + for _, rank in pairs(SMODS.Ranks) do + SMODS.inject_p_card(self, rank) + end + end, + delete = function(self) + local i + for j, v in ipairs(self.obj_buffer) do + if v == self.key then i = j end + end + for _, rank in pairs(SMODS.Ranks) do + SMODS.remove_p_card(self, rank) + end + self.used_card_keys[self.card_key] = nil + table.remove(self.obj_buffer, i) + end, + process_loc_text = function(self) + -- empty loc_txt indicates there are existing values that shouldn't be changed + SMODS.process_loc_text(G.localization.misc.suits_plural, self.key, self.loc_txt, 'plural') + SMODS.process_loc_text(G.localization.misc.suits_singular, self.key, self.loc_txt, 'singular') + if not self.keep_base_colours then + if type(self.lc_colour) == 'string' then self.lc_colour = HEX(self.lc_colour) end + if type(self.hc_colour) == 'string' then self.hc_colour = HEX(self.hc_colour) end + G.C.SO_1[self.key] = self.lc_colour + G.C.SO_2[self.key] = self.hc_colour + G.C.SUITS[self.key] = G.C["SO_" .. (G.SETTINGS.colourblind_option and 2 or 1)][self.key] + end + end, + } + SMODS.Suit { + key = 'Diamonds', + card_key = 'D', + pos = { y = 2 }, + ui_pos = { x = 1, y = 1 }, + keep_base_colours = true, + } + SMODS.Suit { + key = 'Clubs', + card_key = 'C', + pos = { y = 1 }, + ui_pos = { x = 2, y = 1 }, + keep_base_colours = true, + } + SMODS.Suit { + key = 'Hearts', + card_key = 'H', + pos = { y = 0 }, + ui_pos = { x = 0, y = 1 }, + keep_base_colours = true, + } + SMODS.Suit { + key = 'Spades', + card_key = 'S', + pos = { y = 3 }, + ui_pos = { x = 3, y = 1 }, + keep_base_colours = true, + } + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Rank + ------------------------------------------------------------------------------------------------- + + SMODS.Ranks = {} + SMODS.Rank = SMODS.GameObject:extend { + obj_table = SMODS.Ranks, + obj_buffer = {}, + used_card_keys = {}, + set = 'Rank', + required_params = { + 'key', + 'pos', + 'nominal', + }, + hc_atlas = 'cards_2', + lc_atlas = 'cards_1', + strength_effect = { + fixed = 1, + random = false, + ignore = false + }, + next = {}, + straight_edge = false, + -- TODO we need a better system for what this is doing. + -- We should allow setting a playing card's atlas and position to any values, + -- and we should also ensure that it's easy to create an atlas with a standard + -- arrangement: x and y set according to rank and suit. + + -- Currently suit_map does the following: + -- suit_map forces a playing card's atlas to be rank.hc_atlas/lc_atlas, + -- and not the atlas defined on the suit of the playing card; + -- additionally pos.y is set according to the corresponding value in the + -- suit_map + suit_map = { + Hearts = 0, + Clubs = 1, + Diamonds = 2, + Spades = 3, + }, + max_id = { + value = 1, + }, + register = function(self) + if self.used_card_keys[self.card_key] then + sendWarnMessage(('Tried to use duplicate card key %s, aborting registration'):format(self.card_key), self.set) + return + end + self.used_card_keys[self.card_key] = true + self.max_id.value = self.max_id.value + 1 + self.id = self.max_id.value + self.shorthand = self.shorthand or self.key + self.sort_nominal = self.nominal + (self.face_nominal or 0) + if self:check_dependencies() and not self.obj_table[self.key] then + self.obj_table[self.key] = self + local j + -- keep buffer sorted in ascending nominal order + for i = 1, #self.obj_buffer - 1 do + if self.obj_table[self.obj_buffer[i]].sort_nominal > self.sort_nominal then + j = i + break + end + end + if j then + table.insert(self.obj_buffer, j, self.key) + else + table.insert(self.obj_buffer, self.key) + end + end + end, + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.misc.ranks, self.key, self.loc_txt, 'name') + end, + inject = function(self) + for _, suit in pairs(SMODS.Suits) do + SMODS.inject_p_card(suit, self) + end + end, + delete = function(self) + local i + for j, v in ipairs(self.obj_buffer) do + if v == self.key then i = j end + end + for _, suit in pairs(SMODS.Suits) do + SMODS.remove_p_card(suit, self) + end + self.used_card_keys[self.card_key] = nil + table.remove(self.obj_buffer, i) + end + } + for _, v in ipairs({ 2, 3, 4, 5, 6, 7, 8, 9 }) do + SMODS.Rank { + key = v .. '', + card_key = v .. '', + pos = { x = v - 2 }, + nominal = v, + next = { (v + 1) .. '' }, + } + end + SMODS.Rank { + key = '10', + card_key = 'T', + pos = { x = 8 }, + nominal = 10, + next = { 'Jack' }, + } + SMODS.Rank { + key = 'Jack', + card_key = 'J', + pos = { x = 9 }, + nominal = 10, + face_nominal = 0.1, + face = true, + shorthand = 'J', + next = { 'Queen' }, + } + SMODS.Rank { + key = 'Queen', + card_key = 'Q', + pos = { x = 10 }, + nominal = 10, + face_nominal = 0.2, + face = true, + shorthand = 'Q', + next = { 'King' }, + } + SMODS.Rank { + key = 'King', + card_key = 'K', + pos = { x = 11 }, + nominal = 10, + face_nominal = 0.3, + face = true, + shorthand = 'K', + next = { 'Ace' }, + } + SMODS.Rank { + key = 'Ace', + card_key = 'A', + pos = { x = 12 }, + nominal = 11, + face_nominal = 0.4, + shorthand = 'A', + straight_edge = true, + next = { '2' }, + } + -- make consumable effects compatible with added suits + -- TODO put this in utils.lua + local function juice_flip(used_tarot) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.4, + func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true + end + })) + for i = 1, #G.hand.cards do + local percent = 1.15 - (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.15, + func = function() + G.hand.cards[i]:flip(); play_sound('card1', percent); G.hand.cards[i]:juice_up(0.3, 0.3); return true + end + })) + end + end + SMODS.Consumable:take_ownership('strength', { + use = function(self, card, area, copier) + local used_tarot = copier or card + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.4, + func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true + end + })) + for i = 1, #G.hand.highlighted do + local percent = 1.15 - (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.15, + func = function() + G.hand.highlighted[i]:flip(); play_sound('card1', percent); G.hand.highlighted[i]:juice_up(0.3, + 0.3); return true + end + })) + end + delay(0.2) + for i = 1, #G.hand.highlighted do + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + local _card = G.hand.highlighted[i] + local rank_data = SMODS.Ranks[_card.base.value] + local behavior = rank_data.strength_effect or { fixed = 1, ignore = false, random = false } + local new_rank + if behavior.ignore or not next(rank_data.next) then + return true + elseif behavior.random then + -- TODO doesn't respect in_pool + new_rank = pseudorandom_element(rank_data.next, pseudoseed('strength')) + else + local ii = (behavior.fixed and rank_data.next[behavior.fixed]) and behavior.fixed or 1 + new_rank = rank_data.next[ii] + end + assert(SMODS.change_base(_card, nil, new_rank)) + return true + end + })) + end + for i = 1, #G.hand.highlighted do + local percent = 0.85 + (i - 0.999) / (#G.hand.highlighted - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.15, + func = function() + G.hand.highlighted[i]:flip(); play_sound('tarot2', percent, 0.6); G.hand.highlighted[i] + :juice_up( + 0.3, 0.3); return true + end + })) + end + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.2, + func = function() + G.hand:unhighlight_all(); return true + end + })) + delay(0.5) + end, + }) + SMODS.Consumable:take_ownership('sigil', { + use = function(self, card, area, copier) + local used_tarot = copier or card + juice_flip(used_tarot) + local _suit = pseudorandom_element(SMODS.Suits, pseudoseed('sigil')) + for i = 1, #G.hand.cards do + G.E_MANAGER:add_event(Event({ + func = function() + local _card = G.hand.cards[i] + assert(SMODS.change_base(_card, _suit.key)) + return true + end + })) + end + for i = 1, #G.hand.cards do + local percent = 0.85 + (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.15, + func = function() + G.hand.cards[i]:flip(); play_sound('tarot2', percent, 0.6); G.hand.cards[i]:juice_up(0.3, 0.3); return true + end + })) + end + delay(0.5) + end, + }) + SMODS.Consumable:take_ownership('ouija', { + use = function(self, card, area, copier) + local used_tarot = copier or card + juice_flip(used_tarot) + local _rank = pseudorandom_element(SMODS.Ranks, pseudoseed('ouija')) + for i = 1, #G.hand.cards do + G.E_MANAGER:add_event(Event({ + func = function() + local _card = G.hand.cards[i] + assert(SMODS.change_base(_card, nil, _rank.key)) + return true + end + })) + end + G.hand:change_size(-1) + for i = 1, #G.hand.cards do + local percent = 0.85 + (i - 0.999) / (#G.hand.cards - 0.998) * 0.3 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.15, + func = function() + G.hand.cards[i]:flip(); play_sound('tarot2', percent, 0.6); G.hand.cards[i]:juice_up(0.3, 0.3); return true + end + })) + end + delay(0.5) + end, + }) + local function random_destroy(used_tarot) + local destroyed_cards = {} + destroyed_cards[#destroyed_cards + 1] = pseudorandom_element(G.hand.cards, pseudoseed('random_destroy')) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.4, + func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + for i = #destroyed_cards, 1, -1 do + local card = destroyed_cards[i] + if card.ability.name == 'Glass Card' then + card:shatter() + else + card:start_dissolve(nil, i ~= #destroyed_cards) + end + end + return true + end + })) + return destroyed_cards + end + SMODS.Consumable:take_ownership('grim', { + use = function(self, card, area, copier) + local used_tarot = copier or card + local destroyed_cards = random_destroy(used_tarot) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {} + for i = 1, card.ability.extra do + cards[i] = true + -- TODO preserve suit vanilla RNG + local _suit, _rank = + pseudorandom_element(SMODS.Suits, pseudoseed('grim_create')).card_key, 'A' + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if v.key ~= 'm_stone' and not v.overrides_base_rank then + cen_pool[#cen_pool + 1] = v + end + end + create_playing_card({ + front = G.P_CARDS[_suit .. '_' .. _rank], + center = pseudorandom_element(cen_pool, pseudoseed('spe_card')) + }, G.hand, nil, i ~= 1, { G.C.SECONDARY_SET.Spectral }) + end + playing_card_joker_effects(cards) + return true + end + })) + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({ remove_playing_cards = true, removed = destroyed_cards }) + end + end, + }) + SMODS.Consumable:take_ownership('familiar', { + use = function(self, card, area, copier) + local used_tarot = copier or card + local destroyed_cards = random_destroy(used_tarot) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {} + for i = 1, card.ability.extra do + cards[i] = true + -- TODO preserve suit vanilla RNG + local faces = {} + for _, v in ipairs(SMODS.Rank.obj_buffer) do + local r = SMODS.Ranks[v] + if r.face then table.insert(faces, r) end + end + local _suit, _rank = + pseudorandom_element(SMODS.Suits, pseudoseed('familiar_create')).card_key, + pseudorandom_element(faces, pseudoseed('familiar_create')).card_key + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if v.key ~= 'm_stone' and not v.overrides_base_rank then + cen_pool[#cen_pool + 1] = v + end + end + create_playing_card({ + front = G.P_CARDS[_suit .. '_' .. _rank], + center = pseudorandom_element(cen_pool, pseudoseed('spe_card')) + }, G.hand, nil, i ~= 1, { G.C.SECONDARY_SET.Spectral }) + end + playing_card_joker_effects(cards) + return true + end + })) + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({ remove_playing_cards = true, removed = destroyed_cards }) + end + end, + }) + SMODS.Consumable:take_ownership('incantation', { + use = function(self, card, area, copier) + local used_tarot = copier or card + local destroyed_cards = random_destroy(used_tarot) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {} + for i = 1, card.ability.extra do + cards[i] = true + -- TODO preserve suit vanilla RNG + local numbers = {} + for _, v in ipairs(SMODS.Rank.obj_buffer) do + local r = SMODS.Ranks[v] + if v ~= 'Ace' and not r.face then table.insert(numbers, r) end + end + local _suit, _rank = + pseudorandom_element(SMODS.Suits, pseudoseed('incantation_create')).card_key, + pseudorandom_element(numbers, pseudoseed('incantation_create')).card_key + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if v.key ~= 'm_stone' and not v.overrides_base_rank then + cen_pool[#cen_pool + 1] = v + end + end + create_playing_card({ + front = G.P_CARDS[_suit .. '_' .. _rank], + center = pseudorandom_element(cen_pool, pseudoseed('spe_card')) + }, G.hand, nil, i ~= 1, { G.C.SECONDARY_SET.Spectral }) + end + playing_card_joker_effects(cards) + return true + end + })) + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({ remove_playing_cards = true, removed = destroyed_cards }) + end + end, + }) + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.DeckSkin + ------------------------------------------------------------------------------------------------- + + local deck_skin_count_by_suit = {} + SMODS.DeckSkins = {} + SMODS.DeckSkin =SMODS.GameObject:extend { + obj_table = SMODS.DeckSkins, + obj_buffer = {}, + required_params = { + 'key', + 'suit', + 'ranks', + 'lc_atlas', + }, + posStyle = 'deck', + set = 'DeckSkin', + process_loc_text = function(self) + if G.localization.misc.collabs[self.suit] == nil then + G.localization.misc.collabs[self.suit] = {["1"] = 'Default'} + end + + if self.loc_txt and self.loc_txt[G.SETTINGS.language] then + G.localization.misc.collabs[self.suit][self.suit_index .. ''] = self.loc_txt[G.SETTINGS.language] + elseif G.localization.misc.collabs[self.suit][self.suit_index .. ''] == nil then + G.localization.misc.collabs[self.suit][self.suit_index .. ''] = self.key + end + end, + register = function (self) + if self.registered then + sendWarnMessage(('Detected duplicate register call on DeckSkin %s'):format(self.key), self.set) + return + end + if self:check_dependencies() then + self.hc_atlas = self.hc_atlas or self.lc_atlas + + if not (self.posStyle == 'collab' or self.posStyle == 'suit' or self.posStyle == 'deck') then + sendWarnMessage(('%s is not a valid posStyle on DeckSkin %s. Supported posStyle values are \'collab\', \'suit\' and \'deck\''):format(self.posStyle, self.key), self.set) + end + + self.obj_table[self.key] = self + + if deck_skin_count_by_suit[self.suit] then + self.suit_index = deck_skin_count_by_suit[self.suit] + 1 + else + --start at 2 for default + self.suit_index = 2 + end + deck_skin_count_by_suit[self.suit] = self.suit_index + + self.obj_buffer[#self.obj_buffer + 1] = self.key + self.registered = true + end + end, + inject = function (self) + if G.COLLABS.options[self.suit] == nil then + G.COLLABS.options[self.suit] = {'default'} + end + + local options = G.COLLABS.options[self.suit] + options[#options + 1] = self.key + end + } + + for suitName, options in pairs(G.COLLABS.options) do + --start at 2 to skip default + for i = 2, #options do + SMODS.DeckSkin{ + key = options[i], + suit = suitName, + ranks = {'Jack', 'Queen', 'King'}, + lc_atlas = options[i] .. '_1', + hc_atlas = options[i] .. '_2', + posStyle = 'collab' + } + end + end + + --Clear 'Friends of Jimbo' skins so they can be handled via the same pipeline + G.COLLABS.options = {} + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.PokerHand + ------------------------------------------------------------------------------------------------- + + SMODS.PokerHandParts = {} + SMODS.PokerHandPart = SMODS.GameObject:extend { + obj_table = SMODS.PokerHandParts, + obj_buffer = {}, + required_params = { + 'key', + 'func', + }, + inject_class = function() end, + } + local handlist = G.handlist + G.handlist = {} + SMODS.PokerHands = {} + SMODS.PokerHand = SMODS.GameObject:extend { + obj_table = SMODS.PokerHands, + obj_buffer = G.handlist, + required_params = { + 'key', + 'mult', + 'chips', + 'l_mult', + 'l_chips', + 'example', + 'evaluate' + }, + visible = true, + played = 0, + played_this_round = 0, + level = 1, + set = 'PokerHand', + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.misc.poker_hands, self.key, self.loc_txt, 'name') + SMODS.process_loc_text(G.localization.misc.poker_hand_descriptions, self.key, self.loc_txt, 'description') + end, + register = function(self) + if self:check_dependencies() and not self.obj_table[self.key] then + self.s_mult = self.mult + self.s_chips = self.chips + self.visible = self.visible + self.level = self.level + self.played = self.played + self.played_this_round = self.played_this_round + self.obj_table[self.key] = self + self.obj_buffer[#self.obj_buffer + 1] = self.key + end + end, + inject = function(self) end, + post_inject_class = function(self) + table.sort( + self.obj_buffer, + function(a, b) + local x, y = self.obj_table[a], self.obj_table[b] + local x_above = self.obj_table[x.above_hand or {}] + local y_above = self.obj_table[y.above_hand or {}] + local function eval(h) return h.mult*h.chips + (h.order_offset or 0) end + return (x_above and (1e-6*eval(x) + eval(x_above)) or eval(x)) > (y_above and (1e-6*eval(y) + eval(y_above)) or eval(y)) + end + ) + for i, v in ipairs(self.obj_buffer) do self.obj_table[v].order = i end + end + } + + SMODS.PokerHandPart { + key = '_highest', + func = function(hand) return get_highest(hand) end + } + SMODS.PokerHandPart { + key = '_straight', + func = function(hand) return get_straight(hand) end + } + SMODS.PokerHandPart { + key = '_flush', + func = function(hand) return get_flush(hand) end, + } + -- all sets of 2 or more cards of same rank + SMODS.PokerHandPart { + key = '_all_pairs', + func = function(hand) + local _2 = get_X_same(2, hand, true) + if not next(_2) then return {} end + return {SMODS.merge_lists(_2)} + end + } + for i = 2, 5 do + SMODS.PokerHandPart { + key = '_'..i, + func = function(hand) return get_X_same(i, hand, true) end + } + end + + local hands = G:init_game_object().hands + local eval_functions = { + ['Flush Five'] = function(parts) + if not next(parts._5) or not next(parts._flush) then return {} end + return { SMODS.merge_lists(parts._5, parts._flush) } + end, + ['Flush House'] = function(parts) + if #parts._3 < 1 or #parts._2 < 2 or not next(parts._flush) then return {} end + return { SMODS.merge_lists(parts._all_pairs, parts._flush) } + end, + ['Five of a Kind'] = function(parts) return parts._5 end, + ['Straight Flush'] = function(parts) + if not next(parts._straight) or not next(parts._flush) then return end + return { SMODS.merge_lists(parts._straight, parts._flush) } + end, + ['Four of a Kind'] = function(parts) return parts._4 end, + ['Full House'] = function(parts) + if #parts._3 < 1 or #parts._2 < 2 then return {} end + return parts._all_pairs + end, + ['Flush'] = function(parts) return parts._flush end, + ['Straight'] = function(parts) return parts._straight end, + ['Three of a Kind'] = function(parts) return parts._3 end, + ['Two Pair'] = function(parts) + if #parts._2 < 2 then return {} end + return parts._all_pairs + end, + ['Pair'] = function(parts) return parts._2 end, + ['High Card'] = function(parts) return parts._highest end, + } + for _, v in ipairs(handlist) do + local hand = copy_table(hands[v]) + hand.key = v + hand.evaluate = eval_functions[v] + SMODS.PokerHand(hand) + end + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Challenge + ------------------------------------------------------------------------------------------------- + + SMODS.Challenges = {} + SMODS.Challenge = SMODS.GameObject:extend { + obj_table = SMODS.Challenges, + obj_buffer = {}, + get_obj = function(self, key) + for _, v in ipairs(G.CHALLENGES) do + if v.id == key then return v end + end + end, + set = "Challenge", + required_params = { + 'key', + }, + deck = { type = "Challenge Deck" }, + rules = { custom = {}, modifiers = {} }, + jokers = {}, + consumeables = {}, + vouchers = {}, + restrictions = { banned_cards = {}, banned_tags = {}, banned_other = {} }, + unlocked = function(self) return true end, + class_prefix = 'c', + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.misc.challenge_names, self.key, self.loc_txt, 'name') + end, + register = function(self) + if self.registered then + sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set) + return + end + self.id = self.key + -- only needs to be called once + SMODS.insert_pool(G.CHALLENGES, self) + SMODS.Challenge.super.register(self) + end, + inject = function(self) end, + } + for k, v in ipairs { + 'omelette_1', + 'city_1', + 'rich_1', + 'knife_1', + 'xray_1', + 'mad_world_1', + 'luxury_1', + 'non_perishable_1', + 'medusa_1', + 'double_nothing_1', + 'typecast_1', + 'inflation_1', + 'bram_poker_1', + 'fragile_1', + 'monolith_1', + 'blast_off_1', + 'five_card_1', + 'golden_needle_1', + 'cruelty_1', + 'jokerless_1', + } do + SMODS.Challenge:take_ownership(v, { + unlocked = function(self) + return G.PROFILES[G.SETTINGS.profile].challenges_unlocked and + (G.PROFILES[G.SETTINGS.profile].challenges_unlocked >= k) + end, + }) + end + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Tag + ------------------------------------------------------------------------------------------------- + + SMODS.Tags = {} + SMODS.Tag = SMODS.GameObject:extend { + obj_table = SMODS.Tags, + obj_buffer = {}, + required_params = { + 'key', + }, + discovered = false, + min_ante = nil, + atlas = 'tags', + class_prefix = 'tag', + set = 'Tag', + pos = { x = 0, y = 0 }, + config = {}, + get_obj = function(self, key) return G.P_TAGS[key] end, + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.descriptions.Tag, self.key, self.loc_txt) + end, + inject = function(self) + G.P_TAGS[self.key] = self + SMODS.insert_pool(G.P_CENTER_POOLS[self.set], self) + end, + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + local target = { + type = 'descriptions', + key = self.key, + set = self.set, + nodes = desc_nodes, + vars = specific_vars + } + local res = {} + if self.loc_vars and type(self.loc_vars) == 'function' then + -- card is a dead arg here + res = self:loc_vars(info_queue, card) or {} + target.vars = res.vars or target.vars + target.key = res.key or target.key + target.set = res.set or target.set + target.scale = res.scale + target.text_colour = res.text_colour + end + if desc_nodes == full_UI_table.main and not full_UI_table.name then + full_UI_table.name = localize { type = 'name', set = target.set, key = target.key, nodes = full_UI_table.name } + elseif desc_nodes ~= full_UI_table.main and not desc_nodes.name then + desc_nodes.name = localize{type = 'name_text', key = target.key, set = target.set } + end + if res.main_start then + desc_nodes[#desc_nodes + 1] = res.main_start + end + localize(target) + if res.main_end then + desc_nodes[#desc_nodes + 1] = res.main_end + end + desc_nodes.background_colour = res.background_colour + end + } + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Sticker + ------------------------------------------------------------------------------------------------- + + SMODS.Stickers = {} + SMODS.Sticker = SMODS.GameObject:extend { + obj_table = SMODS.Stickers, + obj_buffer = {}, + set = 'Sticker', + required_params = { + 'key', + }, + rate = 0.3, + atlas = 'stickers', + pos = { x = 0, y = 0 }, + badge_colour = HEX 'FFFFFF', + default_compat = true, + compat_exceptions = {}, + sets = { Joker = true }, + needs_enable_flag = true, + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.descriptions.Other, self.key, self.loc_txt) + SMODS.process_loc_text(G.localization.misc.labels, self.key, self.loc_txt, 'label') + end, + register = function(self) + if self.registered then + sendWarnMessage(('Detected duplicate register call on object %s'):format(self.key), self.set) + return + end + SMODS.Sticker.super.register(self) + self.order = #self.obj_buffer + end, + inject = function(self) + self.sticker_sprite = Sprite(0, 0, G.CARD_W, G.CARD_H, G.ASSET_ATLAS[self.atlas], self.pos) + G.shared_stickers[self.key] = self.sticker_sprite + end, + -- relocating sticker checks to here, so if the sticker has different checks than default + -- they can be handled without hooking/injecting into create_card + -- or handling it in apply + -- TODO: rename + should_apply = function(self, card, center, area, bypass_roll) + if + ( not self.sets or self.sets[center.set or {}]) and + ( + center[self.key..'_compat'] or -- explicit marker + (self.default_compat and not self.compat_exceptions[center.key]) or -- default yes with no exception + (not self.default_compat and self.compat_exceptions[center.key]) -- default no with exception + ) and + (not self.needs_enable_flag or G.GAME.modifiers['enable_'..self.key]) + then + self.last_roll = pseudorandom((area == G.pack_cards and 'packssj' or 'shopssj')..self.key..G.GAME.round_resets.ante) + return (bypass_roll ~= nil) and bypass_roll or self.last_roll > (1-self.rate) + end + end, + apply = function(self, card, val) + card.ability[self.key] = val + end + } + + -- Create base game stickers + -- eternal and perishable follow shared checks for sticker application, therefore omitted + SMODS.Sticker{ + key = "eternal", + badge_colour = HEX 'c75985', + prefix_config = {key = false}, + pos = { x = 0, y = 0 }, + hide_badge = true, + order = 1, + should_apply = false, + inject = function(self) + SMODS.Sticker.inject(self) + G.shared_sticker_eternal = self.sticker_sprite + end + } + + SMODS.Sticker{ + key = "perishable", + badge_colour = HEX '4f5da1', + prefix_config = {key = false}, + pos = { x = 0, y = 2 }, + hide_badge = true, + order = 2, + should_apply = false, + apply = function(self, card, val) + card.ability[self.key] = val + if card.ability[self.key] then card.ability.perish_tally = G.GAME.perishable_rounds end + end, + loc_vars = function(self, info_queue, card) + return {vars = {card.ability.perishable_rounds or 5, card.ability.perish_tally or G.GAME.perishable_rounds}} + end, + inject = function(self) + SMODS.Sticker.inject(self) + G.shared_sticker_perishable = self.sticker_sprite + end + } + + SMODS.Sticker{ + key = "rental", + badge_colour = HEX 'b18f43', + prefix_config = {key = false}, + pos = { x = 1, y = 2 }, + hide_badge = true, + order = 3, + should_apply = false, + apply = function(self, card, val) + card.ability[self.key] = val + if card.ability[self.key] then card:set_cost() end + end, + loc_vars = function(self, info_queue, card) + return {vars = {G.GAME.rental_rate or 1}} + end, + inject = function(self) + SMODS.Sticker.inject(self) + G.shared_sticker_rental = self.sticker_sprite + end + } + + SMODS.Sticker{ + key = "pinned", + badge_colour = HEX 'fda200', + prefix_config = {key = false}, + pos = { x = 10, y = 10 }, -- Base game has no art, and I haven't made any yet to represent Pinned with + rate = 0, + should_apply = false, + order = 4, + apply = function(self, card, val) + card[self.key] = val + end + } + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Enhancement + ------------------------------------------------------------------------------------------------- + + SMODS.Enhancement = SMODS.Center:extend { + set = 'Enhanced', + class_prefix = 'm', + atlas = 'centers', + pos = { x = 0, y = 0 }, + required_params = { + 'key', + -- table with keys `name` and `text` + }, + -- other fields: + -- replace_base_card + -- if true, don't draw base card sprite and don't give base card's chips + -- no_suit + -- if true, enhanced card has no suit + -- no_rank + -- if true, enhanced card has no rank + -- overrides_base_rank + -- Set to true if your enhancement overrides the base card's rank. + -- This prevents rank generators like Familiar creating cards + -- whose rank is overridden. + -- any_suit + -- if true, enhanced card is any suit + -- always_scores + -- if true, card always scores + -- loc_subtract_extra_chips + -- During tooltip generation, number of chips to subtract from displayed extra chips. + -- Use if enhancement already displays its own chips. + -- Future work: use ranks() and suits() for better control + register = function(self) + self.config = self.config or {} + assert(not (self.no_suit and self.any_suit)) + if self.no_rank then self.overrides_base_rank = true end + SMODS.Enhancement.super.register(self) + end, + -- Produces the description of the whole playing card + -- (including chips from the rank of the card and permanent bonus chips). + -- You will probably want to override this if your enhancement interacts with + -- those parts of the base card. + generate_ui = function(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + if specific_vars and specific_vars.nominal_chips and not self.replace_base_card then + localize { type = 'other', key = 'card_chips', nodes = desc_nodes, vars = { specific_vars.nominal_chips } } + end + SMODS.Enhancement.super.generate_ui(self, info_queue, card, desc_nodes, specific_vars, full_UI_table) + if specific_vars and specific_vars.bonus_chips then + local remaining_bonus_chips = specific_vars.bonus_chips - (self.config.bonus or 0) + if remaining_bonus_chips > 0 then + localize { type = 'other', key = 'card_extra_chips', nodes = desc_nodes, vars = { specific_vars.bonus_chips - (self.config.bonus or 0) } } + end + end + end, + -- other methods: + -- calculate(self, context, effect) + } + -- Note: `name`, `effect`, and `label` all serve the same purpose as + -- the name of the enhancement. In theory, `effect` serves to allow reusing + -- similar effects (ex. the Sinful jokers). But Balatro just uses them all + -- indiscriminately for enhancements. + -- `name` and `effect` are technically different for Bonus and Mult + -- cards but this never matters in practice; also `label` is a red herring, + -- I can't even find a single use of `label`. + + -- It would be nice if the relevant functions for modding each class of object + -- would be documented. + -- For example, Card:set_ability sets the card's enhancement, which is not immediately + -- obvious. + + -- local stone_card = SMODS.Enhancement:take_ownership('m_stone', { + -- replace_base_card = true, + -- no_suit = true, + -- no_rank = true, + -- always_scores = true, + -- loc_txt = { + -- name = "Stone Card", + -- text = { + -- "{C:chips}+#1#{} Chips", + -- "no rank or suit" + -- } + -- }, + -- loc_vars = function(self) + -- return { + -- vars = { self.config.bonus } + -- } + -- end + -- }) + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Shader + ------------------------------------------------------------------------------------------------- + + SMODS.Shaders = {} + SMODS.Shader = SMODS.GameObject:extend { + obj_table = SMODS.Shaders, + obj_buffer = {}, + required_params = { + 'key', + 'path', + }, + set = 'Shader', + send_vars = nil, -- function (sprite) - get custom externs to send to shader. + inject = function(self) + self.full_path = (self.mod and self.mod.path or SMODS.path) .. + 'assets/shaders/' .. self.path + local file = NFS.read(self.full_path) + love.filesystem.write(self.key .. "-temp.fs", file) + G.SHADERS[self.key] = love.graphics.newShader(self.key .. "-temp.fs") + love.filesystem.remove(self.key .. "-temp.fs") + -- G.SHADERS[self.key] = love.graphics.newShader(self.full_path) + end, + process_loc_text = function() end + } + + ------------------------------------------------------------------------------------------------- + ----- API CODE GameObject.Edition + ------------------------------------------------------------------------------------------------- + + SMODS.Edition = SMODS.Center:extend { + set = 'Edition', + -- atlas only matters for displaying editions in the collection + atlas = 'Joker', + pos = { x = 0, y = 0 }, + class_prefix = 'e', + discovered = false, + unlocked = true, + apply_to_float = false, + in_shop = false, + weight = 0, + badge_colour = G.C.DARK_EDITION, + -- default sound is foil sound + sound = { sound = "foil1", per = 1.2, vol = 0.4 }, + required_params = { + 'key', + 'shader' -- can be set to `false` for shaderless edition + }, + -- optional fields: + extra_cost = nil, + + -- TODO badge colours. need to check how Steamodded already does badge colors + -- other methods: + calculate = nil, -- function (self) + on_apply = nil, -- function (card) - modify card when edition is applied + on_remove = nil, -- function (card) - modify card when edition is removed + on_load = nil, -- function (card) - modify card when it is loaded from the save file + register = function(self) + self.config = self.config or {} + SMODS.Edition.super.register(self) + end, + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.misc.labels, self.key:sub(3), self.loc_txt, 'label') + SMODS.Edition.super.process_loc_text(self) + end, + -- apply_modifier = true when G.GAME.edition_rate is to be applied + get_weight = function(self, apply_modifier) + return self.weight + end + } + + -- TODO also, this should probably be a utility method in core + -- card_area = pass the card area + -- edition = boolean value + function SMODS.Edition:get_edition_cards(card_area, edition) + local cards = {} + for _, v in ipairs(card_area.cards) do + if (not v.edition and edition) or (v.edition and not edition) then + table.insert(cards, v) + end + end + return cards + end + + SMODS.Edition:take_ownership('foil', { + shader = 'foil', + config = setmetatable({ chips = 50 }, { + __index = function(t, k) + if k == 'extra' then return t.chips end + return rawget(t, k) + end, + __newindex = function(t, k, v) + if k == 'extra' then + t.chips = v; return + end + rawset(t, k, v) + end, + }), + sound = { sound = "foil1", per = 1.2, vol = 0.4 }, + weight = 20, + extra_cost = 2, + get_weight = function(self) + return G.GAME.edition_rate * self.weight + end, + loc_vars = function(self) + return { vars = { self.config.chips } } + end + }) + SMODS.Edition:take_ownership('holo', { + shader = 'holo', + config = setmetatable({ mult = 10 }, { + __index = function(t, k) + if k == 'extra' then return t.mult end + return rawget(t, k) + end, + __newindex = function(t, k, v) + if k == 'extra' then + t.mult = v; return + end + rawset(t, k, v) + end, + }), + sound = { sound = "holo1", per = 1.2 * 1.58, vol = 0.4 }, + weight = 14, + extra_cost = 3, + get_weight = function(self) + return G.GAME.edition_rate * self.weight + end, + loc_vars = function(self) + return { vars = { self.config.mult } } + end + }) + SMODS.Edition:take_ownership('polychrome', { + shader = 'polychrome', + config = setmetatable({ x_mult = 1.5 }, { + __index = function(t, k) + if k == 'extra' then return t.x_mult end + return rawget(t, k) + end, + __newindex = function(t, k, v) + if k == 'extra' then + t.x_mult = v; return + end + rawset(t, k, v) + end, + }), + sound = { sound = "polychrome1", per = 1.2, vol = 0.7 }, + weight = 3, + extra_cost = 5, + get_weight = function(self) + return (G.GAME.edition_rate - 1) * G.P_CENTERS["e_negative"].weight + G.GAME.edition_rate * self.weight + end, + loc_vars = function(self) + return { vars = { self.config.x_mult } } + end + }) + SMODS.Edition:take_ownership('negative', { + shader = 'negative', + config = setmetatable({ card_limit = 1 }, { + __index = function(t, k) + if k == 'extra' then return t.card_limit end + return rawget(t, k) + end, + __newindex = function(t, k, v) + if k == 'extra' then + t.card_limit = v; return + end + rawset(t, k, v) + end, + }), + sound = { sound = "negative", per = 1.5, vol = 0.4 }, + weight = 3, + extra_cost = 5, + get_weight = function(self) + return self.weight + end, + loc_vars = function(self) + return { vars = { self.config.card_limit } } + end, + }) + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.Keybind + ------------------------------------------------------------------------------------------------- + SMODS.Keybinds = {} + SMODS.Keybind = SMODS.GameObject:extend { + obj_table = SMODS.Keybinds, + obj_buffer = {}, + + -- key_pressed = 'x', + held_keys = {}, -- other key(s) that need to be held + -- action = function(controller) + -- print("Keybind pressed") + -- end, + + -- TODO : option to specify if keybind activates on hold, press or release + + required_params = { + 'key', + 'key_pressed', + 'action', + }, + set = 'Keybind', + class_prefix = 'keybind', + + inject = function(_) end + } + + ------------------------------------------------------------------------------------------------- + ------- API CODE GameObject.Achievements + ------------------------------------------------------------------------------------------------- + + SMODS.Achievements = {} + SMODS.Achievement = SMODS.GameObject:extend{ + obj_table = SMODS.Achievements, + obj_buffer = {}, + required_params = { + 'key', + 'unlock_condition', + }, + set = 'Achievement', + class_prefix = "ach", + atlas = "achievements", + pos = {x=1, y=0}, + hidden_pos = {x=0, y=0}, + bypass_all_unlocked = false, + hidden_name = true, + steamid = "STEAMODDED", + pre_inject_class = fetch_achievements, + inject = function(self) + G.ACHIEVEMENTS[self.key] = self + if self.reset_on_startup then + if G.SETTINGS.ACHIEVEMENTS_EARNED[self.key] then G.SETTINGS.ACHIEVEMENTS_EARNED[self.key] = nil end + if G.ACHIEVEMENTS[self.key].earned then G.ACHIEVEMENTS[self.key].earned = nil end + end + end, + process_loc_text = function(self) + SMODS.process_loc_text(G.localization.misc.achievement_names, self.key, self.loc_txt, "name") + SMODS.process_loc_text(G.localization.misc.achievement_descriptions, self.key, self.loc_txt, "description") + end, + } + + ------------------------------------------------------------------------------------------------- + ----- INTERNAL API CODE GameObject._Loc_Post + ------------------------------------------------------------------------------------------------- + + SMODS._Loc_Post = SMODS.GameObject:extend { + obj_table = {}, + obj_buffer = {}, + set = '[INTERNAL]', + silent = true, + register = function() error('INTERNAL CLASS, DO NOT CALL') end, + pre_inject_class = function() + for _, mod in ipairs(SMODS.mod_list) do + if mod.can_load then + SMODS.handle_loc_file(mod.path) + end + end + end + } +end diff --git a/Steamodded/src/index.lua b/Steamodded/src/index.lua new file mode 100644 index 0000000..c4a82c3 --- /dev/null +++ b/Steamodded/src/index.lua @@ -0,0 +1,32 @@ +SMODS.fetch_index = function() + SMODS.index = {} + local https = require"https" + local status, contents = https.request("https://github.com/Aurelius7309/Steamodded.index/archive/refs/heads/main.zip") + if status ~= 200 then return false end + love.filesystem.write('index.zip', contents) + if not love.filesystem.mount('index.zip', 'index') then return false end + local path = 'index/Steamodded.index-main/mods/' + for _, filename in ipairs(love.filesystem.getDirectoryItems(path)) do + local key, ext = filename:sub(1, -6), filename:sub(-5) + if ext:lower() == '.json' then + local success, data = pcall(function() return JSON.decode(love.filesystem.read(path..filename)) end) + if success and data.id == key then SMODS.index[key] = data end + end + end + love.filesystem.unmount('index.zip') + return true +end + +SMODS.update_mod_files = function(id) + local mod = SMODS.Mods[id] + if not mod then return false end + local use_git = os.execute('git -v') == 0 + if false and use_git and os.execute(('cd %s & git pull'):format(mod.path)) == 0 then + return true + end + local https = require"https" + local url = mod.github or (SMODS.index[id] or {}).github + local status, contents = https.request(url) -- TODO account for branches + local hash = contents:match('"currentOid":"([^"]*)"') + sendWarnMessage(hash, "Index") +end diff --git a/Steamodded/src/loader.lua b/Steamodded/src/loader.lua new file mode 100644 index 0000000..249a61e --- /dev/null +++ b/Steamodded/src/loader.lua @@ -0,0 +1,684 @@ +--- STEAMODDED CORE +--- MODULE MODLOADER + +function loadMods(modsDirectory) + SMODS.Mods = {} + SMODS.Mods[SMODS.id] = SMODS + SMODS.Mods['Lovely'] = { + id = 'Lovely', + can_load = true, + version = require'lovely'.version, + meta_mod = true, + } + SMODS.Mods['Balatro'] = { + id = 'Balatro', + can_load = true, + version = G.VERSION, + meta_mod = true, + } + SMODS.mod_priorities = {} + SMODS.mod_list = {} + -- for legacy header support + local header_components = { + name = { pattern = '%-%-%- MOD_NAME: ([^\n]+)\n', required = true }, + id = { pattern = '%-%-%- MOD_ID: ([^ \n]+)\n', required = true }, + author = { pattern = '%-%-%- MOD_AUTHOR: %[(.-)%]\n', required = true, parse_array = true }, + description = { pattern = '%-%-%- MOD_DESCRIPTION: (.-)\n', required = true }, + priority = { pattern = '%-%-%- PRIORITY: (%-?%d+)\n', handle = function(x) return x and x + 0 or 0 end }, + badge_colour = { pattern = '%-%-%- BADGE_COLO[U]?R: (%x-)\n', handle = function(x) return HEX(x or '666666FF') end }, + badge_text_colour = { pattern = '%-%-%- BADGE_TEXT_COLO[U]?R: (%x-)\n', handle = function(x) return HEX(x or 'FFFFFF') end }, + display_name = { pattern = '%-%-%- DISPLAY_NAME: (.-)\n' }, + dependencies = { + pattern = { + '%-%-%- DEPENDENCIES: %[(.-)%]\n', + '%-%-%- DEPENDS: %[(.-)%]\n', + '%-%-%- DEPS: %[(.-)%]\n', + }, + parse_array = true, + handle = function(x) + local t = {} + for _, v in ipairs(x) do + table.insert(t, { + id = v:match '(.-)[<>]' or v, + min_version = v:match '>=([^<>]+)', + max_version = v:match '<=([^<>]+)', + }) + end + return t + end, + }, + conflicts = { + pattern = '%-%-%- CONFLICTS: %[(.-)%]\n', + parse_array = true, + handle = function(x) + local t = {} + for _, v in ipairs(x) do + table.insert(t, { + id = v:match '(.-)[<>]', + min_version = v:match '>=([^<>]+)', + max_version = v:match '<=([^<>]+)', + }) + if t.min_version and not V(t[#t].min_version):is_valid() then t[#t].min_version = nil end + if t.max_version and not V(t[#t].max_version):is_valid() then t[#t].max_version = nil end + end + + return t + end + }, + prefix = { pattern = '%-%-%- PREFIX: (.-)\n' }, + version = { pattern = '%-%-%- VERSION: (.-)\n', handle = function(x) return x and V(x):is_valid() and x or '0.0.0' end }, + outdated = { pattern = { 'SMODS%.INIT', 'SMODS%.Deck[:.]new' } }, + dump_loc = { pattern = { '%-%-%- DUMP_LOCALIZATION\n'}} + } + + + local json_spec = { + id = { type = 'string', required = true }, + author = { type = 'table', required = true, check = function(mod, t) + for k, v in pairs(t) do + if type(k) ~= 'number' or type(v) ~= 'string' then t[k] = nil end + end + return t + end }, + name = { type = 'string', required = true }, + display_name = { type = 'string', check = function(mod, s) mod.display_name = s or mod.name end }, + description = { type = 'string', required = true }, + priority = { type = 'number', default = 0 }, + badge_colour = { type = 'string', check = function(mod, s) local success, hex = pcall(HEX, s); mod.badge_colour = success and hex or HEX('666665FF') end }, + badge_text_colour = { type = 'string', check = function(mod, s) local success, hex = pcall(HEX, s); mod.badge_text_colour = success and hex or HEX('FFFFFFFF') end}, + prefix = { type = 'string', required = true }, + version = { type = 'string', check = function(mod, x) return x and V(x):is_valid() and x or '0.0.0' end }, + dump_loc = { type = 'boolean' }, + dependencies = { type = 'table', check = function(mod, t) + local ops = { + ['<<'] = function(a,b) return a>'] = function(a,b) return a>b end, + ['<='] = function(a,b) return a<=b end, + ['>='] = function(a,b) return a>=b end, + ['=='] = function(a,b) return a==b end + } + for i,v in ipairs(t or {}) do + local parts = {} + parts.str = v + for part in v:gmatch('([^|]+)') do + local x = {} + x.id = part:match '^([^(%s]+)' + local j = 1 + for version_string in string.gmatch(part, '%((.-)%)') do + local operator, version = string.match(version_string, '^(..)(.*)$') + local op = ops[operator] + local ver = V(version) + -- if operator == '<<' and not ver.rev then + -- ver.beta = -1 + -- ver.rev = '~' + -- end + if op and ver:is_valid(true) then + x[j] = { op = op, ver = ver } + j = j+1 + end + end + parts[#parts+1] = x + end + t[i] = parts + end + end}, + conflicts = { type = 'table', check = function(mod, t) + local ops = { + ['<<'] = function(a,b) return a>'] = function(a,b) return a>b end, + ['<='] = function(a,b) return a<=b end, + ['>='] = function(a,b) return a>=b end, + ['=='] = function(a,b) return a==b end + } + for i,v in ipairs(t or {}) do + v = v:gsub('%s', '') + local x = {} + x.str = v + v = v:gsub('%s', '') + x.id = v:match '^([^(%s]+)' + local j = 1 + for version_string in string.gmatch(v, '%((.-)%)') do + local operator, version = string.match(version_string, '^(..)(.*)$') + local op = ops[operator] + local ver = V(version) + -- if operator == '<<' and not ver.rev then + -- ver.beta = -1 + -- ver.rev = '~' + -- end + if op and ver:is_valid(true) then + x[j] = { op = op, ver = ver, str = '('..version_string..')' } + j = j+1 + end + end + t[i] = x + end + end}, + main_file = { type = 'string', required = true }, + __ = { check = function(mod) + if SMODS.Mods[mod.id] then error('dupe') end + end}, + provides = { type = 'table' } + + } + + + local used_prefixes = {} + local lovely_directories = {} + + -- Function to process each directory (including subdirectories) with depth tracking + local function processDirectory(directory, depth) + if depth > 3 or directory..'/' == SMODS.path then + return + end + + local isDirLovely = false + + for _, filename in ipairs(NFS.getDirectoryItems(directory)) do + local file_path = directory .. "/" .. filename + + -- Check if the current file is a directory + local file_type = NFS.getInfo(file_path).type + if file_type == 'directory' or file_type == 'symlink' then + -- Lovely patches + if depth == 2 and filename == "lovely" and not isDirLovely then + isDirLovely = true + table.insert(lovely_directories, directory .. "/") + end + -- If it's a directory and depth is within limit, recursively process it + if depth < 2 or (filename:lower() ~= 'localization' and filename:lower() ~= 'assets') then + processDirectory(file_path, depth + 1) + end + elseif depth == 2 and filename == "lovely.toml" and not isDirLovely then + isDirLovely = true + table.insert(lovely_directories, directory .. "/") + elseif filename:lower():match('%.json') and depth > 1 then + local json_str = NFS.read(file_path) + local parsed, mod = pcall(JSON.decode, json_str) + local valid = true + local err + if not parsed then + valid = false + err = mod + else + mod.json = true + mod.path = directory .. '/' + mod.optional_dependencies = {} + local success, e = pcall(function() + -- remove invalid fields and check required ones first + for k, v in pairs(json_spec) do + if v.type and type(mod[k]) ~= v.type then mod[k] = nil end + if v.required and mod[k] == nil then error(k) end + end + -- perform additional checks and fill in defaults + for k, v in pairs(json_spec) do + if v.default then mod[k] = mod[k] or v.default end + if v.check then v.check(mod, mod[k]) end + end + end) + if not success then + valid = false + err = e + end + end + if not valid then + sendErrorMessage(('Found invalid metadata JSON file at %s, ignoring: %s'):format(file_path, err), 'Loader') + else + sendInfoMessage('Valid JSON file found') + if NFS.getInfo(directory..'/.lovelyignore') then + mod.disabled = true + end + if mod.prefix and used_prefixes[mod.prefix] then + mod.can_load = false + mod.load_issues = { + prefix_conflict = used_prefixes[mod.prefix], + dependencies = {}, + conflicts = {}, + } + sendWarnMessage(('Duplicate Mod prefix %s used by %s, %s'):format(mod.prefix, mod.id, used_prefixes[mod.prefix]), 'Loader') + end + if not NFS.getInfo(mod.path..mod.main_file) then + mod.can_load = false + mod.load_issues = { + main_file_not_found = true, + dependencies = {}, + conflicts = {}, + } + sendWarnMessage(('Unable to load Mod %s: cannot find main file'):format(mod.id), 'Loader') + end + if mod.dump_loc then + SMODS.dump_loc = { + path = mod.path, + } + end + for _,v in ipairs(mod.provides or {}) do + SMODS.Mods[v] = SMODS.Mods[v] or mod + end + SMODS.Mods[mod.id] = mod + SMODS.mod_priorities[mod.priority] = SMODS.mod_priorities[mod.priority] or {} + table.insert(SMODS.mod_priorities[mod.priority], mod) + end + elseif filename:lower():match("%.lua$") then -- Check for legacy headers + if depth == 1 then + sendWarnMessage(('Found lone Lua file %s in Mods directory :: Please place the files for each mod in its own subdirectory.'):format(filename), 'Loader') + end + local file_content = NFS.read(file_path) + + -- Convert CRLF in LF + file_content = file_content:gsub("\r\n", "\n") + + -- Check the header lines using string.match + local headerLine = file_content:match("^(.-)\n") + if headerLine == "--- STEAMODDED HEADER" then + sendTraceMessage('Processing Mod file (Legacy header): ' .. filename, "Loader") + local mod = {} + local sane = true + for k, v in pairs(header_components) do + local component = nil + if type(v.pattern) == "table" then + for _, pattern in ipairs(v.pattern) do + component = file_content:match(pattern) or component + if component then break end + end + else + component = file_content:match(v.pattern) + end + if v.required and not component then + sane = false + sendWarnMessage(string.format('Mod file %s is missing required header component: %s', + filename, k), 'Loader') + break + end + if v.parse_array then + local list = {} + component = component or '' + for val in string.gmatch(component, "([^,]+)") do + table.insert(list, val:match("^%s*(.-)%s*$")) -- Trim spaces + end + component = list + end + if v.handle and type(v.handle) == 'function' then + component = v.handle(component) + end + mod[k] = component + end + if NFS.getInfo(directory..'/.lovelyignore') then + mod.disabled = true + end + if SMODS.Mods[mod.id] then + sane = false + sendWarnMessage("Duplicate Mod ID: " .. mod.id, 'Loader') + end + + if mod.outdated then + mod.prefix_config = { key = { mod = false }, atlas = false } + else + mod.prefix = mod.prefix or (mod.id or ''):lower():sub(1, 4) + end + if mod.prefix and used_prefixes[mod.prefix] then + mod.can_load = false + mod.load_issues = { + prefix_conflict = used_prefixes[mod.prefix], + dependencies = {}, + conflicts = {}, + } + sendWarnMessage(('Duplicate Mod prefix %s used by %s, %s'):format(mod.prefix, mod.id, used_prefixes[mod.prefix]), 'Loader') + end + + if sane then + sendTraceMessage('Saving Mod Info: ' .. mod.id, 'Loader') + mod.path = directory .. '/' + mod.main_file = filename + mod.display_name = mod.display_name or mod.name + if mod.prefix then + used_prefixes[mod.prefix] = mod.id + end + mod.optional_dependencies = {} + if mod.dump_loc then + SMODS.dump_loc = { + path = mod.path, + } + end + SMODS.Mods[mod.id] = mod + SMODS.mod_priorities[mod.priority] = SMODS.mod_priorities[mod.priority] or {} + table.insert(SMODS.mod_priorities[mod.priority], mod) + end + end + end + end + end + + + boot_print_stage('Processing Mod Files') + -- Start processing with the initial directory at depth 1 + processDirectory(modsDirectory, 1) + for _, path in ipairs(lovely_directories) do + local hasSMOD = false + for _, mod in pairs(SMODS.Mods) do + if mod.path == path then + mod.lovely = true + hasSMOD = true + end + end + if not hasSMOD then + local name = string.match(path, "[/\\]([^/\\]+)[/\\]?$") + local disabled = not not NFS.getInfo(path .. '/.lovelyignore') + local mod = { + name = name, + id = "lovely-compat-" .. name, + author = {"???"}, + description = "A lovely mod.", + prefix_config = { key = { mod = false }, atlas = false }, + priority = 0, + badge_colour = HEX("666666FF"), + badge_text_colour = HEX('FFFFFF'), + path = path, + main_file = "", + display_name = name, + dependencies = {}, + optional_dependencies = {}, + conflicts = {}, + version = "0.0.0", + can_load = not disabled, + lovely = true, + lovely_only = true, + meta_mod = true, + disabled = disabled, + load_issues = { + dependencies = {}, + conflicts = {}, + disabled = disabled + } + + } + SMODS.mod_priorities[mod.priority] = SMODS.mod_priorities[mod.priority] or {} + table.insert(SMODS.mod_priorities[mod.priority], mod) + SMODS.Mods[mod.id] = mod + end + end + + -- sort by priority + local keyset = {} + for k, _ in pairs(SMODS.mod_priorities) do + keyset[#keyset + 1] = k + end + table.sort(keyset) + + local function check_dependencies(mod, seen) + if not (mod.can_load == nil) then return mod.can_load end + seen = seen or {} + local can_load = true + if seen[mod.id] then return true end + seen[mod.id] = true + local load_issues = { + dependencies = {}, + conflicts = {}, + } + if not mod.json then + for _, v in ipairs(mod.conflicts or {}) do + -- block load even if the conflict is also blocked + if + SMODS.Mods[v.id] and + (not v.max_version or V(SMODS.Mods[v.id].version) <= V(v.max_version)) and + (not v.min_version or V(SMODS.Mods[v.id].version) >= V(v.min_version)) + then + can_load = false + table.insert(load_issues.conflicts, v.id..(v.max_version and '<='..v.max_version or '')..(v.min_version and '>='..v.min_version or '')) + end + end + for _, v in ipairs(mod.dependencies or {}) do + -- recursively check dependencies of dependencies to make sure they are actually fulfilled + if + not SMODS.Mods[v.id] or + not check_dependencies(SMODS.Mods[v.id], seen) or + (v.max_version and V(SMODS.Mods[v.id].version) > V(v.max_version)) or + (v.min_version and V(SMODS.Mods[v.id].version) < V(v.min_version)) + then + can_load = false + table.insert(load_issues.dependencies, + v.id .. (v.min_version and '>=' .. v.min_version or '') .. (v.max_version and '<=' .. v.max_version or '')) + if v.id == 'Steamodded' then + load_issues.version_mismatch = ''..(v.min_version and '>='..v.min_version or '')..(v.max_version and '<='..v.max_version or '') + end + end + end + else + for _, x in ipairs(mod.dependencies or {}) do + local fulfilled + for _, y in ipairs(x) do + if fulfilled then break end + local id = y.id + if SMODS.Mods[id] and check_dependencies(SMODS.Mods[id], seen) then + fulfilled = true + local dep_ver = V(SMODS.Mods[id].version) + for _, v in ipairs(y) do + if not v.op(dep_ver, v.ver) then + fulfilled = false + end + end + if fulfilled then y.fulfilled = true end + end + end + if not fulfilled then + can_load = false + table.insert(load_issues.dependencies, x.str) + end + end + for _, y in ipairs(mod.conflicts or {}) do + local id = y.id + local conflict = false + if SMODS.Mods[id] and check_dependencies(SMODS.Mods[id], seen) then + conflict = true + local dep_ver = V(SMODS.Mods[id].version) + for _, v in ipairs(y) do + if not v.op(dep_ver, v.ver) then + conflict = false + break + end + end + end + if conflict then + can_load = false + table.insert(load_issues.conflicts, y.str) + end + end + end + if mod.disabled then + can_load = false + load_issues.disabled = true + end + if not can_load then + mod.load_issues = load_issues + return false + end + for _, x in ipairs(mod.dependencies or {}) do + for _, y in ipairs(x) do + if y.fulfilled then SMODS.Mods[y.id].can_load = true end + end + end + return true + end + + -- check dependencies first (for object dependencies) + for _, mod in pairs(SMODS.Mods) do mod.can_load = check_dependencies(mod) end + + boot_print_stage('Loading Mods') + -- load the mod files + for _, priority in ipairs(keyset) do + table.sort(SMODS.mod_priorities[priority], + function(mod_a, mod_b) + return mod_a.id < mod_b.id + end) + for _, mod in ipairs(SMODS.mod_priorities[priority]) do + SMODS.mod_list[#SMODS.mod_list + 1] = mod -- keep mod list in prioritized load order + if mod.can_load and not mod.lovely_only then + SMODS.current_mod = mod + if mod.outdated then + SMODS.compat_0_9_8.with_compat(function() + mod.config = {} + assert(load(NFS.read(mod.path..mod.main_file), ('=[SMODS %s "%s"]'):format(mod.id, mod.main_file)))() + for k, v in pairs(SMODS.compat_0_9_8.init_queue) do + v() + SMODS.compat_0_9_8.init_queue[k] = nil + end + end) + else + SMODS.load_mod_config(mod) + assert(load(NFS.read(mod.path..mod.main_file), ('=[SMODS %s "%s"]'):format(mod.id, mod.main_file)))() + end + SMODS.current_mod = nil + elseif not mod.lovely_only then + sendTraceMessage(string.format("Mod %s was unable to load: %s%s%s%s", mod.id, + mod.load_issues.outdated and + 'Outdated: Steamodded versions 0.9.8 and below are no longer supported!\n' or '', + mod.load_issues.main_file_not_found and "The main file could not be found.\n" or '', + next(mod.load_issues.dependencies) and + ('Missing Dependencies: ' .. inspect(mod.load_issues.dependencies) .. '\n') or '', + next(mod.load_issues.conflicts) and + ('Unresolved Conflicts: ' .. inspect(mod.load_issues.conflicts) .. '\n') or '' + ), 'Loader') + end + end + end + -- compat after loading mods + if SMODS.compat_0_9_8.load_done then + -- Invasive change to Card:generate_UIBox_ability_table() + local Card_generate_UIBox_ability_table_ref = Card.generate_UIBox_ability_table + function Card:generate_UIBox_ability_table(...) + SMODS.compat_0_9_8.generate_UIBox_ability_table_card = self + local ret = Card_generate_UIBox_ability_table_ref(self, ...) + SMODS.compat_0_9_8.generate_UIBox_ability_table_card = nil + return ret + end + end +end + +function SMODS.injectItems() + -- Set .key for vanilla undiscovered, locked objects + for k, v in pairs(G) do + if type(k) == 'string' and (k:sub(-12, -1) == 'undiscovered' or k:sub(-6, -1) == 'locked') then + v.key = k + end + end + SMODS.injectObjects(SMODS.GameObject) + if SMODS.dump_loc then + boot_print_stage('Dumping Localization') + SMODS.create_loc_dump() + end + boot_print_stage('Initializing Localization') + init_localization() + SMODS.SAVE_UNLOCKS() + table.sort(G.P_CENTER_POOLS["Back"], function (a, b) return (a.order - (a.unlocked and 100 or 0)) < (b.order - (b.unlocked and 100 or 0)) end) + for _, t in ipairs{ + G.P_CENTERS, + G.P_BLINDS, + G.P_TAGS, + G.P_SEALS, + } do + for k, v in pairs(t) do + assert(v._discovered_unlocked_overwritten) + end + end +end + +local function initializeModUIFunctions() + for id, modInfo in pairs(SMODS.mod_list) do + G.FUNCS["openModUI_" .. modInfo.id] = function(arg_736_0) + G.ACTIVE_MOD_UI = modInfo + G.FUNCS.overlay_menu({ + definition = create_UIBox_mods(arg_736_0) + }) + end + end +end + +local function checkForLoadFailure() + SMODS.mod_button_alert = false + for k,v in pairs(SMODS.Mods) do + if v and not v.can_load and not v.disabled then + SMODS.mod_button_alert = true + return + end + end +end + +function initSteamodded() + initGlobals() + boot_print_stage("Loading APIs") + loadAPIs() + loadMods(SMODS.MODS_DIR) + checkForLoadFailure() + initializeModUIFunctions() + boot_print_stage("Injecting Items") + SMODS.injectItems() + SMODS.booted = true +end + +-- re-inject on reload +local init_item_prototypes_ref = Game.init_item_prototypes +function Game:init_item_prototypes() + init_item_prototypes_ref(self) + convert_save_data() + if SMODS.booted then + SMODS.injectItems() + end +end + +SMODS.booted = false +function boot_print_stage(stage) + if not SMODS.booted then + boot_timer(nil, "STEAMODDED - " .. stage, 0.95) + end +end + +function boot_timer(_label, _next, progress) + progress = progress or 0 + G.LOADING = G.LOADING or { + font = love.graphics.setNewFont("resources/fonts/m6x11plus.ttf", 20), + love.graphics.dis + } + local realw, realh = love.window.getMode() + love.graphics.setCanvas() + love.graphics.push() + love.graphics.setShader() + love.graphics.clear(0, 0, 0, 1) + love.graphics.setColor(0.6, 0.8, 0.9, 1) + if progress > 0 then love.graphics.rectangle('fill', realw / 2 - 150, realh / 2 - 15, progress * 300, 30, 5) end + love.graphics.setColor(1, 1, 1, 1) + love.graphics.setLineWidth(3) + love.graphics.rectangle('line', realw / 2 - 150, realh / 2 - 15, 300, 30, 5) + love.graphics.print("LOADING: " .. _next, realw / 2 - 150, realh / 2 + 40) + love.graphics.pop() + love.graphics.present() + + G.ARGS.bt = G.ARGS.bt or love.timer.getTime() + G.ARGS.bt = love.timer.getTime() +end + +function SMODS.load_file(path, id) + if not path or path == "" then + error("No path was provided to load.") + end + local mod + if not id then + if not SMODS.current_mod then + error("No ID was provided! Usage without an ID is only available when file is first loaded.") + end + mod = SMODS.current_mod + else + mod = SMODS.Mods[id] + end + if not mod then + error("Mod not found. Ensure you are passing the correct ID.") + end + local file_path = mod.path .. path + local file_content, err = NFS.read(file_path) + if not file_content then return nil, "Error reading file '" .. path .. "' for mod with ID '" .. mod.id .. "': " .. err end + local chunk, err = load(file_content, "=[SMODS " .. mod.id .. ' "' .. path .. '"]') + if not chunk then return nil, "Error processing file '" .. path .. "' for mod with ID '" .. mod.id .. "': " .. err end + return chunk +end + +---------------------------------------------- +------------MOD LOADER END-------------------- diff --git a/Steamodded/src/logging.lua b/Steamodded/src/logging.lua new file mode 100644 index 0000000..e8d7e00 --- /dev/null +++ b/Steamodded/src/logging.lua @@ -0,0 +1,58 @@ +--- STEAMODDED CORE +--- MODULE LOGGING + +function initializeSocketConnection() + local socket = require("socket") + client = socket.connect("localhost", 12345) + if not client then + print("Failed to connect to the debug server") + end +end + +-- message, logger in this order to preserve backward compatibility +function sendTraceMessage(message, logger) + sendMessageToConsole("TRACE", logger, message) +end + +function sendDebugMessage(message, logger) + sendMessageToConsole("DEBUG", logger, message) +end + +function sendInfoMessage(message, logger) + -- space in info string to align the logs in console + sendMessageToConsole("INFO ", logger, message) +end + +function sendWarnMessage(message, logger) + -- space in warn string to align the logs in console + sendMessageToConsole("WARN ", logger, message) +end + +function sendErrorMessage(message, logger) + sendMessageToConsole("ERROR", logger, message) +end + +function sendFatalMessage(message, logger) + sendMessageToConsole("FATAL", logger, message) +end + +function sendMessageToConsole(level, logger, message) + level = level or "DEBUG" + logger = logger or "DefaultLogger" + message = message or "Default log message" + date = os.date('%Y-%m-%d %H:%M:%S') + print(date .. " :: " .. level .. " :: " .. logger .. " :: " .. message) + if client then + -- naive way to separate the logs if the console receive multiple logs at the same time + client:send(date .. " :: " .. level .. " :: " .. logger .. " :: " .. message .. "ENDOFLOG") + end +end + +initializeSocketConnection() + +-- Use the function to send messages +sendDebugMessage("Steamodded Debug Socket started !", "DebugConsole") + + +----------------------------------------------- +---------------MOD LOGGING END----------------- diff --git a/Steamodded/src/overrides.lua b/Steamodded/src/overrides.lua new file mode 100644 index 0000000..a7ab755 --- /dev/null +++ b/Steamodded/src/overrides.lua @@ -0,0 +1,2085 @@ +--- STEAMODDED CORE +--- OVERRIDES + +--#region blind UI +-- Recreate all lines of the blind description. +-- This callback is called each frame. +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.HUD_blind_debuff = function(e) + local scale = 0.4 + local num_lines = #G.GAME.blind.loc_debuff_lines + while G.GAME.blind.loc_debuff_lines[num_lines] == '' do + num_lines = num_lines - 1 + end + local padding = 0.05 + if num_lines > 5 then + local excess_height = (0.3 + padding)*(num_lines - 5) + padding = padding - excess_height / (num_lines + 1) + end + e.config.padding = padding + if num_lines > #e.children then + for i = #e.children+1, num_lines do + local node_def = {n = G.UIT.R, config = {align = "cm", minh = 0.3, maxw = 4.2}, nodes = { + {n = G.UIT.T, config = {ref_table = G.GAME.blind.loc_debuff_lines, ref_value = i, scale = scale * 0.9, colour = G.C.UI.TEXT_LIGHT}}}} + e.UIBox:set_parent_child(node_def, e) + end + elseif num_lines < #e.children then + for i = num_lines+1, #e.children do + e.children[i]:remove() + e.children[i] = nil + end + end + e.UIBox:recalculate() + assert(G.HUD_blind == e.UIBox) +end + +function create_UIBox_your_collection_blinds(exit) + local min_ante = 1 + local max_ante = 16 + local spacing = 1 - 15*0.06 + if G.GAME and G.GAME.round_resets and G.GAME.round_resets.ante then + local current_ante = G.GAME.round_resets.ante + + if current_ante > 8 then + min_ante = current_ante - 8 + 1 + max_ante = current_ante + 8 + end + end + local ante_amounts = {} + for i = min_ante, max_ante do + if i > 1 then + ante_amounts[#ante_amounts + 1] = { n = G.UIT.R, config = { minh = spacing }, nodes = {} } + end + local blind_chip = Sprite(0, 0, 0.2, 0.2, G.ASSET_ATLAS["ui_" .. (G.SETTINGS.colourblind_option and 2 or 1)], + { x = 0, y = 0 }) + blind_chip.states.drag.can = false + ante_amounts[#ante_amounts + 1] = { + n = G.UIT.R, + config = { align = "cm", padding = 0.03 }, + nodes = { + { + n = G.UIT.C, + config = { align = "cm", minw = 0.7 }, + nodes = { + { n = G.UIT.T, config = { text = i, scale = 0.4, colour = G.C.FILTER, shadow = true } }, + } + }, + { + n = G.UIT.C, + config = { align = "cr", minw = 2.8 }, + nodes = { + { n = G.UIT.O, config = { object = blind_chip } }, + { n = G.UIT.C, config = { align = "cm", minw = 0.03, minh = 0.01 }, nodes = {} }, + { n = G.UIT.T, config = { text = number_format(get_blind_amount(i)), scale = 0.4, colour = i <= G.PROFILES[G.SETTINGS.profile].high_scores.furthest_ante.amt and G.C.RED or G.C.JOKER_GREY, shadow = true } }, + } + } + } + } + end + + local rows = 6 + local cols = 5 + local page = 1 + local deck_tables = {} + + G.your_collection = {} + for j = 1, rows do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2 * G.ROOM.T.w / 2, G.ROOM.T.h, + cols * 1.55, + 0.95 * 1.33, + { card_limit = cols, type = 'title_2', highlight_limit = 0, collection = true }) + table.insert(deck_tables, + { + n = G.UIT.R, + config = { align = "cm", padding = 0, no_fill = true }, + nodes = { + j%2 == 0 and { n = G.UIT.B, config = { h = 0.2, w = 0.5 } } or nil, + { n = G.UIT.O, config = { object = G.your_collection[j] } }, + j%2 == 1 and { n = G.UIT.B, config = { h = 0.2, w = 0.5 } } or nil, + } + } + ) + end + + local blind_tab = SMODS.collection_pool(G.P_BLINDS) + local blinds_amt = #blind_tab + + local this_page = {} + for i, v in ipairs(blind_tab) do + if i > rows*cols*(page-1) and i <= rows*cols*page then + table.insert(this_page, v) + elseif i > rows*cols*page then + break + end + end + blind_tab = this_page + + local blinds_to_be_alerted = {} + local row, col = 1, 1 + for k, v in ipairs(blind_tab) do + local temp_blind = AnimatedSprite(G.your_collection[row].T.x + G.your_collection[row].T.w/2, G.your_collection[row].T.y, 1.3, 1.3, G.ANIMATION_ATLAS[v.discovered and v.atlas or 'blind_chips'], + v.discovered and v.pos or G.b_undiscovered.pos) + temp_blind.states.click.can = false + temp_blind.states.drag.can = false + temp_blind.states.hover.can = true + local card = Card(G.your_collection[row].T.x + G.your_collection[row].T.w/2, G.your_collection[row].T.y, 1.3, 1.3, G.P_CARDS.empty, G.P_CENTERS.c_base) + temp_blind.states.click.can = false + card.states.drag.can = false + card.states.hover.can = true + card.children.center = temp_blind + temp_blind:set_role({major = card, role_type = 'Glued', draw_major = card}) + card.set_sprites = function(...) + local args = {...} + if not args[1].animation then return end -- fix for debug unlock + local c = card.children.center + Card.set_sprites(...) + card.children.center = c + end + temp_blind:define_draw_steps({ + { shader = 'dissolve', shadow_height = 0.05 }, + { shader = 'dissolve' } + }) + temp_blind.float = true + card.states.collide.can = true + card.config.blind = v + card.config.force_focus = true + if v.discovered and not v.alerted then + blinds_to_be_alerted[#blinds_to_be_alerted + 1] = card + end + card.hover = function() + if not G.CONTROLLER.dragging.target or G.CONTROLLER.using_touch then + if not card.hovering and card.states.visible then + card.hovering = true + card.hover_tilt = 3 + card:juice_up(0.05, 0.02) + play_sound('chips1', math.random() * 0.1 + 0.55, 0.12) + card.config.h_popup = create_UIBox_blind_popup(v, card.config.blind.discovered) + card.config.h_popup_config = card:align_h_popup() + Node.hover(card) + if card.children.alert then + card.children.alert:remove() + card.children.alert = nil + card.config.blind.alerted = true + G:save_progress() + end + end + end + card.stop_hover = function() + card.hovering = false; Node.stop_hover(card); card.hover_tilt = 0 + end + end + G.your_collection[row]:emplace(card) + col = col + 1 + if col > cols then col = 1; row = row + 1 end + end + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + for _, v in ipairs(blinds_to_be_alerted) do + v.children.alert = UIBox { + definition = create_UIBox_card_alert(), + config = { align = "tri", offset = { x = 0.1, y = 0.1 }, parent = v } + } + v.children.alert.states.collide.can = false + end + return true + end) + })) + + local page_options = {} + for i = 1, math.ceil(blinds_amt/(rows*cols)) do + table.insert(page_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(blinds_amt/(rows*cols)))) + end + + local extras = nil + local t = create_UIBox_generic_options({ + back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', + contents = { + { + n = G.UIT.C, + config = { align = "cm", r = 0.1, colour = G.C.BLACK, padding = 0.1, emboss = 0.05 }, + nodes = { + { + n = G.UIT.C, + config = { align = "cm", r = 0.1, colour = G.C.L_BLACK, padding = 0.1, force_focus = true, focus_args = { nav = 'tall' } }, + nodes = { + { + n = G.UIT.R, + config = { align = "cm", padding = 0.05 }, + nodes = { + { + n = G.UIT.C, + config = { align = "cm", minw = 0.7 }, + nodes = { + { n = G.UIT.T, config = { text = localize('k_ante_cap'), scale = 0.4, colour = lighten(G.C.FILTER, 0.2), shadow = true } }, + } + }, + { + n = G.UIT.C, + config = { align = "cr", minw = 2.8 }, + nodes = { + { n = G.UIT.T, config = { text = localize('k_base_cap'), scale = 0.4, colour = lighten(G.C.RED, 0.2), shadow = true } }, + } + } + } + }, + { n = G.UIT.R, config = { align = "cm" }, nodes = ante_amounts } + } + }, + { + n = G.UIT.C, + config = { align = 'cm' }, + nodes = { + { + n = G.UIT.R, + config = { align = 'cm', padding = 0.15 }, + nodes = {} + }, + { + n= G.UIT.R, + config = {align = 'cm' }, + nodes = { + { + n = G.UIT.C, + config = { + align = 'cm', + }, + nodes = deck_tables, + } + } + }, + { + n = G.UIT.R, + config = { align = 'cm', padding = 0.1 }, + nodes = {} + }, + create_option_cycle({ + options = page_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_collection_blinds_page', + focus_args = {snap_to = true, nav = 'wide'}, + current_option = page, + colour = G.C.RED, + no_pips = true + }) + }, + }, + } + } + } + }) + return t +end + +function G.FUNCS.your_collection_blinds_page(args) + if not args or not args.cycle_config then return end + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards, 1, -1 do + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + + local cols = 5 + local rows = 6 + local page = args.cycle_config.current_option + local blind_tab = SMODS.collection_pool(G.P_BLINDS) + + local this_page = {} + for i, v in ipairs(blind_tab) do + if i > rows*cols*(page-1) and i <= rows*cols*page then + table.insert(this_page, v) + elseif i > rows*cols*page then + break + end + end + blind_tab = this_page + + local blinds_to_be_alerted = {} + local row, col = 1, 1 + for k, v in ipairs(blind_tab) do + local temp_blind = AnimatedSprite(G.your_collection[row].T.x + G.your_collection[row].T.w/2, G.your_collection[row].T.y, 1.3, 1.3, G.ANIMATION_ATLAS[v.discovered and v.atlas or 'blind_chips'], + v.discovered and v.pos or G.b_undiscovered.pos) + temp_blind.states.click.can = false + temp_blind.states.drag.can = false + temp_blind.states.hover.can = true + local card = Card(G.your_collection[row].T.x + G.your_collection[row].T.w/2, G.your_collection[row].T.y, 1.3, 1.3, G.P_CARDS.empty, G.P_CENTERS.c_base) + temp_blind.states.click.can = false + card.states.drag.can = false + card.states.hover.can = true + card.children.center = temp_blind + temp_blind:set_role({major = card, role_type = 'Glued', draw_major = card}) + card.set_sprites = function(...) + local args = {...} + if not args[1].animation then return end -- fix for debug unlock + local c = card.children.center + Card.set_sprites(...) + card.children.center = c + end + temp_blind:define_draw_steps({ + { shader = 'dissolve', shadow_height = 0.05 }, + { shader = 'dissolve' } + }) + temp_blind.float = true + card.states.collide.can = true + card.config.blind = v + card.config.force_focus = true + if v.discovered and not v.alerted then + blinds_to_be_alerted[#blinds_to_be_alerted + 1] = card + end + card.hover = function() + if not G.CONTROLLER.dragging.target or G.CONTROLLER.using_touch then + if not card.hovering and card.states.visible then + card.hovering = true + card.hover_tilt = 3 + card:juice_up(0.05, 0.02) + play_sound('chips1', math.random() * 0.1 + 0.55, 0.12) + card.config.h_popup = create_UIBox_blind_popup(v, card.config.blind.discovered) + card.config.h_popup_config = card:align_h_popup() + Node.hover(card) + if card.children.alert then + card.children.alert:remove() + card.children.alert = nil + card.config.blind.alerted = true + G:save_progress() + end + end + end + card.stop_hover = function() + card.hovering = false; Node.stop_hover(card); card.hover_tilt = 0 + end + end + G.your_collection[row]:emplace(card) + col = col + 1 + if col > cols then col = 1; row = row + 1 end + end + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + for _, v in ipairs(blinds_to_be_alerted) do + v.children.alert = UIBox{ + definition = create_UIBox_card_alert(), + config = { align="tri", offset = {x = 0.1, y = 0.1}, parent = v} + } + v.children.alert.states.collide.can = false + end + return true + end) + })) +end +--#endregion +--#region tag collections +function create_UIBox_your_collection_tags() + G.E_MANAGER:add_event(Event({ + func = function() + G.FUNCS.your_collection_tags_page({ cycle_config = {}}) + return true + end + })) + return { + n = G.UIT.O, + config = { object = UIBox{ + definition = create_UIBox_your_collection_tags_content(), + config = { offset = {x=0, y=0}, align = 'cm' } + }, id = 'your_collection_tags_contents', align = 'cm' }, + } +end + +function create_UIBox_your_collection_tags_content(page) + page = page or 1 + local tag_matrix = {} + local rows = 4 + local cols = 6 + local tag_tab = SMODS.collection_pool(G.P_TAGS) + for i = 1, math.ceil(rows) do + table.insert(tag_matrix, {}) + end + + local tags_to_be_alerted = {} + local row, col = 1, 1 + for k, v in ipairs(tag_tab) do + if k <= cols*rows*(page-1) then elseif k > cols*rows*page then break else + local discovered = v.discovered + local temp_tag = Tag(v.key, true) + if not v.discovered then temp_tag.hide_ability = true end + local temp_tag_ui, temp_tag_sprite = temp_tag:generate_UI() + tag_matrix[row][col] = { + n = G.UIT.C, + config = { align = "cm", padding = 0.1 }, + nodes = { + temp_tag_ui, + } + } + col = col + 1 + if col > cols then col = 1; row = row + 1 end + if discovered and not v.alerted then + tags_to_be_alerted[#tags_to_be_alerted + 1] = temp_tag_sprite + end + end + end + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + for _, v in ipairs(tags_to_be_alerted) do + v.children.alert = UIBox { + definition = create_UIBox_card_alert(), + config = { align = "tri", offset = { x = 0.1, y = 0.1 }, parent = v } + } + v.children.alert.states.collide.can = false + end + return true + end) + })) + + + local table_nodes = {} + for i = 1, rows do + table.insert(table_nodes, { n = G.UIT.R, config = { align = "cm", minh = 1 }, nodes = tag_matrix[i] }) + end + local page_options = {} + for i = 1, math.ceil(#tag_tab/(rows*cols)) do + table.insert(page_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#tag_tab/(rows*cols)))) + end + local t = create_UIBox_generic_options({ + back_func = G.ACTIVE_MOD_UI and "openModUI_" .. G.ACTIVE_MOD_UI.id or 'your_collection', + contents = { + { + n = G.UIT.R, + config = { align = "cm", r = 0.1, colour = G.C.BLACK, padding = 0.1, emboss = 0.05 }, + nodes = { + { + n = G.UIT.C, + config = { align = "cm" }, + nodes = { + { n = G.UIT.R, config = { align = "cm" }, nodes = table_nodes }, + } + }, + } + }, + { + n = G.UIT.R, + config = { align = 'cm' }, + nodes = { + create_option_cycle({ + options = page_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_collection_tags_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = page, + colour = G.C.RED, + no_pips = true + }) + } + } + } + }) + return t +end + +G.FUNCS.your_collection_tags_page = function(args) + local page = args.cycle_config.current_option or 1 + local t = create_UIBox_your_collection_tags_content(page) + local e = G.OVERLAY_MENU:get_UIE_by_ID('your_collection_tags_contents') + if e.config.object then e.config.object:remove() end + e.config.object = UIBox{ + definition = t, + config = {offset = {x=0,y=0}, align = 'cm', parent = e} + } +end +--#endregion +--#region stakes UI +function SMODS.applied_stakes_UI(i, stake_desc_rows, num_added) + if num_added == nil then num_added = { val = 0 } end + if G.P_CENTER_POOLS['Stake'][i].applied_stakes then + for _, v in pairs(G.P_CENTER_POOLS['Stake'][i].applied_stakes) do + if v ~= "white" then + --todo: manage this with pages + if num_added.val < 8 then + local i = G.P_STAKES[v].stake_level + local _stake_desc = {} + local _stake_center = G.P_CENTER_POOLS.Stake[i] + localize { type = 'descriptions', key = _stake_center.key, set = _stake_center.set, nodes = _stake_desc } + local _full_desc = {} + for k, v in ipairs(_stake_desc) do + _full_desc[#_full_desc + 1] = {n = G.UIT.R, config = {align = "cm"}, nodes = v} + end + _full_desc[#_full_desc] = nil + stake_desc_rows[#stake_desc_rows + 1] = {n = G.UIT.R, config = {align = "cm" }, nodes = { + {n = G.UIT.C, config = {align = 'cm'}, nodes = { + {n = G.UIT.C, config = {align = "cm", colour = get_stake_col(i), r = 0.1, minh = 0.35, minw = 0.35, emboss = 0.05 }, nodes = {}}, + {n = G.UIT.B, config = {w = 0.1, h = 0.1}}}}, + {n = G.UIT.C, config = {align = "cm", padding = 0.03, colour = G.C.WHITE, r = 0.1, minh = 0.7, minw = 4.8 }, nodes = + _full_desc},}} + end + num_added.val = num_added.val + 1 + num_added.val = SMODS.applied_stakes_UI(G.P_STAKES[v].stake_level, stake_desc_rows, + num_added) + end + end + end +end + +-- We're overwriting so much that it's better to just remake this +function G.UIDEF.deck_stake_column(_deck_key) + local deck_usage = G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key] + local stake_col = {} + local valid_option = nil + local num_stakes = #G.P_CENTER_POOLS['Stake'] + for i = #G.P_CENTER_POOLS['Stake'], 1, -1 do + local _wins = deck_usage and deck_usage.wins[i] or 0 + if (deck_usage and deck_usage.wins[i - 1]) or i == 1 or G.PROFILES[G.SETTINGS.profile].all_unlocked then valid_option = true end + stake_col[#stake_col + 1] = {n = G.UIT.R, config = {id = i, align = "cm", colour = _wins > 0 and G.C.GREY or G.C.CLEAR, outline = 0, outline_colour = G.C.WHITE, r = 0.1, minh = 2 / num_stakes, minw = valid_option and 0.45 or 0.25, func = 'RUN_SETUP_check_back_stake_highlight'}, nodes = { + {n = G.UIT.R, config = {align = "cm", minh = valid_option and 1.36 / num_stakes or 1.04 / num_stakes, minw = valid_option and 0.37 or 0.13, colour = _wins > 0 and get_stake_col(i) or G.C.UI.TRANSPARENT_LIGHT, r = 0.1}, nodes = {}}}} + if i > 1 then stake_col[#stake_col + 1] = {n = G.UIT.R, config = {align = "cm", minh = 0.8 / num_stakes, minw = 0.04 }, nodes = {} } end + end + return {n = G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR}, nodes = stake_col} +end + +--#endregion +--#region straights and view deck UI +function get_straight(hand) + local ret = {} + local four_fingers = next(SMODS.find_card('j_four_fingers')) + local can_skip = next(SMODS.find_card('j_shortcut')) + if #hand < (5 - (four_fingers and 1 or 0)) then return ret end + local t = {} + local RANKS = {} + for i = 1, #hand do + if hand[i]:get_id() > 0 then + local rank = hand[i].base.value + RANKS[rank] = RANKS[rank] or {} + RANKS[rank][#RANKS[rank] + 1] = hand[i] + end + end + local straight_length = 0 + local straight = false + local skipped_rank = false + local vals = {} + for k, v in pairs(SMODS.Ranks) do + if v.straight_edge then + table.insert(vals, k) + end + end + local init_vals = {} + for _, v in ipairs(vals) do + init_vals[v] = true + end + if not next(vals) then table.insert(vals, 'Ace') end + local initial = true + local br = false + local end_iter = false + local i = 0 + while 1 do + end_iter = false + if straight_length >= (5 - (four_fingers and 1 or 0)) then + straight = true + end + i = i + 1 + if br or (i > #SMODS.Rank.obj_buffer + 1) then break end + if not next(vals) then break end + for _, val in ipairs(vals) do + if init_vals[val] and not initial then br = true end + if RANKS[val] then + straight_length = straight_length + 1 + skipped_rank = false + for _, vv in ipairs(RANKS[val]) do + t[#t + 1] = vv + end + vals = SMODS.Ranks[val].next + initial = false + end_iter = true + break + end + end + if not end_iter then + local new_vals = {} + for _, val in ipairs(vals) do + for _, r in ipairs(SMODS.Ranks[val].next) do + table.insert(new_vals, r) + end + end + vals = new_vals + if can_skip and not skipped_rank then + skipped_rank = true + else + straight_length = 0 + skipped_rank = false + if not straight then t = {} end + if straight then break end + end + end + end + if not straight then return ret end + table.insert(ret, t) + return ret +end + +function G.UIDEF.deck_preview(args) + local _minh, _minw = 0.35, 0.5 + local suit_labels = {} + local suit_counts = {} + local mod_suit_counts = {} + for _, v in ipairs(SMODS.Suit.obj_buffer) do + suit_counts[v] = 0 + mod_suit_counts[v] = 0 + end + local mod_suit_diff = false + local wheel_flipped, wheel_flipped_text = 0, nil + local flip_col = G.C.WHITE + local rank_counts = {} + local deck_tables = {} + remove_nils(G.playing_cards) + table.sort(G.playing_cards, function(a, b) return a:get_nominal('suit') > b:get_nominal('suit') end) + local SUITS = {} + for _, suit in ipairs(SMODS.Suit.obj_buffer) do + SUITS[suit] = {} + for _, rank in ipairs(SMODS.Rank.obj_buffer) do + SUITS[suit][rank] = {} + end + end + local stones = nil + local suit_map = {} + for i = #SMODS.Suit.obj_buffer, 1, -1 do + suit_map[#suit_map + 1] = SMODS.Suit.obj_buffer[i] + end + local rank_name_mapping = {} + for i = #SMODS.Rank.obj_buffer, 1, -1 do + rank_name_mapping[#rank_name_mapping + 1] = SMODS.Rank.obj_buffer[i] + end + for k, v in ipairs(G.playing_cards) do + if v.ability.effect == 'Stone Card' then + stones = stones or 0 + end + if (v.area and v.area == G.deck) or v.ability.wheel_flipped then + if v.ability.wheel_flipped and not (v.area and v.area == G.deck) then wheel_flipped = wheel_flipped + 1 end + if v.ability.effect == 'Stone Card' then + stones = stones + 1 + else + for kk, vv in pairs(suit_counts) do + if v.base.suit == kk then suit_counts[kk] = suit_counts[kk] + 1 end + if v:is_suit(kk) then mod_suit_counts[kk] = mod_suit_counts[kk] + 1 end + end + if SUITS[v.base.suit][v.base.value] then + table.insert(SUITS[v.base.suit][v.base.value], v) + end + rank_counts[v.base.value] = (rank_counts[v.base.value] or 0) + 1 + end + end + end + + wheel_flipped_text = (wheel_flipped > 0) and + {n = G.UIT.T, config = {text = '?', colour = G.C.FILTER, scale = 0.25, shadow = true}} + or nil + flip_col = wheel_flipped_text and mix_colours(G.C.FILTER, G.C.WHITE, 0.7) or G.C.WHITE + + suit_labels[#suit_labels + 1] = {n = G.UIT.R, config = {align = "cm", r = 0.1, padding = 0.04, minw = _minw, minh = 2 * _minh + 0.25}, nodes = { + stones and {n = G.UIT.T, config = {text = localize('ph_deck_preview_stones') .. ': ', colour = G.C.WHITE, scale = 0.25, shadow = true}} + or nil, + stones and {n = G.UIT.T, config = {text = '' .. stones, colour = (stones > 0 and G.C.WHITE or G.C.UI.TRANSPARENT_LIGHT), scale = 0.4, shadow = true}} + or nil,}} + + local _row = {} + local _bg_col = G.C.JOKER_GREY + for k, v in ipairs(rank_name_mapping) do + local _tscale = 0.3 + local _colour = G.C.BLACK + local rank_col = SMODS.Ranks[v].face and G.C.WHITE or _bg_col + rank_col = mix_colours(rank_col, _bg_col, 0.8) + + local _col = {n = G.UIT.C, config = {align = "cm" }, nodes = { + {n = G.UIT.C, config = {align = "cm", r = 0.1, minw = _minw, minh = _minh, colour = rank_col, emboss = 0.04, padding = 0.03 }, nodes = { + {n = G.UIT.R, config = {align = "cm" }, nodes = { + {n = G.UIT.T, config = {text = '' .. SMODS.Ranks[v].shorthand, colour = _colour, scale = 1.6 * _tscale } },}}, + {n = G.UIT.R, config = {align = "cm", minw = _minw + 0.04, minh = _minh, colour = G.C.L_BLACK, r = 0.1 }, nodes = { + {n = G.UIT.T, config = {text = '' .. (rank_counts[v] or 0), colour = flip_col, scale = _tscale, shadow = true } }}}}}}} + table.insert(_row, _col) + end + table.insert(deck_tables, {n = G.UIT.R, config = {align = "cm", padding = 0.04 }, nodes = _row }) + + for _, suit in ipairs(suit_map) do + if not (SMODS.Suits[suit].hidden and suit_counts[suit] == 0) then + _row = {} + _bg_col = mix_colours(G.C.SUITS[suit], G.C.L_BLACK, 0.7) + for _, rank in ipairs(rank_name_mapping) do + local _tscale = #SUITS[suit][rank] > 0 and 0.3 or 0.25 + local _colour = #SUITS[suit][rank] > 0 and flip_col or G.C.UI.TRANSPARENT_LIGHT + + local _col = {n = G.UIT.C, config = {align = "cm", padding = 0.05, minw = _minw + 0.098, minh = _minh }, nodes = { + {n = G.UIT.T, config = {text = '' .. #SUITS[suit][rank], colour = _colour, scale = _tscale, shadow = true, lang = G.LANGUAGES['en-us'] } },}} + table.insert(_row, _col) + end + table.insert(deck_tables, + {n = G.UIT.R, config = {align = "cm", r = 0.1, padding = 0.04, minh = 0.4, colour = _bg_col }, nodes = + _row}) + end + end + + for k, v in ipairs(suit_map) do + if not (SMODS.Suits[v].hidden and suit_counts[v] == 0) then + local t_s = Sprite(0, 0, 0.3, 0.3, + G.ASSET_ATLAS[SMODS.Suits[v][G.SETTINGS.colourblind_option and "hc_ui_atlas" or "lc_ui_atlas"]] or + G.ASSET_ATLAS[("ui_" .. (G.SETTINGS.colourblind_option and "2" or "1"))], SMODS.Suits[v].ui_pos) + t_s.states.drag.can = false + t_s.states.hover.can = false + t_s.states.collide.can = false + + if mod_suit_counts[v] ~= suit_counts[v] then mod_suit_diff = true end + + suit_labels[#suit_labels + 1] = + {n = G.UIT.R, config = {align = "cm", r = 0.1, padding = 0.03, colour = G.C.JOKER_GREY }, nodes = { + {n = G.UIT.C, config = {align = "cm", minw = _minw, minh = _minh }, nodes = { + {n = G.UIT.O, config = {can_collide = false, object = t_s } }}}, + {n = G.UIT.C, config = {align = "cm", minw = _minw * 2.4, minh = _minh, colour = G.C.L_BLACK, r = 0.1 }, nodes = { + {n = G.UIT.T, config = {text = '' .. suit_counts[v], colour = flip_col, scale = 0.3, shadow = true, lang = G.LANGUAGES['en-us'] } }, + mod_suit_counts[v] ~= suit_counts[v] and {n = G.UIT.T, config = {text = ' (' .. mod_suit_counts[v] .. ')', colour = mix_colours(G.C.BLUE, G.C.WHITE, 0.7), scale = 0.28, shadow = true, lang = G.LANGUAGES['en-us'] } } + or nil,}}}} + end + end + + + local t = {n = G.UIT.ROOT, config = {align = "cm", colour = G.C.JOKER_GREY, r = 0.1, emboss = 0.05, padding = 0.07}, nodes = { + {n = G.UIT.R, config = {align = "cm", r = 0.1, emboss = 0.05, colour = G.C.BLACK, padding = 0.1}, nodes = { + {n = G.UIT.R, config = {align = "cm"}, nodes = { + {n = G.UIT.C, config = {align = "cm", padding = 0.04}, nodes = suit_labels }, + {n = G.UIT.C, config = {align = "cm", padding = 0.02}, nodes = deck_tables }}}, + mod_suit_diff and {n = G.UIT.R, config = {align = "cm" }, nodes = { + {n = G.UIT.C, config = {padding = 0.3, r = 0.1, colour = mix_colours(G.C.BLUE, G.C.WHITE, 0.7) }, nodes = {} }, + {n = G.UIT.T, config = {text = ' ' .. localize('ph_deck_preview_effective'), colour = G.C.WHITE, scale = 0.3 } },}} + or nil, + wheel_flipped_text and {n = G.UIT.R, config = {align = "cm" }, nodes = { + {n = G.UIT.C, config = {padding = 0.3, r = 0.1, colour = flip_col }, nodes = {} }, + {n = G.UIT.T, config = { + text = ' ' .. (wheel_flipped > 1 and + localize { type = 'variable', key = 'deck_preview_wheel_plural', vars = { wheel_flipped } } or + localize { type = 'variable', key = 'deck_preview_wheel_singular', vars = { wheel_flipped } }), + colour = G.C.WHITE, + scale = 0.3}},}} + or nil,}}}} + return t +end + +function G.UIDEF.view_deck(unplayed_only) + local deck_tables = {} + remove_nils(G.playing_cards) + G.VIEWING_DECK = true + table.sort(G.playing_cards, function(a, b) return a:get_nominal('suit') > b:get_nominal('suit') end) + local SUITS = {} + local suit_map = {} + for i = #SMODS.Suit.obj_buffer, 1, -1 do + SUITS[SMODS.Suit.obj_buffer[i]] = {} + suit_map[#suit_map + 1] = SMODS.Suit.obj_buffer[i] + end + for k, v in ipairs(G.playing_cards) do + if v.base.suit then table.insert(SUITS[v.base.suit], v) end + end + local num_suits = 0 + for j = 1, #suit_map do + if SUITS[suit_map[j]][1] then num_suits = num_suits + 1 end + end + for j = 1, #suit_map do + if SUITS[suit_map[j]][1] then + local view_deck = CardArea( + G.ROOM.T.x + 0.2 * G.ROOM.T.w / 2, G.ROOM.T.h, + 6.5 * G.CARD_W, + ((num_suits > 8) and 0.2 or (num_suits > 4) and (1 - 0.1 * num_suits) or 0.6) * G.CARD_H, + { + card_limit = #SUITS[suit_map[j]], + type = 'title', + view_deck = true, + highlight_limit = 0, + card_w = G + .CARD_W * 0.7, + draw_layers = { 'card' } + }) + table.insert(deck_tables, + {n = G.UIT.R, config = {align = "cm", padding = 0}, nodes = { + {n = G.UIT.O, config = {object = view_deck}}}} + ) + + for i = 1, #SUITS[suit_map[j]] do + if SUITS[suit_map[j]][i] then + local greyed, _scale = nil, 0.7 + if unplayed_only and not ((SUITS[suit_map[j]][i].area and SUITS[suit_map[j]][i].area == G.deck) or SUITS[suit_map[j]][i].ability.wheel_flipped) then + greyed = true + end + local copy = copy_card(SUITS[suit_map[j]][i], nil, _scale) + copy.greyed = greyed + copy.T.x = view_deck.T.x + view_deck.T.w / 2 + copy.T.y = view_deck.T.y + + copy:hard_set_T() + view_deck:emplace(copy) + end + end + end + end + + local flip_col = G.C.WHITE + + local suit_tallies = {} + local mod_suit_tallies = {} + for _, v in ipairs(suit_map) do + suit_tallies[v] = 0 + mod_suit_tallies[v] = 0 + end + local rank_tallies = {} + local mod_rank_tallies = {} + local rank_name_mapping = SMODS.Rank.obj_buffer + for _, v in ipairs(rank_name_mapping) do + rank_tallies[v] = 0 + mod_rank_tallies[v] = 0 + end + local face_tally = 0 + local mod_face_tally = 0 + local num_tally = 0 + local mod_num_tally = 0 + local ace_tally = 0 + local mod_ace_tally = 0 + local wheel_flipped = 0 + + for k, v in ipairs(G.playing_cards) do + if v.ability.name ~= 'Stone Card' and (not unplayed_only or ((v.area and v.area == G.deck) or v.ability.wheel_flipped)) then + if v.ability.wheel_flipped and not (v.area and v.area == G.deck) and unplayed_only then wheel_flipped = wheel_flipped + 1 end + --For the suits + if v.base.suit then suit_tallies[v.base.suit] = (suit_tallies[v.base.suit] or 0) + 1 end + for kk, vv in pairs(mod_suit_tallies) do + mod_suit_tallies[kk] = (vv or 0) + (v:is_suit(kk) and 1 or 0) + end + + --for face cards/numbered cards/aces + local card_id = v:get_id() + if v.base.value then face_tally = face_tally + ((SMODS.Ranks[v.base.value].face) and 1 or 0) end + mod_face_tally = mod_face_tally + (v:is_face() and 1 or 0) + if v.base.value and not SMODS.Ranks[v.base.value].face and card_id ~= 14 then + num_tally = num_tally + 1 + if not v.debuff then mod_num_tally = mod_num_tally + 1 end + end + if card_id == 14 then + ace_tally = ace_tally + 1 + if not v.debuff then mod_ace_tally = mod_ace_tally + 1 end + end + + --ranks + if v.base.value then rank_tallies[v.base.value] = rank_tallies[v.base.value] + 1 end + if v.base.value and not v.debuff then mod_rank_tallies[v.base.value] = mod_rank_tallies[v.base.value] + 1 end + end + end + local modded = face_tally ~= mod_face_tally + for kk, vv in pairs(mod_suit_tallies) do + modded = modded or (vv ~= suit_tallies[kk]) + if modded then break end + end + + if wheel_flipped > 0 then flip_col = mix_colours(G.C.FILTER, G.C.WHITE, 0.7) end + + local rank_cols = {} + for i = #rank_name_mapping, 1, -1 do + local mod_delta = mod_rank_tallies[i] ~= rank_tallies[i] + rank_cols[#rank_cols + 1] = {n = G.UIT.R, config = {align = "cm", padding = 0.07}, nodes = { + {n = G.UIT.C, config = {align = "cm", r = 0.1, padding = 0.04, emboss = 0.04, minw = 0.5, colour = G.C.L_BLACK}, nodes = { + {n = G.UIT.T, config = {text = SMODS.Ranks[rank_name_mapping[i]].shorthand, colour = G.C.JOKER_GREY, scale = 0.35, shadow = true}},}}, + {n = G.UIT.C, config = {align = "cr", minw = 0.4}, nodes = { + mod_delta and {n = G.UIT.O, config = { + object = DynaText({ + string = { { string = '' .. rank_tallies[i], colour = flip_col }, { string = '' .. mod_rank_tallies[i], colour = G.C.BLUE } }, + colours = { G.C.RED }, scale = 0.4, y_offset = -2, silent = true, shadow = true, pop_in_rate = 10, pop_delay = 4 + })}} + or {n = G.UIT.T, config = {text = rank_tallies[rank_name_mapping[i]], colour = flip_col, scale = 0.45, shadow = true } },}}}} + end + + local tally_ui = { + -- base cards + {n = G.UIT.R, config = {align = "cm", minh = 0.05, padding = 0.07}, nodes = { + {n = G.UIT.O, config = { + object = DynaText({ + string = { + { string = localize('k_base_cards'), colour = G.C.RED }, + modded and { string = localize('k_effective'), colour = G.C.BLUE } or nil + }, + colours = { G.C.RED }, silent = true, scale = 0.4, pop_in_rate = 10, pop_delay = 4 + }) + }}}}, + -- aces, faces and numbered cards + {n = G.UIT.R, config = {align = "cm", minh = 0.05, padding = 0.1}, nodes = { + tally_sprite( + { x = 1, y = 0 }, + { { string = '' .. ace_tally, colour = flip_col }, { string = '' .. mod_ace_tally, colour = G.C.BLUE } }, + { localize('k_aces') } + ), --Aces + tally_sprite( + { x = 2, y = 0 }, + { { string = '' .. face_tally, colour = flip_col }, { string = '' .. mod_face_tally, colour = G.C.BLUE } }, + { localize('k_face_cards') } + ), --Face + tally_sprite( + { x = 3, y = 0 }, + { { string = '' .. num_tally, colour = flip_col }, { string = '' .. mod_num_tally, colour = G.C.BLUE } }, + { localize('k_numbered_cards') } + ), --Numbers + }}, + } + -- add suit tallies + local i = 1 + local num_suits_shown = 0 + for i = 1, #suit_map do + if not (SMODS.Suits[suit_map[i]].hidden and suit_tallies[suit_map[i]] == 0) then + num_suits_shown = num_suits_shown+1 + end + end + local suits_per_row = num_suits_shown > 6 and 4 or num_suits_shown > 4 and 3 or 2 + local n_nodes = {} + while i <= #suit_map do + while #n_nodes < suits_per_row and i <= #suit_map do + if not (SMODS.Suits[suit_map[i]].hidden and suit_tallies[suit_map[i]] == 0) then + table.insert(n_nodes, tally_sprite( + SMODS.Suits[suit_map[i]].ui_pos, + { + { string = '' .. suit_tallies[suit_map[i]], colour = flip_col }, + { string = '' .. mod_suit_tallies[suit_map[i]], colour = G.C.BLUE } + }, + { localize(suit_map[i], 'suits_plural') }, + suit_map[i] + )) + end + i = i + 1 + end + if #n_nodes > 0 then + local n = {n = G.UIT.R, config = {align = "cm", minh = 0.05, padding = 0.1}, nodes = n_nodes} + table.insert(tally_ui, n) + n_nodes = {} + end + end + local t = {n = G.UIT.ROOT, config = {align = "cm", colour = G.C.CLEAR}, nodes = { + {n = G.UIT.R, config = {align = "cm", padding = 0.05}, nodes = {}}, + {n = G.UIT.R, config = {align = "cm"}, nodes = { + {n = G.UIT.C, config = {align = "cm", minw = 1.5, minh = 2, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = { + {n = G.UIT.C, config = {align = "cm", padding = 0.1}, nodes = { + {n = G.UIT.R, config = {align = "cm", r = 0.1, colour = G.C.L_BLACK, emboss = 0.05, padding = 0.15}, nodes = { + {n = G.UIT.R, config = {align = "cm"}, nodes = { + {n = G.UIT.O, config = { + object = DynaText({ string = G.GAME.selected_back.loc_name, colours = {G.C.WHITE}, bump = true, rotate = true, shadow = true, scale = 0.6 - string.len(G.GAME.selected_back.loc_name) * 0.01 }) + }},}}, + {n = G.UIT.R, config = {align = "cm", r = 0.1, padding = 0.1, minw = 2.5, minh = 1.3, colour = G.C.WHITE, emboss = 0.05}, nodes = { + {n = G.UIT.O, config = { + object = UIBox { + definition = G.GAME.selected_back:generate_UI(nil, 0.7, 0.5, G.GAME.challenge), config = {offset = { x = 0, y = 0 } } + } + }}}}}}, + {n = G.UIT.R, config = {align = "cm", r = 0.1, outline_colour = G.C.L_BLACK, line_emboss = 0.05, outline = 1.5}, nodes = + tally_ui}}}, + {n = G.UIT.C, config = {align = "cm"}, nodes = rank_cols}, + {n = G.UIT.B, config = {w = 0.1, h = 0.1}},}}, + {n = G.UIT.B, config = {w = 0.2, h = 0.1}}, + {n = G.UIT.C, config = {align = "cm", padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = + deck_tables}}}, + {n = G.UIT.R, config = {align = "cm", minh = 0.8, padding = 0.05}, nodes = { + modded and {n = G.UIT.R, config = {align = "cm"}, nodes = { + {n = G.UIT.C, config = {padding = 0.3, r = 0.1, colour = mix_colours(G.C.BLUE, G.C.WHITE, 0.7)}, nodes = {}}, + {n = G.UIT.T, config = {text = ' ' .. localize('ph_deck_preview_effective'), colour = G.C.WHITE, scale = 0.3}},}} + or nil, + wheel_flipped > 0 and {n = G.UIT.R, config = {align = "cm"}, nodes = { + {n = G.UIT.C, config = {padding = 0.3, r = 0.1, colour = flip_col}, nodes = {}}, + {n = G.UIT.T, config = { + text = ' ' .. (wheel_flipped > 1 and + localize { type = 'variable', key = 'deck_preview_wheel_plural', vars = { wheel_flipped } } or + localize { type = 'variable', key = 'deck_preview_wheel_singular', vars = { wheel_flipped } }), + colour = G.C.WHITE, scale = 0.3 + }},}} + or nil,}}}} + return t +end + +--#endregion +--#region poker hands +local init_game_object_ref = Game.init_game_object +function Game:init_game_object() + local t = init_game_object_ref(self) + for _, key in ipairs(SMODS.PokerHand.obj_buffer) do + t.hands[key] = {} + for k, v in pairs(SMODS.PokerHands[key]) do + -- G.GAME needs to be able to be serialized + -- TODO this is too specific; ex. nested tables with simple keys + -- are fine. + -- In fact, the check should just warn you if you have a key that + -- can't be serialized. + if type(v) == 'number' or type(v) == 'boolean' or k == 'example' then + t.hands[key][k] = v + end + end + end + return t +end + +-- why bother patching when i basically change everything +function G.FUNCS.get_poker_hand_info(_cards) + local poker_hands = evaluate_poker_hand(_cards) + local scoring_hand = {} + local text, disp_text, loc_disp_text = 'NULL', 'NULL', 'NULL' + for _, v in ipairs(G.handlist) do + if next(poker_hands[v]) then + text = v + scoring_hand = poker_hands[v][1] + break + end + end + disp_text = text + local _hand = SMODS.PokerHands[text] + if text == 'Straight Flush' then + local royal = true + for j = 1, #scoring_hand do + local rank = SMODS.Ranks[scoring_hand[j].base.value] + royal = royal and (rank.key == 'Ace' or rank.key == '10' or rank.face) + end + if royal then + disp_text = 'Royal Flush' + end + elseif _hand and _hand.modify_display_text and type(_hand.modify_display_text) == 'function' then + disp_text = _hand:modify_display_text(_cards, scoring_hand) or disp_text + end + loc_disp_text = localize(disp_text, 'poker_hands') + return text, loc_disp_text, poker_hands, scoring_hand, disp_text +end + +function create_UIBox_current_hands(simple) + G.current_hands = {} + local index = 0 + for _, v in ipairs(G.handlist) do + local ui_element = create_UIBox_current_hand_row(v, simple) + G.current_hands[index + 1] = ui_element + if ui_element then + index = index + 1 + end + if index >= 10 then + break + end + end + + local visible_hands = {} + for _, v in ipairs(G.handlist) do + if G.GAME.hands[v].visible then + table.insert(visible_hands, v) + end + end + + local hand_options = {} + for i = 1, math.ceil(#visible_hands / 10) do + table.insert(hand_options, + localize('k_page') .. ' ' .. tostring(i) .. '/' .. tostring(math.ceil(#visible_hands / 10))) + end + + local object = {n = G.UIT.ROOT, config = {align = "cm", colour = G.C.CLEAR}, nodes = { + {n = G.UIT.R, config = {align = "cm", padding = 0.04}, nodes = + G.current_hands}, + {n = G.UIT.R, config = {align = "cm", padding = 0}, nodes = { + create_option_cycle({ + options = hand_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_hands_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = 1, + colour = G.C.RED, + no_pips = true + })}}}} + + local t = {n = G.UIT.ROOT, config = {align = "cm", minw = 3, padding = 0.1, r = 0.1, colour = G.C.CLEAR}, nodes = { + {n = G.UIT.O, config = { + id = 'hand_list', + object = UIBox { + definition = object, config = {offset = { x = 0, y = 0 }, align = 'cm'} + } + }}}} + return t +end + +G.FUNCS.your_hands_page = function(args) + if not args or not args.cycle_config then return end + G.current_hands = {} + + + local index = 0 + for _, v in ipairs(G.handlist) do + local ui_element = create_UIBox_current_hand_row(v, simple) + if index >= (0 + 10 * (args.cycle_config.current_option - 1)) and index < 10 * args.cycle_config.current_option then + G.current_hands[index - (10 * (args.cycle_config.current_option - 1)) + 1] = ui_element + end + + if ui_element then + index = index + 1 + end + + if index >= 10 * args.cycle_config.current_option then + break + end + end + + local visible_hands = {} + for _, v in ipairs(G.handlist) do + if G.GAME.hands[v].visible then + table.insert(visible_hands, v) + end + end + + local hand_options = {} + for i = 1, math.ceil(#visible_hands / 10) do + table.insert(hand_options, + localize('k_page') .. ' ' .. tostring(i) .. '/' .. tostring(math.ceil(#visible_hands / 10))) + end + + local object = {n = G.UIT.ROOT, config = {align = "cm", colour = G.C.CLEAR }, nodes = { + {n = G.UIT.R, config = {align = "cm", padding = 0.04 }, nodes = G.current_hands + }, + {n = G.UIT.R, config = {align = "cm", padding = 0 }, nodes = { + create_option_cycle({ + options = hand_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = + 'your_hands_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = args.cycle_config.current_option, + colour = G + .C.RED, + no_pips = true + }) + } + } + } + } + + local hand_list = G.OVERLAY_MENU:get_UIE_by_ID('hand_list') + if hand_list then + if hand_list.config.object then + hand_list.config.object:remove() + end + hand_list.config.object = UIBox { + definition = object, config = {offset = { x = 0, y = 0 }, align = 'cm', parent = hand_list } + } + end +end + +function evaluate_poker_hand(hand) + local results = {} + local parts = {} + for _, v in ipairs(SMODS.PokerHandPart.obj_buffer) do + parts[v] = SMODS.PokerHandParts[v].func(hand) or {} + end + for k, _hand in pairs(SMODS.PokerHands) do + results[k] = _hand.evaluate(parts, hand) or {} + end + for _, v in ipairs(G.handlist) do + if not results.top and results[v] then + results.top = results[v] + break + end + end + return results +end +--#endregion +--#region editions +function create_UIBox_your_collection_editions(exit) + local deck_tables = {} + local edition_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Edition) + local rows, cols = (#edition_pool > 5 and 2 or 1), 5 + local page = 0 + + G.your_collection = {} + for j = 1, rows do + G.your_collection[j] = CardArea(G.ROOM.T.x + 0.2 * G.ROOM.T.w / 2, G.ROOM.T.h, 5.3 * G.CARD_W, 1.03 * G.CARD_H, + { + card_limit = cols, + type = 'title', + highlight_limit = 0, + collection = true + }) + table.insert(deck_tables, + {n = G.UIT.R, config = {align = "cm", padding = 0, no_fill = true}, nodes = { + {n = G.UIT.O, config = {object = G.your_collection[j]}}}} + ) + end + + table.sort(edition_pool, function(a, b) return a.order < b.order end) + + local count = math.min(cols * rows, #edition_pool) + local index = 1 + (rows * cols * page) + for j = 1, rows do + for i = 1, cols do + local edition = edition_pool[index] + + if not edition then + break + end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, + G.CARD_W, G.CARD_H, nil, edition) + card:start_materialize(nil, i > 1 or j > 1) + if edition.discovered then card:set_edition(edition.key, true, true) end + G.your_collection[j]:emplace(card) + index = index + 1 + end + if index > count then + break + end + end + + local edition_options = {} + + local t = create_UIBox_generic_options({ + infotip = localize('ml_edition_seal_enhancement_explanation'), + back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', + snap_back = true, + contents = { + {n = G.UIT.R, config = {align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = + deck_tables}} + }) + + if #edition_pool > rows * cols then + for i = 1, math.ceil(#edition_pool / (rows * cols)) do + table.insert(edition_options, localize('k_page') .. ' ' .. tostring(i) .. '/' .. + tostring(math.ceil(#edition_pool / (rows * cols)))) + end + t = create_UIBox_generic_options({ + infotip = localize('ml_edition_seal_enhancement_explanation'), + back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', + snap_back = true, + contents = { + {n = G.UIT.R, config = {align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = + deck_tables}, + {n = G.UIT.R, config = {align = "cm"}, nodes = { + create_option_cycle({ + options = edition_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_collection_editions_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = 1, + r = rows, + c = cols, + colour = G.C.RED, + no_pips = true + })}} + } + }) + end + return t +end + +G.FUNCS.your_collection_editions_page = function(args) + if not args or not args.cycle_config then + return + end + local edition_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Edition) + local rows = (#edition_pool > 5 and 2 or 1) + local cols = 5 + local page = args.cycle_config.current_option + if page > math.ceil(#edition_pool / (rows * cols)) then + page = page - math.ceil(#edition_pool / (rows * cols)) + end + local count = rows * cols + local offset = (rows * cols) * (page - 1) + + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards, 1, -1 do + if G.your_collection[j] ~= nil then + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + end + + for j = 1, rows do + for i = 1, cols do + if count % rows > 0 and i <= count % rows and j == cols then + offset = offset - 1 + break + end + local idx = i + (j - 1) * cols + offset + if idx > #edition_pool then return end + local edition = edition_pool[idx] + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, + G.CARD_W, G.CARD_H, G.P_CARDS.empty, edition) + if edition.discovered then card:set_edition(edition.key, true, true) end + card:start_materialize(nil, i > 1 or j > 1) + G.your_collection[j]:emplace(card) + end + end +end + +-- Init custom card parameters. +local card_init = Card.init +function Card:init(X, Y, W, H, card, center, params) + card_init(self, X, Y, W, H, card, center, params) + + -- This table contains object keys for layers (e.g. edition) + -- that dont want base layer to be drawn. + -- When layer is removed, layer's value should be set to nil. + self.ignore_base_shader = self.ignore_base_shader or {} + -- This table contains object keys for layers (e.g. edition) + -- that dont want shadow to be drawn. + -- When layer is removed, layer's value should be set to nil. + self.ignore_shadow = self.ignore_shadow or {} +end + +function Card:should_draw_base_shader() + return not next(self.ignore_base_shader or {}) +end + +function Card:should_draw_shadow() + return not next(self.ignore_shadow or {}) +end + +local smods_card_load = Card.load +-- +function Card:load(cardTable, other_card) + local ret = smods_card_load(self, cardTable, other_card) + local on_edition_loaded = self.edition and self.edition.key and G.P_CENTERS[self.edition.key].on_load + if type(on_edition_loaded) == "function" then + on_edition_loaded(self) + end + + return ret +end + +-- self = pass the card +-- edition = +-- nil (removes edition) +-- OR key as string +-- OR { name_of_edition = true } (key without e_). This is from the base game, prefer using a string. +-- OR another card's self.edition table +-- immediate = boolean value +-- silent = boolean value +function Card:set_edition(edition, immediate, silent) + -- Check to see if negative is being removed and reduce card_limit accordingly + if (self.added_to_deck or self.joker_added_to_deck_but_debuffed or (self.area == G.hand and not self.debuff)) and self.edition and self.edition.card_limit then + if self.ability.consumeable and self.area == G.consumeables then + G.consumeables.config.card_limit = G.consumeables.config.card_limit - self.edition.card_limit + elseif self.ability.set == 'Joker' and self.area == G.jokers then + G.jokers.config.card_limit = G.jokers.config.card_limit - self.edition.card_limit + elseif self.area == G.hand then + G.hand.config.card_limit = G.hand.config.card_limit - self.edition.card_limit + end + end + + local old_edition = self.edition and self.edition.key + if old_edition then + self.ignore_base_shader[old_edition] = nil + self.ignore_shadow[old_edition] = nil + + local on_old_edition_removed = G.P_CENTERS[old_edition] and G.P_CENTERS[old_edition].on_remove + if type(on_old_edition_removed) == "function" then + on_old_edition_removed(self) + end + end + + local edition_type = nil + if type(edition) == 'string' then + assert(string.sub(edition, 1, 2) == 'e_') + edition_type = string.sub(edition, 3) + elseif type(edition) == 'table' then + if edition.type then + edition_type = edition.type + else + for k, v in pairs(edition) do + if v then + assert(not edition_type) + edition_type = k + end + end + end + end + + if not edition_type or edition_type == 'base' then + if self.edition == nil then -- early exit + return + end + self.edition = nil -- remove edition from card + self:set_cost() + if not silent then + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = not immediate and 0.2 or 0, + blockable = not immediate, + func = function() + self:juice_up(1, 0.5) + play_sound('whoosh2', 1.2, 0.6) + return true + end + })) + end + return + end + + self.edition = {} + self.edition[edition_type] = true + self.edition.type = edition_type + self.edition.key = 'e_' .. edition_type + + local p_edition = G.P_CENTERS['e_' .. edition_type] + + if p_edition.override_base_shader or p_edition.disable_base_shader then + self.ignore_base_shader[self.edition.key] = true + end + if p_edition.no_shadow or p_edition.disable_shadow then + self.ignore_shadow[self.edition.key] = true + end + + local on_edition_applied = p_edition.on_apply + if type(on_edition_applied) == "function" then + on_edition_applied(self) + end + + for k, v in pairs(p_edition.config) do + if type(v) == 'table' then + self.edition[k] = copy_table(v) + else + self.edition[k] = v + end + if k == 'card_limit' and (self.added_to_deck or self.joker_added_to_deck_but_debuffed or (self.area == G.hand and not self.debuff)) and G.jokers and G.consumeables then + if self.ability.consumeable then + G.consumeables.config.card_limit = G.consumeables.config.card_limit + v + elseif self.ability.set == 'Joker' then + G.jokers.config.card_limit = G.jokers.config.card_limit + v + elseif self.area == G.hand and not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK) then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + if G.hand.config.real_card_limit then + G.hand.config.real_card_limit = G.hand.config.real_card_limit + v + end + G.hand.config.card_limit = G.hand.config.card_limit + v + G.FUNCS.draw_from_deck_to_hand(v) + return true + end + })) + end + end + end + + if self.area and self.area == G.jokers then + if self.edition then + if not G.P_CENTERS['e_' .. (self.edition.type)].discovered then + discover_card(G.P_CENTERS['e_' .. (self.edition.type)]) + end + else + if not G.P_CENTERS['e_base'].discovered then + discover_card(G.P_CENTERS['e_base']) + end + end + end + + if self.edition and not silent then + local ed = G.P_CENTERS['e_' .. (self.edition.type)] + G.CONTROLLER.locks.edition = true + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = not immediate and 0.2 or 0, + blockable = not immediate, + func = function() + if self.edition then + self:juice_up(1, 0.5) + play_sound(ed.sound.sound, ed.sound.per, ed.sound.vol) + end + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + G.CONTROLLER.locks.edition = false + return true + end + })) + end + + if G.jokers and self.area == G.jokers then + check_for_unlock({ type = 'modify_jokers' }) + end + + self:set_cost() +end + +-- _key = key value for random seed +-- _mod = scale of chance against base card (does not change guaranteed weights) +-- _no_neg = boolean value to disable negative edition +-- _guaranteed = boolean value to determine whether an edition is guaranteed +-- _options = list of keys of editions to include in the poll +-- OR list of tables { name = key, weight = number } +function poll_edition(_key, _mod, _no_neg, _guaranteed, _options) + local _modifier = 1 + local edition_poll = pseudorandom(pseudoseed(_key or 'edition_generic')) -- Generate the poll value + local available_editions = {} -- Table containing a list of editions and their weights + + if not _options then + _options = { 'e_negative', 'e_polychrome', 'e_holo', 'e_foil' } + if _key == "wheel_of_fortune" or _key == "aura" then -- set base game edition polling + else + for _, v in ipairs(G.P_CENTER_POOLS.Edition) do + if v.in_shop then + table.insert(_options, v.key) + end + end + end + end + for _, v in ipairs(_options) do + local edition_option = {} + if type(v) == 'string' then + assert(string.sub(v, 1, 2) == 'e_') + edition_option = { name = v, weight = G.P_CENTERS[v].weight } + elseif type(v) == 'table' then + assert(string.sub(v.name, 1, 2) == 'e_') + edition_option = { name = v.name, weight = v.weight } + end + table.insert(available_editions, edition_option) + end + + -- Calculate total weight of editions + local total_weight = 0 + for _, v in ipairs(available_editions) do + total_weight = total_weight + (v.weight) -- total all the weights of the polled editions + end + -- sendDebugMessage("Edition weights: "..total_weight, "EditionAPI") + -- If not guaranteed, calculate the base card rate to maintain base 4% chance of editions + if not _guaranteed then + _modifier = _mod or 1 + total_weight = total_weight + (total_weight / 4 * 96) -- Find total weight with base_card_rate as 96% + for _, v in ipairs(available_editions) do + v.weight = G.P_CENTERS[v.name]:get_weight() -- Apply game modifiers where appropriate (defined in edition declaration) + end + end + -- sendDebugMessage("Total weight: "..total_weight, "EditionAPI") + -- sendDebugMessage("Editions: "..#available_editions, "EditionAPI") + -- sendDebugMessage("Poll: "..edition_poll, "EditionAPI") + + -- Calculate whether edition is selected + local weight_i = 0 + for _, v in ipairs(available_editions) do + weight_i = weight_i + v.weight * _modifier + -- sendDebugMessage(v.name.." weight is "..v.weight*_modifier) + -- sendDebugMessage("Checking for "..v.name.." at "..(1 - (weight_i)/total_weight), "EditionAPI") + if edition_poll > 1 - (weight_i) / total_weight then + if not (v.name == 'e_negative' and _no_neg) then -- skip return if negative is selected and _no_neg is true + -- sendDebugMessage("Matched edition: "..v.name, "EditionAPI") + return v.name + end + end + end + + return nil +end + +local cge = Card.get_edition +function Card:get_edition() + local ret = cge(self) + if self.edition and self.edition.key then + local ed = SMODS.Centers[self.edition.key] + if ed.calculate and type(ed.calculate) == 'function' then + ed:calculate(self, {edition_main = true, edition_val = ret}) + end + end + return ret +end + +--#endregion +--#region enhancements UI +function create_UIBox_your_collection_enhancements(exit) + local deck_tables = {} + local rows, cols = 2, 4 + local page = 0 + local enhancement_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Enhanced) + G.your_collection = {} + for j = 1, rows do + G.your_collection[j] = CardArea(G.ROOM.T.x + 0.2 * G.ROOM.T.w / 2, G.ROOM.T.h, 4.25 * G.CARD_W, 1.03 * G.CARD_H, + { + card_limit = cols, + type = 'title', + highlight_limit = 0, + collection = true + }) + table.insert(deck_tables, + {n = G.UIT.R, config = {align = "cm", padding = 0, no_fill = true}, nodes = { + {n = G.UIT.O, config = {object = G.your_collection[j]}}} + }) + end + + table.sort(enhancement_pool, function(a, b) return a.order < b.order end) + + local count = math.min(cols * rows, #enhancement_pool) + local index = 1 + (rows * cols * page) + for j = 1, rows do + for i = 1, cols do + local center = enhancement_pool[index] + if not center then + break + end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, G + .CARD_W, G.CARD_H, G.P_CARDS.empty, center) + card:set_ability(center, true, true) + G.your_collection[j]:emplace(card) + index = index + 1 + end + if index > count then + break + end + end + + local enhancement_options = {} + + local t = create_UIBox_generic_options({ + infotip = localize('ml_edition_seal_enhancement_explanation'), + back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', + snap_back = true, + contents = { + {n = G.UIT.R, config = {align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05 }, nodes = + deck_tables} + } + }) + + if #enhancement_pool > rows * cols then + for i = 1, math.ceil(#enhancement_pool / (rows * cols)) do + table.insert(enhancement_options, localize('k_page') .. ' ' .. tostring(i) .. '/' .. + tostring(math.ceil(#enhancement_pool / (rows * cols)))) + end + t = create_UIBox_generic_options({ + infotip = localize('ml_edition_seal_enhancement_explanation'), + back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', + snap_back = true, + contents = { + {n = G.UIT.R, config = {align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = + deck_tables}, + {n = G.UIT.R, config = {align = "cm"}, nodes = { + create_option_cycle({ + options = enhancement_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_collection_enhancements_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = 1, + r = rows, + c = cols, + colour = G.C.RED, + no_pips = true + })}} + } + }) + end + return t +end + +G.FUNCS.your_collection_enhancements_page = function(args) + if not args or not args.cycle_config then + return + end + local rows = 2 + local cols = 4 + local page = args.cycle_config.current_option + local enhancement_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Enhanced) + if page > math.ceil(#enhancement_pool / (rows * cols)) then + page = page - math.ceil(#enhancement_pool / (rows * cols)) + end + local count = rows * cols + local offset = (rows * cols) * (page - 1) + + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards, 1, -1 do + if G.your_collection[j] ~= nil then + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + end + + for j = 1, rows do + for i = 1, cols do + if count % rows > 0 and i <= count % rows and j == cols then + offset = offset - 1 + break + end + local idx = i + (j - 1) * cols + offset + if idx > #enhancement_pool then return end + local center = enhancement_pool[idx] + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, + G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + card:set_ability(center, true, true) + card:start_materialize(nil, i > 1 or j > 1) + G.your_collection[j]:emplace(card) + end + end +end +--#endregion +--#region seals ui +function create_UIBox_your_collection_seals(exit) + local deck_tables = {} + local seal_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Seal) + local rows, cols = (#seal_pool > 5 and 2 or 1), 5 + local page = 0 + + G.your_collection = {} + for j = 1, rows do + G.your_collection[j] = CardArea(G.ROOM.T.x + 0.2 * G.ROOM.T.w / 2, G.ROOM.T.h, 5.3 * G.CARD_W, 1.03 * G.CARD_H, + { + card_limit = cols, + type = 'title', + highlight_limit = 0, + collection = true + }) + table.insert(deck_tables, + {n = G.UIT.R, config = {align = "cm", padding = 0, no_fill = true}, nodes = { + {n = G.UIT.O, config = {object = G.your_collection[j]}}}} + ) + end + + table.sort(seal_pool, function(a, b) return a.order < b.order end) + + local count = math.min(cols * rows, #seal_pool) + local index = 1 + (rows * cols * page) + for j = 1, rows do + for i = 1, cols do + local seal = seal_pool[index] + + if not seal then + break + end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, + G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS.c_base) + card:set_seal(seal.key, true) + G.your_collection[j]:emplace(card) + index = index + 1 + end + if index > count then + break + end + end + + local edition_options = {} + + local t = create_UIBox_generic_options({ + infotip = localize('ml_edition_seal_enhancement_explanation'), + back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', + snap_back = true, + contents = { + {n = G.UIT.R, config = {align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = + deck_tables}} + }) + + if #seal_pool > rows * cols then + for i = 1, math.ceil(#seal_pool / (rows * cols)) do + table.insert(edition_options, localize('k_page') .. ' ' .. tostring(i) .. '/' .. + tostring(math.ceil(#seal_pool / (rows * cols)))) + end + t = create_UIBox_generic_options({ + infotip = localize('ml_edition_seal_enhancement_explanation'), + back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', + snap_back = true, + contents = { + {n = G.UIT.R, config = {align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = + deck_tables}, + {n = G.UIT.R, config = {align = "cm"}, nodes = { + create_option_cycle({ + options = edition_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_collection_seals_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = 1, + r = rows, + c = cols, + colour = G.C.RED, + no_pips = true + })}} + } + }) + end + return t +end + +G.FUNCS.your_collection_seals_page = function(args) + if not args or not args.cycle_config then + return + end + local seal_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Seal) + local rows, cols = (#seal_pool > 5 and 2 or 1), 5 + local page = args.cycle_config.current_option + if page > math.ceil(#seal_pool / (rows * cols)) then + page = page - math.ceil(#seal_pool / (rows * cols)) + end + local count = rows * cols + local offset = (rows * cols) * (page - 1) + + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards, 1, -1 do + if G.your_collection[j] ~= nil then + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + end + + for j = 1, rows do + for i = 1, cols do + if count % rows > 0 and i <= count % rows and j == cols then + offset = offset - 1 + break + end + local idx = i + (j - 1) * cols + offset + if idx > #seal_pool then return end + local seal = seal_pool[idx] + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, + G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS.c_base) + card:set_seal(seal.key, true) + G.your_collection[j]:emplace(card) + end + end +end +--#endregion +function get_joker_win_sticker(_center, index) + local joker_usage = G.PROFILES[G.SETTINGS.profile].joker_usage[_center.key] or {} + if joker_usage.wins then + local applied = {} + local _count = 0 + local _stake = nil + for k, v in pairs(joker_usage.wins_by_key) do + SMODS.build_stake_chain(G.P_STAKES[k], applied) + end + for i, v in ipairs(G.P_CENTER_POOLS.Stake) do + if applied[v.order] then + _count = _count+1 + if (v.stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then + _stake = v.key + end + end + end + if index then return _count end + if _count > 0 then return G.sticker_map[_stake] end + end + if index then return 0 end +end + +function get_deck_win_stake(_deck_key) + if not _deck_key then + local _stake, _stake_low = nil, nil + local deck_count = 0 + for _, deck in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage) do + local deck_won_with = false + for key, _ in pairs(deck.wins_by_key or {}) do + deck_won_with = true + if (G.P_STAKES[key] and G.P_STAKES[key].stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then + _stake = key + end + end + if deck_won_with then deck_count = deck_count + 1 end + if not _stake_low then _stake_low = _stake end + if (_stake and G.P_STAKES[_stake] and G.P_STAKES[_stake].stake_level or 0) < (_stake_low and G.P_STAKES[_stake_low].stake_level or 0) then + _stake_low = _stake + end + end + return _stake and G.P_STAKES[_stake].order or 0, (deck_count >= #G.P_CENTER_POOLS.Back and G.P_STAKES[_stake_low].order or 0) + end + if G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key] and G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key].wins_by_key then + local _stake = nil + for key, _ in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key].wins_by_key) do + if (G.P_STAKES[key] and G.P_STAKES[key].stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then + _stake = key + end + end + if _stake then return G.P_STAKES[_stake].order end + end + return 0 +end + +function get_deck_win_sticker(_center) + if G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key] and + G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key].wins_by_key then + local _stake = nil + for key, _ in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key].wins_by_key) do + if (G.P_STAKES[key] and G.P_STAKES[key].stake_level or 0) > (_stake and G.P_STAKES[_stake].stake_level or 0) then + _stake = key + end + end + if _stake then return G.sticker_map[_stake] end + end +end + +function set_deck_win() + if G.GAME.selected_back and G.GAME.selected_back.effect and G.GAME.selected_back.effect.center and G.GAME.selected_back.effect.center.key then + local deck_key = G.GAME.selected_back.effect.center.key + local deck_usage = G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] + if not deck_usage then deck_usage = { count = 1, order = + G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {} } end + if deck_usage then + deck_usage.wins[G.GAME.stake] = (deck_usage.wins[G.GAME.stake] or 0) + 1 + deck_usage.wins_by_key[SMODS.stake_from_index(G.GAME.stake)] = (deck_usage.wins_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1 + local applied = SMODS.build_stake_chain(G.P_STAKES[SMODS.stake_from_index(G.GAME.stake)]) or {} + for i, v in ipairs(G.P_CENTER_POOLS.Stake) do + if applied[i] then + deck_usage.wins[i] = math.max(deck_usage.wins[i] or 0, 1) + deck_usage.wins_by_key[SMODS.stake_from_index(i)] = math.max(deck_usage.wins_by_key[SMODS.stake_from_index(i)] or 0, 1) + end + end + end + set_challenge_unlock() + G:save_settings() + G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = deck_usage + end +end + +function Card:align_h_popup() + local focused_ui = self.children.focused_ui and true or false + local popup_direction = (self.children.buy_button or (self.area and self.area.config.view_deck) or (self.area and self.area.config.type == 'shop')) and 'cl' or + (self.T.y < G.CARD_H*0.8) and 'bm' or + 'tm' + local sign = 1 + if popup_direction == 'cl' and self.T.x <= G.ROOM.T.w*0.4 then + popup_direction = 'cr' + sign = -1 + end + return { + major = self.children.focused_ui or self, + parent = self, + xy_bond = 'Strong', + r_bond = 'Weak', + wh_bond = 'Weak', + offset = { + x = popup_direction ~= 'cl' and popup_direction ~= 'cr' and 0 or + focused_ui and sign*-0.05 or + (self.ability.consumeable and 0.0) or + (self.ability.set == 'Voucher' and 0.0) or + sign*-0.05, + y = focused_ui and ( + popup_direction == 'tm' and (self.area and self.area == G.hand and -0.08 or-0.15) or + popup_direction == 'bm' and 0.12 or + 0 + ) or + popup_direction == 'tm' and -0.13 or + popup_direction == 'bm' and 0.1 or + 0 + }, + type = popup_direction, + --lr_clamp = true + } +end + +function get_pack(_key, _type) + if not G.GAME.first_shop_buffoon and not G.GAME.banned_keys['p_buffoon_normal_1'] then + G.GAME.first_shop_buffoon = true + return G.P_CENTERS['p_buffoon_normal_'..(math.random(1, 2))] + end + local cume, it, center = 0, 0, nil + local temp_in_pool = {} + for k, v in ipairs(G.P_CENTER_POOLS['Booster']) do + local add + v.current_weight = v.get_weight and v:get_weight() or v.weight or 1 + if (not _type or _type == v.kind) then add = true end + if v.in_pool and type(v.in_pool) == 'function' then + local res, pool_opts = v:in_pool() + pool_opts = pool_opts or {} + add = res and (add or pool_opts.override_base_checks) + end + if add and not G.GAME.banned_keys[v.key] then cume = cume + (v.current_weight or 1); temp_in_pool[v.key] = true end + end + local poll = pseudorandom(pseudoseed((_key or 'pack_generic')..G.GAME.round_resets.ante))*cume + for k, v in ipairs(G.P_CENTER_POOLS['Booster']) do + if temp_in_pool[v.key] then + it = it + (v.current_weight or 1) + if it >= poll and it - (v.current_weight or 1) <= poll then center = v; break end + end + end + if not center then center = G.P_CENTERS['p_buffoon_normal_1'] end return center +end + +--#region joker retrigger API +local cj = Card.calculate_joker +function Card:calculate_joker(context) + local ret, triggered = cj(self, context) + --Copy ret table before it gets modified + local _ret = ret + if type(ret) == 'table' then + _ret = {} + for k, v in pairs(ret) do + _ret[k] = v + end + else + _ret = ret and {} + end + --Apply editions + if (not _ret or not _ret.no_callback) and not context.no_callback then + if self.edition and self.edition.key and not context.retrigger_joker_check and not context.post_trigger then + local ed = SMODS.Centers[self.edition.key] + if ed.calculate and type(ed.calculate) == 'function' then + context.from_joker = true + context.joker_triggered = (_ret or triggered) + ed:calculate(self, context) + context.from_joker = nil + context.joker_triggered = nil + end + end + end + --Check for retrggering jokers + if (ret or triggered) and context and not context.retrigger_joker and not context.retrigger_joker_check and not context.post_trigger then + if type(ret) ~= 'table' then ret = {joker_repetitions = {0}} end + ret.joker_repetitions = {{}} + for i = 1, #G.jokers.cards do + ret.joker_repetitions[i] = ret.joker_repetitions[i] or {} + local check = G.jokers.cards[i]:calculate_joker{retrigger_joker_check = true, other_card = self, other_context = context, other_ret = _ret} + if type(check) == 'table' and check.repetitions then + for j = 1, check.repetitions do + ret.joker_repetitions[i][#ret.joker_repetitions[i]+1] = {message = check.message, card = check.card} + end + else + ret.joker_repetitions[i] = {} + end + if G.jokers.cards[i] == self and self.edition and self.edition.key then + if self.edition.retriggers then + for j = 1, self.edition.retriggers do + ret.joker_repetitions[i][#ret.joker_repetitions[i]+1] = { + message = localize('k_again_ex'), + card = self + } + end + end + local ed = SMODS.Centers[self.edition.key] + if ed.calculate and type(ed.calculate) == 'function' then + context.retrigger_edition_check = true + local check = ed:calculate(self, context) + context.retrigger_edition_check = nil + if type(check) == 'table' and check.repetitions then + for j = 1, check.repetitions do + ret.joker_repetitions[i][#ret.joker_repetitions[i]+1] = {message = check.message, card = check.card} + end + end + end + end + end + --do the retriggers + for z = 1, #ret.joker_repetitions do + if type(ret.joker_repetitions[z]) == 'table' and ret.joker_repetitions[z][1] then + for r = 1, #ret.joker_repetitions[z] do + if percent then percent = percent+percent_delta end + context.retrigger_joker = ret.joker_repetitions[z][r] + local _ret, _triggered = self:calculate_joker(context, callback) + if (_ret or _triggered) and not context.no_retrigger_anim then card_eval_status_text(ret.joker_repetitions[z][r].card, 'jokers', nil, nil, nil, ret.joker_repetitions[z][r]) end + end + end + end + context.retrigger_joker = nil + end + if (not _ret or not _ret.no_callback) and not context.no_callback then + if context.callback and type(context.callback) == 'function' then context.callback(context.blueprint_card or self, _ret, context.retrigger_joker) end + if (_ret or triggered) and not context.retrigger_joker_check and not context.post_trigger then + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker{blueprint_card = context.blueprint_card, post_trigger = true, other_joker = self, other_context = context, other_ret = _ret} + end + end + end + return ret, triggered +end +--#endregion diff --git a/Steamodded/src/ui.lua b/Steamodded/src/ui.lua new file mode 100644 index 0000000..8fc6d82 --- /dev/null +++ b/Steamodded/src/ui.lua @@ -0,0 +1,1797 @@ +SMODS.GUI = {} +SMODS.GUI.DynamicUIManager = {} + +function STR_UNPACK(str) + local chunk, err = loadstring(str) + if chunk then + setfenv(chunk, {}) -- Use an empty environment to prevent access to potentially harmful functions + local success, result = pcall(chunk) + if success then + return result + else + print("Error unpacking string: " .. result) + return nil + end + else + print("Error loading string: " .. err) + return nil + end +end + + +local gameMainMenuRef = Game.main_menu +function Game:main_menu(change_context) + gameMainMenuRef(self, change_context) + UIBox({ + definition = { + n = G.UIT.ROOT, + config = { + align = "cm", + colour = G.C.UI.TRANSPARENT_DARK + }, + nodes = { + { + n = G.UIT.T, + config = { + scale = 0.3, + text = MODDED_VERSION, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + config = { + align = "tri", + bond = "Weak", + offset = { + x = 0, + y = 0.3 + }, + major = G.ROOM_ATTACH + } + }) +end + +local gameUpdateRef = Game.update +function Game:update(dt) + if G.STATE ~= G.STATES.SPLASH and G.MAIN_MENU_UI then + local node = G.MAIN_MENU_UI:get_UIE_by_ID("main_menu_play") + + if node and not node.children.alert then + node.children.alert = UIBox({ + definition = create_UIBox_card_alert({ + text = localize('b_modded_version'), + no_bg = true, + scale = 0.4, + text_rot = -0.2 + }), + config = { + align = "tli", + offset = { + x = -0.1, + y = 0 + }, + major = node, + parent = node + } + }) + node.children.alert.states.collide.can = false + end + end + gameUpdateRef(self, dt) +end + +local function wrapText(text, maxChars) + local wrappedText = "" + local currentLineLength = 0 + + for word in text:gmatch("%S+") do + if currentLineLength + #word <= maxChars then + wrappedText = wrappedText .. word .. ' ' + currentLineLength = currentLineLength + #word + 1 + else + wrappedText = wrappedText .. '\n' .. word .. ' ' + currentLineLength = #word + 1 + end + end + + return wrappedText +end + +-- Helper function to concatenate author names +local function concatAuthors(authors) + if type(authors) == "table" then + return table.concat(authors, ", ") + end + return authors or localize('b_unknown') +end + + +SMODS.LAST_SELECTED_MOD_TAB = "mod_desc" +function create_UIBox_mods(args) + local mod = G.ACTIVE_MOD_UI + if not SMODS.LAST_SELECTED_MOD_TAB then SMODS.LAST_SELECTED_MOD_TAB = "mod_desc" end + + local mod_tabs = {} + table.insert(mod_tabs, buildModDescTab(mod)) + local additions_tab = buildAdditionsTab(mod) + if additions_tab then table.insert(mod_tabs, additions_tab) end + local credits_func = mod.credits_tab + if credits_func and type(credits_func) == 'function' then + table.insert(mod_tabs, { + label = localize("b_credits"), + chosen = SMODS.LAST_SELECTED_MOD_TAB == "credits" or false, + tab_definition_function = function(...) + SMODS.LAST_SELECTED_MOD_TAB = "credits" + return credits_func(...) + end + }) + end + local config_func = mod.config_tab + if config_func and type(config_func) == 'function' then + table.insert(mod_tabs, { + label = localize("b_config"), + chosen = SMODS.LAST_SELECTED_MOD_TAB == "config" or false, + tab_definition_function = function(...) + SMODS.LAST_SELECTED_MOD_TAB = "config" + return config_func(...) + end + }) + end + + local mod_has_achievement + for _, v in pairs(SMODS.Achievements) do + if v.mod.id == mod.id then mod_has_achievement = true end + end + if mod_has_achievement then table.insert(mod_tabs, + { + label = localize("b_achievements"), + chosen = SMODS.LAST_SELECTED_MOD_TAB == "achievements" or false, + tab_definition_function = function() + SMODS.LAST_SELECTED_MOD_TAB = "achievements" + return buildAchievementsTab(mod) + end + }) + end + + local custom_ui_func = mod.extra_tabs + if custom_ui_func and type(custom_ui_func) == 'function' then + local custom_tabs = custom_ui_func() + if next(custom_tabs) and #custom_tabs == 0 then custom_tabs = { custom_tabs } end + for i, v in ipairs(custom_tabs) do + local id = mod.id..'_'..i + v.chosen = (SMODS.LAST_SELECTED_MOD_TAB == id) or false + v.label = v.label or '' + local def = v.tab_definition_function + assert(def, ('Custom defined mod tab with label "%s" from mod with id %s is missing definition function'):format(v.label, mod.id)) + v.tab_definition_function = function(...) + SMODS.LAST_SELECTED_MOD_TAB = id + return def(...) + end + table.insert(mod_tabs, v) + end + end + + return (create_UIBox_generic_options({ + back_func = "mods_button", + contents = { + { + n = G.UIT.R, + config = { + padding = 0, + align = "tm" + }, + nodes = { + create_tabs({ + snap_to_nav = true, + colour = G.C.BOOSTER, + tabs = mod_tabs + }) + } + } + } + })) +end + +function buildModDescTab(mod) + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.REFRESH_ALERTS = nil + return true + end + })) + local label = mod.name + if (G.localization.descriptions.Mod or {})[mod.id] then + label = localize { type = 'name_text', set = 'Mod', key = mod.id } + end + return { + label = label, + chosen = SMODS.LAST_SELECTED_MOD_TAB == "mod_desc" or false, + tab_definition_function = function() + local modNodes = {} + local scale = 0.75 -- Scale factor for text + local maxCharsPerLine = 50 + + local wrappedDescription = wrapText(mod.description or '', maxCharsPerLine) + + local authors = localize('b_author' .. (#mod.author > 1 and 's' or '')) .. ': ' .. concatAuthors(mod.author) + + -- Authors names in blue + table.insert(modNodes, { + n = G.UIT.R, + config = { + padding = 0, + align = "cm", + r = 0.1, + emboss = 0.1, + outline = 1, + padding = 0.07 + }, + nodes = { + { + n = G.UIT.T, + config = { + text = authors, + shadow = true, + scale = scale * 0.65, + colour = G.C.BLUE, + } + } + } + }) + + -- Mod description + if (G.localization.descriptions.Mod or {})[mod.id] then + modNodes[#modNodes + 1] = {} + local loc_vars = mod.description_loc_vars and mod:description_loc_vars() or {} + localize { type = 'descriptions', key = loc_vars.key or mod.id, set = 'Mod', nodes = modNodes[#modNodes], vars = loc_vars.vars, scale = loc_vars.scale, text_colour = loc_vars.text_colour } + modNodes[#modNodes] = desc_from_rows(modNodes[#modNodes]) + modNodes[#modNodes].config.colour = loc_vars.background_colour or modNodes[#modNodes].config.colour + else + table.insert(modNodes, { + n = G.UIT.R, + config = { + padding = 0.2, + align = "cm" + }, + nodes = { + { + n = G.UIT.T, + config = { + text = wrappedDescription, + shadow = true, + scale = scale * 0.5, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }) + end + + local custom_ui_func = mod.custom_ui + if custom_ui_func and type(custom_ui_func) == 'function' then + custom_ui_func(modNodes) + end + + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 6, + align = "tm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = modNodes + } + end + } +end + +function buildAdditionsTab(mod) + local consumable_nodes = {} + for _, key in ipairs(SMODS.ConsumableType.ctype_buffer) do + local id = 'your_collection_'..key:lower()..'s' + local tally = modsCollectionTally(G.P_CENTER_POOLS[key]) + if tally.of > 0 then + consumable_nodes[#consumable_nodes+1] = UIBox_button({button = id, label = {localize('b_'..key:lower()..'_cards')}, count = tally, minw = 4, id = id, colour = G.C.SECONDARY_SET[key]}) + end + end + if #consumable_nodes > 3 then + consumable_nodes = { UIBox_button({ button = 'your_collection_consumables', label = {localize('b_stat_consumables'), localize{ type = 'variable', key = 'c_types', vars = {#consumable_nodes} } }, count = modsCollectionTally(G.P_CENTER_POOLS.Consumeables), minw = 4, minh = 4, id = 'your_collection_consumables', colour = G.C.FILTER }) } + end + + local leftside_nodes = {} + for _, v in ipairs { { k = 'Joker', minh = 1.7, scale = 0.6 }, { k = 'Back', b = 'decks' }, { k = 'Voucher' } } do + v.b = v.b or v.k:lower()..'s' + v.l = v.l or v.b + local tally = modsCollectionTally(G.P_CENTER_POOLS[v.k]) + if tally.of > 0 then + leftside_nodes[#leftside_nodes+1] = UIBox_button({button = 'your_collection_'..v.b, label = {localize('b_'..v.l)}, count = modsCollectionTally(G.P_CENTER_POOLS[v.k]), minw = 5, minh = v.minh, scale = v.scale, id = 'your_collection_'..v.b}) + end + end + if #consumable_nodes > 0 then + leftside_nodes[#leftside_nodes + 1] = { + n = G.UIT.R, + config = { align = "cm", padding = 0.1, r = 0.2, colour = G.C.BLACK }, + nodes = { + { + n = G.UIT.C, + config = { align = "cm", maxh = 2.9 }, + nodes = { + { n = G.UIT.T, config = { text = localize('k_cap_consumables'), scale = 0.45, colour = G.C.L_BLACK, vert = true, maxh = 2.2 } }, + } + }, + { n = G.UIT.C, config = { align = "cm", padding = 0.15 }, nodes = consumable_nodes } + } + } + end + + local rightside_nodes = {} + for _, v in ipairs { { k = 'Enhanced', b = 'enhancements', l = 'enhanced_cards'}, { k = 'Seal' }, { k = 'Edition' }, { k = 'Booster', l = 'booster_packs' }, { b = 'tags', p = G.P_TAGS }, { b = 'blinds', p = G.P_BLINDS, minh = 2.0 }, } do + v.b = v.b or v.k:lower()..'s' + v.l = v.l or v.b + v.p = v.p or G.P_CENTER_POOLS[v.k] + local tally = modsCollectionTally(v.p) + if tally.of > 0 then + rightside_nodes[#rightside_nodes+1] = UIBox_button({button = 'your_collection_'..v.b, label = {localize('b_'..v.l)}, count = modsCollectionTally(v.p), minw = 5, minh = v.minh, id = 'your_collection_'..v.b}) + end + end + local has_other_gameobjects = create_UIBox_Other_GameObjects() + if has_other_gameobjects then + rightside_nodes[#rightside_nodes+1] = UIBox_button({button = 'your_collection_other_gameobjects', label = {localize('k_other')}, minw = 5, id = 'your_collection_other_gameobjects', focus_args = {snap_to = true}}) + end + + local t = {n=G.UIT.R, config={align = "cm",padding = 0.2, minw = 7}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes = leftside_nodes }, + {n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes = rightside_nodes } + }} + + local modNodes = {} + table.insert(modNodes, t) + return (#leftside_nodes > 0 or #rightside_nodes > 0 ) and { + label = localize("b_additions"), + chosen = SMODS.LAST_SELECTED_MOD_TAB == "additions" or false, + tab_definition_function = function() + SMODS.LAST_SELECTED_MOD_TAB = "additions" + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 6, + align = "tm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = modNodes + } + end + } or nil +end + +-- Disable alerts when in Additions tab +local set_alerts_ref = set_alerts +function set_alerts() + if G.ACTIVE_MOD_UI then + else + set_alerts_ref() + end +end + +G.FUNCS.your_collection_other_gameobjects = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_Other_GameObjects(), + } +end + +function create_UIBox_Other_GameObjects() + local custom_gameobject_tabs = {{}} + local curr_height = 0 + local curr_col = 1 + local other_collections_tabs = {} + local smods_uibox_buttons = { + { + count = G.ACTIVE_MOD_UI and modsCollectionTally(SMODS.Stickers), --Returns nil outside of G.ACTIVE_MOD_UI but we don't use it anyways + button = UIBox_button({button = 'your_collection_stickers', label = {localize('b_stickers')}, count = G.ACTIVE_MOD_UI and modsCollectionTally(SMODS.Stickers), minw = 5, id = 'your_collection_stickers'}) + } + } + + if G.ACTIVE_MOD_UI then + for _, tab in pairs(smods_uibox_buttons) do + if tab.count.of > 0 then other_collections_tabs[#other_collections_tabs+1] = tab.button end + end + if G.ACTIVE_MOD_UI and G.ACTIVE_MOD_UI.custom_collection_tabs then + object_tabs = G.ACTIVE_MOD_UI.custom_collection_tabs() + for _, tab in ipairs(object_tabs) do + other_collections_tabs[#other_collections_tabs+1] = tab + end + end + else + for _, tab in pairs(smods_uibox_buttons) do + other_collections_tabs[#other_collections_tabs+1] = tab.button + end + for _, mod in pairs(SMODS.Mods) do + if mod.custom_collection_tabs and type(mod.custom_collection_tabs) == "function" then + object_tabs = mod.custom_collection_tabs() + for _, tab in ipairs(object_tabs) do + other_collections_tabs[#other_collections_tabs+1] = tab + end + end + end + end + + local custom_gameobject_rows = {} + if #other_collections_tabs > 0 then + for _, gameobject_tabs in ipairs(other_collections_tabs) do + table.insert(custom_gameobject_tabs[curr_col], gameobject_tabs) + curr_height = curr_height + gameobject_tabs.nodes[1].config.minh + if curr_height > 6 then --TODO: Verify that this is the ideal number + curr_height = 0 + curr_col = curr_col + 1 + custom_gameobject_tabs[curr_col] = {} + end + end + for _, v in ipairs(custom_gameobject_tabs) do + table.insert(custom_gameobject_rows, {n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes = v}) + end + + local t = {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.BLACK, padding = 0.1, emboss = 0.05, minw = 7}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.15}, nodes = custom_gameobject_rows} + }} + + return create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = {t}}) + else + return nil + end +end + +G.FUNCS.your_collection_consumables = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_consumables(), + } +end + +function create_UIBox_your_collection_consumables() + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = { + { n = G.UIT.C, config = { align = 'cm', minw = 11.5, minh = 6 }, nodes = { + { n = G.UIT.O, config = { id = 'consumable_collection', object = Moveable() },} + }}, + }}) + G.E_MANAGER:add_event(Event({func = function() + G.FUNCS.your_collection_consumables_page({ cycle_config = { current_option = 1 }}) + return true + end})) + return t +end + +G.FUNCS.your_collection_consumables_page = function(args) + if not args or not args.cycle_config then return end + if G.OVERLAY_MENU then + local uie = G.OVERLAY_MENU:get_UIE_by_ID('consumable_collection') + if uie then + if uie.config.object then + uie.config.object:remove() + end + uie.config.object = UIBox{ + definition = G.UIDEF.consumable_collection_page(args.cycle_config.current_option), + config = { align = 'cm', parent = uie} + } + end + end +end + +G.UIDEF.consumable_collection_page = function(page) + local nodes_per_page = 10 + local page_offset = nodes_per_page * ((page or 1) - 1) + local type_buf = {} + if G.ACTIVE_MOD_UI then + for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do + if modsCollectionTally(G.P_CENTER_POOLS[v]).of > 0 then type_buf[#type_buf + 1] = v end + end + else + type_buf = SMODS.ConsumableType.ctype_buffer + end + local center_options = {} + for i = 1, math.ceil(#type_buf / nodes_per_page) do + table.insert(center_options, + localize('k_page') .. + ' ' .. tostring(i) .. '/' .. tostring(math.ceil(#type_buf / nodes_per_page))) + end + local option_nodes = { create_option_cycle({ + options = center_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_collection_consumables_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = page or 1, + colour = G.C.RED, + no_pips = true + }) } + local function create_consumable_nodes(_start, _end) + local t = {} + for i = _start, _end do + local key = type_buf[i] + if not key then + if i == _start then break end + t[#t+1] = { n = G.UIT.R, config = { align ='cm', minh = 0.81 }, nodes = {}} + else + local id = 'your_collection_'..key:lower()..'s' + t[#t+1] = UIBox_button({button = id, label = {localize('b_'..key:lower()..'_cards')}, count = G.ACTIVE_MOD_UI and modsCollectionTally(G.P_CENTER_POOLS[key]) or G.DISCOVER_TALLIES[key:lower()..'s'], minw = 4, id = id, colour = G.C.SECONDARY_SET[key]}) + end + end + return t + end + + local t = { n = G.UIT.C, config = { align = 'cm' }, nodes = { + {n=G.UIT.R, config = {align="cm"}, nodes = { + {n=G.UIT.C, config={align = "tm", padding = 0.15}, nodes= create_consumable_nodes(page_offset + 1, page_offset + math.ceil(nodes_per_page/2))}, + {n=G.UIT.C, config={align = "tm", padding = 0.15}, nodes= create_consumable_nodes(page_offset+1+math.ceil(nodes_per_page/2), page_offset + nodes_per_page)}, + }}, + {n=G.UIT.R, config = {align="cm"}, nodes = option_nodes}, + }} + return t +end + +G.FUNCS.your_collection_stickers = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_stickers(), + } +end + +function create_UIBox_your_collection_stickers(exit) + local deck_tables = {} + local sticker_pool = SMODS.collection_pool(SMODS.Stickers) + local rows, cols = (#sticker_pool > 5 and 2 or 1), 5 + local page = 0 + + G.your_collection = {} + for j = 1, rows do + G.your_collection[j] = CardArea(G.ROOM.T.x + 0.2 * G.ROOM.T.w / 2, G.ROOM.T.h, 5.3 * G.CARD_W, 1.03 * G.CARD_H, + { + card_limit = cols, + type = 'title', + highlight_limit = 0, + collection = true + }) + table.insert(deck_tables, + {n = G.UIT.R, config = {align = "cm", padding = 0, no_fill = true}, nodes = { + {n = G.UIT.O, config = {object = G.your_collection[j]}}}} + ) + end + + local count = math.min(cols * rows, #sticker_pool) + local index = 1 + (rows * cols * page) + for j = 1, rows do + for i = 1, cols do + local center = sticker_pool[index] + + if not center then + break + end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, + G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS["c_base"]) + card.ignore_pinned = true -- Scuffed solution to ignoring the effect of pinned, I'll figure out something better later + center:apply(card, true) + G.your_collection[j]:emplace(card) + index = index + 1 + end + if index > count then + break + end + end + + local edition_options = {} + + local t = create_UIBox_generic_options({ + back_func = "your_collection_other_gameobjects", + snap_back = true, + contents = { + {n = G.UIT.R, config = {align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = + deck_tables}} + }) + + if #sticker_pool > rows * cols then + for i = 1, math.ceil(#sticker_pool / (rows * cols)) do + table.insert(edition_options, localize('k_page') .. ' ' .. tostring(i) .. '/' .. + tostring(math.ceil(#sticker_pool / (rows * cols)))) + end + t = create_UIBox_generic_options({ + back_func = "your_collection_other_gameobjects", + snap_back = true, + contents = { + {n = G.UIT.R, config = {align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes = + deck_tables}, + {n = G.UIT.R, config = {align = "cm"}, nodes = { + create_option_cycle({ + options = edition_options, + w = 4.5, + cycle_shoulders = true, + opt_callback = 'your_collection_stickers_page', + focus_args = { snap_to = true, nav = 'wide' }, + current_option = 1, + r = rows, + c = cols, + colour = G.C.RED, + no_pips = true + })}} + } + }) + end + return t +end + +G.FUNCS.your_collection_stickers_page = function(args) + if not args or not args.cycle_config then + return + end + local sticker_pool = SMODS.collection_pool(SMODS.Stickers) + local rows = (#sticker_pool > 5 and 2 or 1) + local cols = 5 + local page = args.cycle_config.current_option + if page > math.ceil(#sticker_pool / (rows * cols)) then + page = page - math.ceil(#sticker_pool / (rows * cols)) + end + local count = rows * cols + local offset = (rows * cols) * (page - 1) + + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards, 1, -1 do + if G.your_collection[j] ~= nil then + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + end + + for j = 1, rows do + for i = 1, cols do + if count % rows > 0 and i <= count % rows and j == cols then + offset = offset - 1 + break + end + local idx = i + (j - 1) * cols + offset + if idx > #sticker_pool then return end + local center = sticker_pool[idx] + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w / 2, G.your_collection[j].T.y, + G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS["c_base"]) + card.ignore_pinned = true -- Scuffed solution to ignoring the effect of pinned, I'll figure out something better later + center:apply(card, true) + G.your_collection[j]:emplace(card) + end + end +end + +function buildAchievementsTab(mod, current_page) + current_page = current_page or 1 + fetch_achievements() + local achievement_matrix = {{},{}} + local achievements_per_row = 3 + local achievements_pool = {} + for k, v in pairs(G.ACHIEVEMENTS) do + if v.mod and v.mod.id == mod.id then achievements_pool[#achievements_pool+1] = v end + end + + local achievement_tab = {} + for k, v in pairs(achievements_pool) do + achievement_tab[#achievement_tab+1] = v + end + + table.sort(achievement_tab, function(a, b) return (a.order or 1) < (b.order or 1) end) + + local row = 1 + local max_lines = 2 + for i = 1, achievements_per_row*2 do + local v = achievement_tab[i+((achievements_per_row*2)*(current_page-1))] + if not v then break end + local temp_achievement = Sprite(0,0,1.1,1.1,G.ASSET_ATLAS[v.atlas or "achievements"], v.earned and v.pos or {x=0, y=0}) + temp_achievement:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'} + }) + if i == 1 then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + G.CONTROLLER:snap_to{node = temp_achievement} + return true + end) + })) + end + temp_achievement.float = true + temp_achievement.states.hover.can = true + temp_achievement.states.drag.can = false + temp_achievement.states.collide.can = true + --temp_achievement.config = {blind = v, force_focus = true} + temp_achievement.hover = function() + if not G.CONTROLLER.dragging.target or G.CONTROLLER.using_touch then + if not temp_achievement.hovering and temp_achievement.states.visible then + temp_achievement.hovering = true + temp_achievement.hover_tilt = 3 + temp_achievement:juice_up(0.05, 0.02) + play_sound('chips1', math.random()*0.1 + 0.55, 0.12) + Node.hover(temp_achievement) + if temp_achievement.children.alert then + temp_achievement.children.alert:remove() + temp_achievement.children.alert = nil + v.alerted = true + G:save_progress() + end + end + end + temp_achievement.stop_hover = function() temp_achievement.hovering = false; Node.stop_hover(temp_achievement); temp_achievement.hover_tilt = 0 end + end + + -- Description + local achievement_text = {} + local maxCharsPerLine = 30 + local function wrapText(text, maxChars) + local wrappedText = {""} + local curr_line = 1 + local currentLineLength = 0 + + for word in text:gmatch("%S+") do + if currentLineLength + #word <= maxChars then + wrappedText[curr_line] = wrappedText[curr_line] .. word .. ' ' + currentLineLength = currentLineLength + #word + 1 + else + wrappedText[curr_line] = string.sub(wrappedText[curr_line], 0, -2) + curr_line = curr_line + 1 + wrappedText[curr_line] = "" + wrappedText[curr_line] = wrappedText[curr_line] .. word .. ' ' + currentLineLength = #word + 1 + end + end + + wrappedText[curr_line] = string.sub(wrappedText[curr_line], 0, -2) + return wrappedText + end + + local loc_target = (v.hidden_text and not v.earned) and {localize("hidden_achievement", 'achievement_descriptions')} or wrapText(localize(v.key, 'achievement_descriptions'), maxCharsPerLine) + local loc_name = (v.hidden_name and not v.earned) and localize("hidden_achievement", 'achievement_names') or localize(v.key, 'achievement_names') + + local ability_text = {} + if loc_target then + for k, v in ipairs(loc_target) do + ability_text[#ability_text + 1] = {n=G.UIT.R, config={align = "cm"}, nodes={{n=G.UIT.T, config={text = v, scale = 0.35, shadow = true, colour = G.C.WHITE}}}} + end + end + max_lines = math.max(max_lines, #ability_text) + achievement_text[#achievement_text + 1] = + {n=G.UIT.R, config={align = "cm", emboss = 0.05, r = 0.1, minw = 4, maxw = 4, padding = 0.05, colour = G.C.WHITE, minh = 0.4*max_lines+0.1}, nodes={ + ability_text[1] and {n=G.UIT.R, config={align = "cm", padding = 0.08, colour = G.C.GREY, r = 0.1, emboss = 0.05, minw = 3.9, maxw = 3.9, minh = 0.4*max_lines}, nodes=ability_text} or nil + }} + + table.insert(achievement_matrix[row], { + n = G.UIT.C, + config = { align = "cm", padding = 0.1 }, + nodes = { + {n=G.UIT.R, config = {align = "cm"}, nodes = { + {n=G.UIT.R, config = {align = "cm", padding = 0.1}, nodes = {{ n = G.UIT.O, config = { object = temp_achievement, focus_with_object = true }}}}, + { + n=G.UIT.R, config = {align = "cm", minw = 4, maxw = 4, padding = 0.05}, nodes = { + {n=G.UIT.R, config={align = "cm", emboss = 0.05, r = 0.1, padding = 0.1, minh = 0.6, colour = G.C.GREY}, nodes={ + {n=G.UIT.O, config={align = "cm", maxw = 3.8, object = DynaText({string = loc_name, maxw = 3.8, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, spacing = 1, bump = true, scale = 0.4})}}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes=achievement_text}, + }, + }, + }}, + }, + }) + if #achievement_matrix[row] == achievements_per_row then + row = row + 1 + achievement_matrix[row] = {} + max_lines = 2 + end + end + + local achievements_options = {} + for i = 1, math.ceil(#achievements_pool/(2*achievements_per_row)) do + table.insert(achievements_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#achievements_pool/(2*achievements_per_row)))) + end + + local t = { + {n=G.UIT.C, config={}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1 }, nodes=achievement_matrix[1]}, + {n=G.UIT.R, config={align = "cm", padding = 0.1 }, nodes=achievement_matrix[2]}, + create_option_cycle({options = achievements_options, w = 4.5, cycle_shoulders = true, opt_callback = 'achievments_tab_page', focus_args = {snap_to = true, nav = 'wide'},current_option = current_page, colour = G.C.RED, no_pips = true}) + }} + }} + }}} + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 6, + align = "tm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = t + } +end + +G.FUNCS.achievments_tab_page = function(args) + if not args or not args.cycle_config then return end + achievement_matrix = {{},{}} + + local tab_contents = G.OVERLAY_MENU:get_UIE_by_ID('tab_contents') + tab_contents.config.object:remove() + tab_contents.config.object = UIBox{ + definition = buildAchievementsTab(G.ACTIVE_MOD_UI, args.cycle_config.current_option), + config = {offset = {x=0,y=0}, parent = tab_contents, type = 'cm'} + } + tab_contents.UIBox:recalculate() +end + +-- TODO: Optimize this. +function modsCollectionTally(pool, set) + local set = set or nil + local obj_tally = {tally = 0, of = 0} + + for _, v in pairs(pool) do + if v.mod and G.ACTIVE_MOD_UI.id == v.mod.id then + if set then + if v.set and v.set == set then + obj_tally.of = obj_tally.of+1 + if v.discovered then + obj_tally.tally = obj_tally.tally+1 + end + end + else + obj_tally.of = obj_tally.of+1 + if v.discovered then + obj_tally.tally = obj_tally.tally+1 + end + end + end + end + + return obj_tally +end + +-- TODO: Make better solution +local UIBox_button_ref = UIBox_button +function UIBox_button(args) + local button = UIBox_button_ref(args) + button.nodes[1].config.count = args.count + return button +end + +function buildModtag(mod) + local tag_pos, tag_message, tag_atlas = { x = 0, y = 0 }, "load_success", mod.prefix and mod.prefix .. '_modicon' or 'modicon' + local specific_vars = {} + + if not mod.can_load then + tag_message = "load_failure" + tag_atlas = "mod_tags" + specific_vars = {} + if next(mod.load_issues.dependencies) then + tag_message = tag_message..'_d' + table.insert(specific_vars, concatAuthors(mod.load_issues.dependencies)) + end + if next(mod.load_issues.conflicts) then + tag_message = tag_message .. '_c' + table.insert(specific_vars, concatAuthors(mod.load_issues.conflicts)) + end + if mod.load_issues.outdated then tag_message = 'load_failure_o' end + if mod.load_issues.version_mismatch then + tag_message = 'load_failure_i' + specific_vars = {mod.load_issues.version_mismatch, MODDED_VERSION:gsub('-STEAMODDED', '')} + end + if mod.load_issues.main_file_not_found then + tag_message = 'load_failure_m' + specific_vars = {mod.main_file} + end + if mod.load_issues.prefix_conflict then + tag_message = 'load_failure_p' + local name = mod.load_issues.prefix_conflict + for _, m in ipairs(SMODS.mod_list) do + if m.id == mod.load_issues.prefix_conflict then + name = m.name or name + end + end + specific_vars = {name} + end + if mod.disabled then + tag_pos = {x = 1, y = 0} + tag_message = 'load_disabled' + end + end + + + local tag_sprite_tab = nil + + local tag_sprite = Sprite(0, 0, 0.8*1, 0.8*1, G.ASSET_ATLAS[tag_atlas] or G.ASSET_ATLAS['tags'], tag_pos) + tag_sprite.T.scale = 1 + tag_sprite_tab = {n= G.UIT.C, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={w=0.8*1, h=0.8*1, colour = G.C.BLUE, object = tag_sprite, focus_with_object = true}}, + }} + tag_sprite:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'}, + }) + tag_sprite.float = true + tag_sprite.states.hover.can = true + tag_sprite.states.drag.can = false + tag_sprite.states.collide.can = true + + tag_sprite.hover = function(_self) + if not G.CONTROLLER.dragging.target or G.CONTROLLER.using_touch then + if not _self.hovering and _self.states.visible then + _self.hovering = true + if _self == tag_sprite then + _self.hover_tilt = 3 + _self:juice_up(0.05, 0.02) + play_sound('paper1', math.random()*0.1 + 0.55, 0.42) + play_sound('tarot2', math.random()*0.1 + 0.55, 0.09) + end + tag_sprite.ability_UIBox_table = generate_card_ui({set = "Other", discovered = false, key = tag_message}, nil, specific_vars, 'Other', nil, false) + _self.config.h_popup = G.UIDEF.card_h_popup(_self) + _self.config.h_popup_config ={align = 'cl', offset = {x=-0.1,y=0},parent = _self} + Node.hover(_self) + if _self.children.alert then + _self.children.alert:remove() + _self.children.alert = nil + G:save_progress() + end + end + end + end + tag_sprite.stop_hover = function(_self) _self.hovering = false; Node.stop_hover(_self); _self.hover_tilt = 0 end + + tag_sprite:juice_up() + + return tag_sprite_tab +end + +-- Helper function to create a clickable mod box +local function createClickableModBox(modInfo, scale) + local function invert(c) + return {1-c[1], 1-c[2], 1-c[3], c[4]} + end + local col, text_col + if modInfo.should_enable == nil then + modInfo.should_enable = not modInfo.disabled + end + if SMODS.full_restart == nil then + SMODS.full_restart = 0 + end + if modInfo.can_load then + col = G.C.BOOSTER + elseif modInfo.disabled then + col = G.C.UI.BACKGROUND_INACTIVE + else + col = mix_colours(G.C.RED, G.C.UI.BACKGROUND_INACTIVE, 0.7) + text_col = G.C.TEXT_DARK + end + local label = { " " .. modInfo.name .. " " } + if modInfo.lovely_only then + label[2] = localize('b_lovely_mod') + else + label[2] = localize('b_by') .. concatAuthors(modInfo.author) .. " " + end + local but = UIBox_button { + label = label, + shadow = true, + scale = scale, + colour = col, + text_colour = text_col, + button = "openModUI_" .. modInfo.id, + minh = 0.8, + minw = 7 + } + if modInfo.lovely_only then + local config = but.nodes[1].nodes[2].nodes[1].config + config.colour = mix_colours(invert(col), G.C.UI.TEXT_INACTIVE, 0.8) + config.scale = scale * .8 + end + if modInfo.version ~= '0.0.0' then + table.insert(but.nodes[1].nodes[1].nodes, { + n = G.UIT.T, + config = { + text = ('(%s)'):format(modInfo.version), + scale = scale*0.8, + colour = mix_colours(invert(col), G.C.UI.TEXT_INACTIVE, 0.8), + shadow = true, + }, + }) + end + return { + n = G.UIT.R, + config = { padding = 0, align = "cm" }, + nodes = { + { + n = G.UIT.C, + config = { align = "cm" }, + nodes = { + buildModtag(modInfo) + } + }, + { + n = G.UIT.C, + config = { align = "cm", padding = 0.1 }, + nodes = {}, + }, + { n = G.UIT.C, config = { padding = 0, align = "cm" }, nodes = { but } }, + create_toggle({ + label = '', + ref_table = modInfo, + ref_value = 'should_enable', + col = true, + w = 0, + h = 0.5, + callback = ( + function(_set_toggle) + if not modInfo.should_enable then + NFS.write(modInfo.path .. '.lovelyignore', '') + else + NFS.remove(modInfo.path .. '.lovelyignore') + end + local toChange = 1 + if modInfo.should_enable == not modInfo.disabled then + toChange = -1 + end + SMODS.full_restart = SMODS.full_restart + toChange + end + ) + }), + }} + +end + +function G.FUNCS.openModsDirectory(options) + if not love.filesystem.exists("Mods") then + love.filesystem.createDirectory("Mods") + end + + love.system.openURL("file://"..love.filesystem.getSaveDirectory().."/Mods") +end + +function G.FUNCS.mods_buttons_page(options) + if not options or not options.cycle_config then + return + end +end + +function SMODS.load_mod_config(mod) + local s1, config = pcall(function() + return load(NFS.read(('config/%s.jkr'):format(mod.id)), ('=[SMODS %s "config"]'):format(mod.id))() + end) + local s2, default_config = pcall(function() + return load(NFS.read(('%sconfig.lua'):format(mod.path)), ('=[SMODS %s "default_config"]'):format(mod.id))() + end) + if not s1 or type(config) ~= 'table' then config = {} end + if not s2 or type(default_config) ~= 'table' then default_config = {} end + mod.config = {} + for k, v in pairs(default_config) do mod.config[k] = v end + for k, v in pairs(config) do mod.config[k] = v end + return mod.config +end +SMODS:load_mod_config() +function SMODS.save_mod_config(mod) + local success = pcall(function() + NFS.createDirectory('config') + assert(mod.config and next(mod.config)) + local serialized = 'return '..serialize(mod.config) + NFS.write(('config/%s.jkr'):format(mod.id), serialized) + end) + return success +end +function SMODS.save_all_config() + SMODS:save_mod_config() + for _, v in ipairs(SMODS.mod_list) do + if v.can_load then + local save_func = type(v.save_mod_config) == 'function' and v.save_mod_config or SMODS.save_mod_config + save_func(v) + end + end +end + +function G.FUNCS.exit_mods(e) + G.ACTIVE_MOD_UI = nil + SMODS.save_all_config() + if SMODS.full_restart and SMODS.full_restart ~= 0 then + -- launch a new instance of the game and quit the current one + SMODS.restart_game() + end + SMODS.IN_MODS_TAB = nil + if e then + -- This is only needed when back button is pressed + G.FUNCS.exit_overlay_menu(e) + end +end + +function create_UIBox_mods_button() + local scale = 0.75 + SMODS.browse_search = SMODS.browse_search or '' + return (create_UIBox_generic_options({ + back_func = 'exit_mods', + contents = { + { + n = G.UIT.R, + config = { + padding = 0, + align = "cm" + }, + nodes = { + create_tabs({ + snap_to_nav = true, + colour = G.C.BOOSTER, + tabs = { + { + label = localize('b_mods'), + chosen = true, + tab_definition_function = function() + return SMODS.GUI.DynamicUIManager.initTab({ + updateFunctions = { + modsList = G.FUNCS.update_mod_list, + }, + staticPageDefinition = SMODS.GUI.staticModListContent() + }) + end + }, + -- { + -- label = localize('b_browse'), + -- tab_definition_function = function() + -- return { + -- n = G.UIT.ROOT, + -- config = { + -- align = "cm", + -- padding = 0.05, + -- colour = G.C.CLEAR, + -- }, + -- nodes = { + -- { + -- n = G.UIT.C, + -- config = { align = 'cm' }, + -- nodes = { + -- { + -- n = G.UIT.R, + -- config = { align = 'cl' }, + -- nodes = { + -- create_text_input{ + -- prompt_text = localize('b_search_prompt'), + -- max_length = 50, + -- text_scale = 0.6, + -- w = 6, + -- h = 1, + -- ref_table = SMODS, + -- ref_value = "browse_search", + -- extended_corpus = true, + -- }, + -- UIBox_button{ + -- button = 'browse_search', + -- label = {localize('b_search_button')}, + -- minw = 3, + -- colour = G.C.RED + -- } + -- } + -- }, + -- { + -- n = G.UIT.R, + -- config = { align = 'cm', emboss = 0.05, colour = G.C.BLACK, minh=5, minw=10.5}, + -- nodes = { + -- { + -- n = G.UIT.O, + -- config = { align = 'cm', object = Moveable(), id = 'browse_mods'} + -- } + -- } + -- } + -- } + -- } + -- } + -- } + -- end, + -- }, + { + + label = localize('b_credits'), + tab_definition_function = function() + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 6, + align = "cm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = { + { + n = G.UIT.R, + config = { + padding = 0, + align = "cm" + }, + nodes = { + { + n = G.UIT.T, + config = { + text = localize('b_mod_loader'), + shadow = true, + scale = scale * 0.8, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + { + n = G.UIT.R, + config = { + padding = 0, + align = "cm" + }, + nodes = { + { + n = G.UIT.T, + config = { + text = localize('b_developed_by'), + shadow = true, + scale = scale * 0.8, + colour = G.C.UI.TEXT_LIGHT + } + }, + { + n = G.UIT.T, + config = { + text = "Steamo", + shadow = true, + scale = scale * 0.8, + colour = G.C.BLUE + } + } + } + }, + { + n = G.UIT.R, + config = { + padding = 0, + align = "cm" + }, + nodes = { + { + n = G.UIT.T, + config = { + text = localize('b_rewrite_by'), + shadow = true, + scale = scale * 0.8, + colour = G.C.UI.TEXT_LIGHT + } + }, + { + n = G.UIT.T, + config = { + text = "Aure", + shadow = true, + scale = scale * 0.8, + colour = G.C.BLUE + } + } + } + }, + { + n = G.UIT.R, + config = { + padding = 0.2, + align = "cm", + }, + nodes = { + UIBox_button({ + minw = 3.85, + button = "steamodded_github", + label = {localize('b_github_project')} + }) + } + }, + { + n = G.UIT.R, + config = { + padding = 0.2, + align = "cm" + }, + nodes = { + { + n = G.UIT.T, + config = { + text = localize('b_github_bugs_1')..'\n'..localize('b_github_bugs_2'), + shadow = true, + scale = scale * 0.5, + colour = G.C.UI.TEXT_LIGHT + } + }, + + } + }, + } + } + end + }, + { + label = localize('b_config'), + tab_definition_function = function() + return { + n = G.UIT.ROOT, + config = { + align = "cm", + padding = 0.05, + colour = G.C.CLEAR, + }, + nodes = { + create_toggle { + label = localize('b_disable_mod_badges'), + ref_table = SMODS.config, + ref_value = 'no_mod_badges', + }, + create_toggle { + label = localize('b_seeded_unlocks'), + info = {localize('b_seeded_unlocks_info')}, + ref_table = SMODS.config, + ref_value = 'seeded_unlocks', + }, + create_option_cycle { + w = 4.5, + scale = 0.8, + label = localize('b_achievements'), + options = localize('ml_achievement_settings'), + opt_callback = 'update_achievement_settings', + current_option = SMODS.config.achievements, + cycle_shoulders = true, + } + } + } + end + } + } + }) + } + } + } + })) +end + +G.FUNCS.update_achievement_settings = function(e) + local opt = (e.cycle_config or {}).current_option or 1 + SMODS.config.achievements = opt + G.F_NO_ACHIEVEMENTS = opt == 1 +end + +G.FUNCS.browse_search = function(e) + SMODS.fetch_index() + +end + +G.FUNCS.browse_mods_page = function(args) + local page = args.cycle_config and args.cycle_config.current_option or 1 +end + +function G.FUNCS.steamodded_github(e) + love.system.openURL("https://github.com/Steamopollys/Steamodded") +end + +function G.FUNCS.mods_button(e) + G.SETTINGS.paused = true + SMODS.LAST_SELECTED_MOD_TAB = nil + SMODS.IN_MODS_TAB = true + + G.FUNCS.overlay_menu({ + definition = create_UIBox_mods_button() + }) +end + +local create_UIBox_main_menu_buttonsRef = create_UIBox_main_menu_buttons +function create_UIBox_main_menu_buttons() + local modsButton = UIBox_button({ + id = "mods_button", + minh = 1.55, + minw = 1.85, + col = true, + button = "mods_button", + colour = G.C.BOOSTER, + label = {localize('b_mods_cap')}, + scale = 0.45 * 1.2 + }) + local menu = create_UIBox_main_menu_buttonsRef() + table.insert(menu.nodes[1].nodes[1].nodes, modsButton) + menu.nodes[1].nodes[1].config = {align = "cm", padding = 0.15, r = 0.1, emboss = 0.1, colour = G.C.L_BLACK, mid = true} + if SMODS.mod_button_alert then + G.E_MANAGER:add_event(Event({ + func = function() + if G.MAIN_MENU_UI then -- Wait until the ui is rendered before spawning the alert + UIBox{definition = create_UIBox_card_alert(), config = {align="tri", offset = {x = 0.05, y = -0.05}, major = G.MAIN_MENU_UI:get_UIE_by_ID('mods_button'), can_collide = false}} + return true + end + end, + blocking = false, + blockable = false + })) + end + return menu +end + +local create_UIBox_profile_buttonRef = create_UIBox_profile_button +function create_UIBox_profile_button() + local profile_menu = create_UIBox_profile_buttonRef() + profile_menu.nodes[1].config = {align = "cm", padding = 0.11, r = 0.1, emboss = 0.1, colour = G.C.L_BLACK} + return(profile_menu) +end + +-- Disable achievments and crash report upload +function initGlobals() + G.F_NO_ACHIEVEMENTS = SMODS.config.achievements == 1 + G.F_CRASH_REPORTS = false +end + +function G.FUNCS.update_mod_list(args) + if not args or not args.cycle_config then return end + SMODS.GUI.DynamicUIManager.updateDynamicAreas({ + ["modsList"] = SMODS.GUI.dynamicModListContent(args.cycle_config.current_option) + }) +end + +-- Same as Balatro base game code, but accepts a value to match against (rather than the index in the option list) +-- e.g. create_option_cycle({ current_option = 1 }) vs. SMODS.GUID.createOptionSelector({ current_option = "Page 1/2" }) +function SMODS.GUI.createOptionSelector(args) + args = args or {} + args.colour = args.colour or G.C.RED + args.options = args.options or { + 'Option 1', + 'Option 2' + } + + local current_option_index = 1 + for i, option in ipairs(args.options) do + if option == args.current_option then + current_option_index = i + break + end + end + args.current_option_val = args.options[current_option_index] + args.current_option = current_option_index + args.opt_callback = args.opt_callback or nil + args.scale = args.scale or 1 + args.ref_table = args.ref_table or nil + args.ref_value = args.ref_value or nil + args.w = (args.w or 2.5)*args.scale + args.h = (args.h or 0.8)*args.scale + args.text_scale = (args.text_scale or 0.5)*args.scale + args.l = '<' + args.r = '>' + args.focus_args = args.focus_args or {} + args.focus_args.type = 'cycle' + + local info = nil + if args.info then + info = {} + for k, v in ipairs(args.info) do + table.insert(info, {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + {n=G.UIT.T, config={text = v, scale = 0.3*args.scale, colour = G.C.UI.TEXT_LIGHT}} + }}) + end + info = {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes=info} + end + + local disabled = #args.options < 2 + local pips = {} + for i = 1, #args.options do + pips[#pips+1] = {n=G.UIT.B, config={w = 0.1*args.scale, h = 0.1*args.scale, r = 0.05, id = 'pip_'..i, colour = args.current_option == i and G.C.WHITE or G.C.BLACK}} + end + + local choice_pips = not args.no_pips and {n=G.UIT.R, config={align = "cm", padding = (0.05 - (#args.options > 15 and 0.03 or 0))*args.scale}, nodes=pips} or nil + + local t = + {n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, colour = G.C.CLEAR, id = args.id and (not args.label and args.id or nil) or nil, focus_args = args.focus_args}, nodes={ + {n=G.UIT.C, config={align = "cm",r = 0.1, minw = 0.6*args.scale, hover = not disabled, colour = not disabled and args.colour or G.C.BLACK,shadow = not disabled, button = not disabled and 'option_cycle' or nil, ref_table = args, ref_value = 'l', focus_args = {type = 'none'}}, nodes={ + {n=G.UIT.T, config={ref_table = args, ref_value = 'l', scale = args.text_scale, colour = not disabled and G.C.UI.TEXT_LIGHT or G.C.UI.TEXT_INACTIVE}} + }}, + args.mid and + {n=G.UIT.C, config={id = 'cycle_main'}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + args.mid + }}, + not disabled and choice_pips or nil + }} + or {n=G.UIT.C, config={id = 'cycle_main', align = "cm", minw = args.w, minh = args.h, r = 0.1, padding = 0.05, colour = args.colour,emboss = 0.1, hover = true, can_collide = true, on_demand_tooltip = args.on_demand_tooltip}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = args, ref_value = "current_option_val"}}, colours = {G.C.UI.TEXT_LIGHT},pop_in = 0, pop_in_rate = 8, reset_pop_in = true,shadow = true, float = true, silent = true, bump = true, scale = args.text_scale, non_recalc = true})}}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + }}, + not disabled and choice_pips or nil + }} + }}, + {n=G.UIT.C, config={align = "cm",r = 0.1, minw = 0.6*args.scale, hover = not disabled, colour = not disabled and args.colour or G.C.BLACK,shadow = not disabled, button = not disabled and 'option_cycle' or nil, ref_table = args, ref_value = 'r', focus_args = {type = 'none'}}, nodes={ + {n=G.UIT.T, config={ref_table = args, ref_value = 'r', scale = args.text_scale, colour = not disabled and G.C.UI.TEXT_LIGHT or G.C.UI.TEXT_INACTIVE}} + }}, + }} + + if args.cycle_shoulders then + t = + {n=G.UIT.R, config={align = "cm", colour = G.C.CLEAR}, nodes = { + {n=G.UIT.C, config={minw = 0.7,align = "cm", colour = G.C.CLEAR,func = 'set_button_pip', focus_args = {button = 'leftshoulder', type = 'none', orientation = 'cm', scale = 0.7, offset = {x = -0.1, y = 0}}}, nodes = {}}, + {n=G.UIT.C, config={id = 'cycle_shoulders', padding = 0.1}, nodes={t}}, + {n=G.UIT.C, config={minw = 0.7,align = "cm", colour = G.C.CLEAR,func = 'set_button_pip', focus_args = {button = 'rightshoulder', type = 'none', orientation = 'cm', scale = 0.7, offset = {x = 0.1, y = 0}}}, nodes = {}}, + }} + else + t = + {n=G.UIT.R, config={align = "cm", colour = G.C.CLEAR, padding = 0.0}, nodes = { + t + }} + end + if args.label or args.info then + t = {n=G.UIT.R, config={align = "cm", padding = 0.05, id = args.id or nil}, nodes={ + args.label and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = args.label, scale = 0.5*args.scale, colour = G.C.UI.TEXT_LIGHT}} + }} or nil, + t, + info, + }} + end + return t +end + +local function generateBaseNode(staticPageDefinition) + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 8, + align = "cm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = { + staticPageDefinition + } + } +end + +-- Initialize a tab with sections that can be updated dynamically (e.g. modifying text labels, showing additional UI elements after toggling buttons, etc.) +function SMODS.GUI.DynamicUIManager.initTab(args) + local updateFunctions = args.updateFunctions + local staticPageDefinition = args.staticPageDefinition + + for _, updateFunction in pairs(updateFunctions) do + G.E_MANAGER:add_event(Event({func = function() + updateFunction{cycle_config = {}} + return true + end})) + end + return generateBaseNode(staticPageDefinition) +end + +-- Call this to trigger an update for a list of dynamic content areas +function SMODS.GUI.DynamicUIManager.updateDynamicAreas(uiDefinitions) + for id, uiDefinition in pairs(uiDefinitions) do + local dynamicArea = G.OVERLAY_MENU:get_UIE_by_ID(id) + if dynamicArea and dynamicArea.config.object then + dynamicArea.config.object:remove() + dynamicArea.config.object = UIBox{ + definition = uiDefinition, + config = {offset = {x=0, y=0}, align = 'cm', parent = dynamicArea} + } + end + end +end + +local function recalculateModsList(page) + page = page or SMODS.LAST_VIEWED_MODS_PAGE or 1 + SMODS.LAST_VIEWED_MODS_PAGE = page + local modsPerPage = 4 + local startIndex = (page - 1) * modsPerPage + 1 + local endIndex = startIndex + modsPerPage - 1 + local totalPages = math.ceil(#SMODS.mod_list / modsPerPage) + local currentPage = localize('k_page') .. ' ' .. page .. "/" .. totalPages + local pageOptions = {} + for i = 1, totalPages do + table.insert(pageOptions, (localize('k_page') .. ' ' .. tostring(i) .. "/" .. totalPages)) + end + local showingList = #SMODS.mod_list > 0 + + return currentPage, pageOptions, showingList, startIndex, endIndex, modsPerPage +end + +-- Define the content in the pane that does not need to update +-- Should include OBJECT nodes that indicate where the dynamic content sections will be populated +-- EX: in this pane the 'modsList' node will contain the dynamic content which is defined in the function below +function SMODS.GUI.staticModListContent() + local scale = 0.75 + local currentPage, pageOptions, showingList = recalculateModsList() + return { + n = G.UIT.ROOT, + config = { + minh = 6, + r = 0.1, + minw = 10, + align = "tm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = { + -- row container + { + n = G.UIT.R, + config = { align = "cm", padding = 0.05 }, + nodes = { + -- column container + { + n = G.UIT.C, + config = { align = "cm", minw = 3, padding = 0.2, r = 0.1, colour = G.C.CLEAR }, + nodes = { + -- title row + { + n = G.UIT.R, + config = { + padding = 0.05, + align = "cm" + }, + nodes = { + { + n = G.UIT.T, + config = { + text = localize('b_mod_list'), + shadow = true, + scale = scale * 0.6, + colour = G.C.UI.TEXT_LIGHT + } + } + } + }, + + -- add some empty rows for spacing + { + n = G.UIT.R, + config = { align = "cm", padding = 0.05 }, + nodes = {} + }, + { + n = G.UIT.R, + config = { align = "cm", padding = 0.05 }, + nodes = {} + }, + { + n = G.UIT.R, + config = { align = "cm", padding = 0.05 }, + nodes = {} + }, + { + n = G.UIT.R, + config = { align = "cm", padding = 0.05 }, + nodes = {} + }, + + -- dynamic content rendered in this row container + -- list of 4 mods on the current page + { + n = G.UIT.R, + config = { + padding = 0.05, + align = "cm", + minh = 2, + minw = 4 + }, + nodes = { + {n=G.UIT.O, config={id = 'modsList', object = Moveable()}}, + } + }, + + -- another empty row for spacing + { + n = G.UIT.R, + config = { align = "cm", padding = 0.3 }, + nodes = {} + }, + + -- page selector + -- does not appear when list of mods is empty + showingList and SMODS.GUI.createOptionSelector({label = "", scale = 0.8, options = pageOptions, opt_callback = 'update_mod_list', no_pips = true, current_option = ( + currentPage + )}) or nil + } + }, + } + }, + } + } +end + +function SMODS.GUI.dynamicModListContent(page) + local scale = 0.75 + local _, __, showingList, startIndex, endIndex, modsPerPage = recalculateModsList(page) + + local modNodes = {} + + -- If no mods are loaded, show a default message + if showingList == false then + table.insert(modNodes, { + n = G.UIT.R, + config = { + padding = 0, + align = "cm" + }, + nodes = { + { + n = G.UIT.T, + config = { + text = localize('b_no_mods'), + shadow = true, + scale = scale * 0.5, + colour = G.C.UI.TEXT_DARK + } + } + } + }) + table.insert(modNodes, { + n = G.UIT.R, + config = { + padding = 0, + align = "cm", + }, + nodes = { + UIBox_button({ + label = { localize('b_open_mods_dir') }, + shadow = true, + scale = scale, + colour = G.C.BOOSTER, + button = "openModsDirectory", + minh = 0.8, + minw = 8 + }) + } + }) + else + local modCount = 0 + local id = 0 + + for _, condition in ipairs({ + function(m) return not m.can_load and not m.disabled end, + function(m) return m.can_load end, + function(m) return m.disabled end, + }) do + for _, modInfo in ipairs(SMODS.mod_list) do + if modCount >= modsPerPage then break end + if condition(modInfo) then + id = id + 1 + if id >= startIndex and id <= endIndex then + table.insert(modNodes, createClickableModBox(modInfo, scale * 0.5)) + modCount = modCount + 1 + end + end + end + end + end + + return { + n = G.UIT.C, + config = { + r = 0.1, + align = "cm", + padding = 0.2, + }, + nodes = modNodes + } +end + +G.FUNCS.SMODS_change_mipmap = function(args) + SMODS.config.graphics_mipmap_level = args.to_key + G:set_render_settings() + SMODS:save_mod_config() +end diff --git a/Steamodded/src/utils.lua b/Steamodded/src/utils.lua new file mode 100644 index 0000000..5b9ead4 --- /dev/null +++ b/Steamodded/src/utils.lua @@ -0,0 +1,813 @@ +--- STEAMODDED CORE +--- UTILITY FUNCTIONS +function inspect(table) + if type(table) ~= 'table' then + return "Not a table" + end + + local str = "" + for k, v in pairs(table) do + local valueStr = type(v) == "table" and "table" or tostring(v) + str = str .. tostring(k) .. ": " .. valueStr .. "\n" + end + + return str +end + +function inspectDepth(table, indent, depth) + if depth and depth > 5 then -- Limit the depth to avoid deep nesting + return "Depth limit reached" + end + + if type(table) ~= 'table' then -- Ensure the object is a table + return "Not a table" + end + + local str = "" + if not indent then indent = 0 end + + for k, v in pairs(table) do + local formatting = string.rep(" ", indent) .. tostring(k) .. ": " + if type(v) == "table" then + str = str .. formatting .. "\n" + str = str .. inspectDepth(v, indent + 1, (depth or 0) + 1) + elseif type(v) == 'function' then + str = str .. formatting .. "function\n" + elseif type(v) == 'boolean' then + str = str .. formatting .. tostring(v) .. "\n" + else + str = str .. formatting .. tostring(v) .. "\n" + end + end + + return str +end + +function inspectFunction(func) + if type(func) ~= 'function' then + return "Not a function" + end + + local info = debug.getinfo(func) + local result = "Function Details:\n" + + if info.what == "Lua" then + result = result .. "Defined in Lua\n" + else + result = result .. "Defined in C or precompiled\n" + end + + result = result .. "Name: " .. (info.name or "anonymous") .. "\n" + result = result .. "Source: " .. info.source .. "\n" + result = result .. "Line Defined: " .. info.linedefined .. "\n" + result = result .. "Last Line Defined: " .. info.lastlinedefined .. "\n" + result = result .. "Number of Upvalues: " .. info.nups .. "\n" + + return result +end + +function SMODS._save_d_u(o) + assert(not o._discovered_unlocked_overwritten) + o._d, o._u = o.discovered, o.unlocked + o._saved_d_u = true +end + +function SMODS.SAVE_UNLOCKS() + boot_print_stage("Saving Unlocks") + G:save_progress() + ------------------------------------- + local TESTHELPER_unlocks = false and not _RELEASE_MODE + ------------------------------------- + if not love.filesystem.getInfo(G.SETTINGS.profile .. '') then + love.filesystem.createDirectory(G.SETTINGS.profile .. + '') + end + if not love.filesystem.getInfo(G.SETTINGS.profile .. '/' .. 'meta.jkr') then + love.filesystem.append( + G.SETTINGS.profile .. '/' .. 'meta.jkr', 'return {}') + end + + convert_save_to_meta() + + local meta = STR_UNPACK(get_compressed(G.SETTINGS.profile .. '/' .. 'meta.jkr') or 'return {}') + meta.unlocked = meta.unlocked or {} + meta.discovered = meta.discovered or {} + meta.alerted = meta.alerted or {} + + G.P_LOCKED = {} + for k, v in pairs(G.P_CENTERS) do + if not v.wip and not v.demo then + if TESTHELPER_unlocks then + v.unlocked = true; v.discovered = true; v.alerted = true + end --REMOVE THIS + if not v.unlocked and meta.unlocked[k] then + v.unlocked = true + end + if not v.unlocked then + G.P_LOCKED[#G.P_LOCKED + 1] = v + end + if not v.discovered and meta.discovered[k] then + v.discovered = true + end + if v.discovered and meta.alerted[k] or v.set == 'Back' or v.start_alerted then + v.alerted = true + elseif v.discovered then + v.alerted = false + end + end + end + + table.sort(G.P_LOCKED, function (a, b) return a.order and b.order and a.order < b.order end) + + for k, v in pairs(G.P_BLINDS) do + v.key = k + if not v.wip and not v.demo then + if TESTHELPER_unlocks then v.discovered = true; v.alerted = true end --REMOVE THIS + if not v.discovered and meta.discovered[k] then + v.discovered = true + end + if v.discovered and meta.alerted[k] then + v.alerted = true + elseif v.discovered then + v.alerted = false + end + end + end + for k, v in pairs(G.P_TAGS) do + v.key = k + if not v.wip and not v.demo then + if TESTHELPER_unlocks then v.discovered = true; v.alerted = true end --REMOVE THIS + if not v.discovered and meta.discovered[k] then + v.discovered = true + end + if v.discovered and meta.alerted[k] then + v.alerted = true + elseif v.discovered then + v.alerted = false + end + end + end + for k, v in pairs(G.P_SEALS) do + v.key = k + if not v.wip and not v.demo then + if TESTHELPER_unlocks then + v.discovered = true; v.alerted = true + end --REMOVE THIS + if not v.discovered and meta.discovered[k] then + v.discovered = true + end + if v.discovered and meta.alerted[k] then + v.alerted = true + elseif v.discovered then + v.alerted = false + end + end + end + for _, t in ipairs{ + G.P_CENTERS, + G.P_BLINDS, + G.P_TAGS, + G.P_SEALS, + } do + for k, v in pairs(t) do + v._discovered_unlocked_overwritten = true + end + end +end + +function SMODS.process_loc_text(ref_table, ref_value, loc_txt, key) + local target = (type(loc_txt) == 'table') and + (loc_txt[G.SETTINGS.language] or loc_txt['default'] or loc_txt['en-us']) or loc_txt + if key and (type(target) == 'table') then target = target[key] end + if not (type(target) == 'string' or target and next(target)) then return end + ref_table[ref_value] = target +end + +local function parse_loc_file(file_name, force) + local loc_table = nil + if file_name:lower():match("%.json$") then + loc_table = assert(JSON.decode(NFS.read(file_name))) + else + loc_table = assert(loadstring(NFS.read(file_name)))() + end + local function recurse(target, ref_table) + if type(target) ~= 'table' then return end --this shouldn't happen unless there's a bad return value + for k, v in pairs(target) do + -- If the value doesn't exist *or* + -- force mode is on and the value is not a table, + -- change/add the thing + -- brings back compatibility with language patching mods + if (not ref_table[k] and type(k) ~= 'number') or (force and ((type(v) ~= 'table') or type(v[1]) == 'string')) then + ref_table[k] = v + else + recurse(v, ref_table[k]) + end + end + end + recurse(loc_table, G.localization) +end + +local function handle_loc_file(dir, language, force) + for k, v in ipairs({ dir .. language .. '.lua', dir .. language .. '.json' }) do + if NFS.getInfo(v) then + parse_loc_file(v, force) + break + end + end +end + +function SMODS.handle_loc_file(path) + local dir = path .. 'localization/' + handle_loc_file(dir, G.SETTINGS.language, true) + handle_loc_file(dir, 'default') + handle_loc_file(dir, 'en-us') +end + +function SMODS.insert_pool(pool, center, replace) + if replace == nil then replace = center.taken_ownership end + if replace then + for k, v in ipairs(pool) do + if v.key == center.key then + pool[k] = center + end + end + else + local prev_order = (pool[#pool] and pool[#pool].order) or 0 + if prev_order ~= nil then + center.order = prev_order + 1 + end + table.insert(pool, center) + end +end + +function SMODS.remove_pool(pool, key) + local j + for i, v in ipairs(pool) do + if v.key == key then j = i end + end + if j then return table.remove(pool, j) end +end + +function SMODS.juice_up_blind() + local ui_elem = G.HUD_blind:get_UIE_by_ID('HUD_blind_debuff') + for _, v in ipairs(ui_elem.children) do + v.children[1]:juice_up(0.3, 0) + end + G.GAME.blind:juice_up() +end + +function SMODS.eval_this(_card, effects) + if effects then + local extras = { mult = false, hand_chips = false } + if effects.mult_mod then + mult = mod_mult(mult + effects.mult_mod); extras.mult = true + end + if effects.chip_mod then + hand_chips = mod_chips(hand_chips + effects.chip_mod); extras.hand_chips = true + end + if effects.Xmult_mod then + mult = mod_mult(mult * effects.Xmult_mod); extras.mult = true + end + update_hand_text({ delay = 0 }, { chips = extras.hand_chips and hand_chips, mult = extras.mult and mult }) + if effects.message then + card_eval_status_text(_card, 'jokers', nil, nil, nil, effects) + end + end +end + +-- Change a card's suit, rank, or both. +-- Accepts keys for both objects instead of needing to build a card key yourself. +function SMODS.change_base(card, suit, rank) + if not card then return false end + local _suit = SMODS.Suits[suit or card.base.suit] + local _rank = SMODS.Ranks[rank or card.base.value] + if not _suit or not _rank then + sendWarnMessage(('Tried to call SMODS.change_base with invalid arguments: suit="%s", rank="%s"'):format(suit, rank), 'Util') + return false + end + card:set_base(G.P_CARDS[('%s_%s'):format(_suit.card_key, _rank.card_key)]) + return card +end + +-- Return an array of all (non-debuffed) jokers or consumables with key `key`. +-- Debuffed jokers count if `count_debuffed` is true. +-- This function replaces find_joker(); please use SMODS.find_card() instead +-- to avoid name conflicts with other mods. +function SMODS.find_card(key, count_debuffed) + local results = {} + if not G.jokers or not G.jokers.cards then return {} end + for k, v in pairs(G.jokers.cards) do + if v and type(v) == 'table' and v.config.center.key == key and (count_debuffed or not v.debuff) then + table.insert(results, v) + end + end + for k, v in pairs(G.consumeables.cards) do + if v and type(v) == 'table' and v.config.center.key == key and (count_debuffed or not v.debuff) then + table.insert(results, v) + end + end + return results +end + +function SMODS.create_card(t) + if not t.area and t.key and G.P_CENTERS[t.key] then + t.area = G.P_CENTERS[t.key].consumeable and G.consumeables or G.P_CENTERS[t.key].set == 'Joker' and G.jokers + end + if not t.area and not t.key and t.set and SMODS.ConsumableTypes[t.set] then + t.area = G.consumeables + end + SMODS.bypass_create_card_edition = t.no_edition + local _card = create_card(t.set, t.area, t.legendary, t.rarity, t.skip_materialize, t.soulable, t.key, t.key_append) + SMODS.bypass_create_card_edition = nil + + -- Should this be restricted to only cards able to handle these + -- or should that be left to the person calling SMODS.create_card to use it correctly? + if t.edition then _card:set_edition(t.edition) end + if t.enhancement then _card:set_ability(G.P_CENTERS[t.enhancement]) end + if t.seal then _card:set_seal(t.seal) end + if t.stickers then + for i, v in ipairs(t.stickers) do + if SMODS.Stickers[v]:should_apply(_card, t.area, true) then SMODS.Stickers[v]:apply(_card, true) end + end + end + + return _card +end + +function SMODS.debuff_card(card, debuff, source) + debuff = debuff or nil + source = source and tostring(source) or nil + if debuff == 'reset' then card.ability.debuff_sources = {}; return end + card.ability.debuff_sources = card.ability.debuff_sources or {} + card.ability.debuff_sources[source] = debuff + card:set_debuff() +end + +-- Recalculate whether a card should be debuffed +function SMODS.recalc_debuff(card) + G.GAME.blind:debuff_card(card) +end + +function SMODS.restart_game() + if love.system.getOS() ~= 'OS X' then + love.thread.newThread("os.execute(...)\n"):start('"' .. arg[-2] .. '" ' .. table.concat(arg, " ")) + else + os.execute('sh "/Users/$USER/Library/Application Support/Steam/steamapps/common/Balatro/run_lovely.sh" &') + end + + love.event.quit() +end + +function SMODS.create_mod_badges(obj, badges) + if not SMODS.config.no_mod_badges and obj and obj.mod and obj.mod.display_name and not obj.no_mod_badges then + local mods = {} + badges.mod_set = badges.mod_set or {} + if not badges.mod_set[obj.mod.id] and not obj.no_main_mod_badge then table.insert(mods, obj.mod) end + badges.mod_set[obj.mod.id] = true + if obj.dependencies then + for _, v in ipairs(obj.dependencies) do + local m = SMODS.Mods[v] + if not badges.mod_set[m.id] then + table.insert(mods, m) + badges.mod_set[m.id] = true + end + end + end + for i, mod in ipairs(mods) do + local mod_name = string.sub(mod.display_name, 1, 20) + local size = 0.9 + local font = G.LANG.font + local max_text_width = 2 - 2*0.05 - 4*0.03*size - 2*0.03 + local calced_text_width = 0 + -- Math reproduced from DynaText:update_text + for _, c in utf8.chars(mod_name) do + local tx = font.FONT:getWidth(c)*(0.33*size)*G.TILESCALE*font.FONTSCALE + 2.7*1*G.TILESCALE*font.FONTSCALE + calced_text_width = calced_text_width + tx/(G.TILESIZE*G.TILESCALE) + end + local scale_fac = + calced_text_width > max_text_width and max_text_width/calced_text_width + or 1 + badges[#badges + 1] = {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", colour = mod.badge_colour or G.C.GREEN, r = 0.1, minw = 2, minh = 0.36, emboss = 0.05, padding = 0.03*size}, nodes={ + {n=G.UIT.B, config={h=0.1,w=0.03}}, + {n=G.UIT.O, config={object = DynaText({string = mod_name or 'ERROR', colours = {mod.badge_text_colour or G.C.WHITE},float = true, shadow = true, offset_y = -0.05, silent = true, spacing = 1*scale_fac, scale = 0.33*size*scale_fac})}}, + {n=G.UIT.B, config={h=0.1,w=0.03}}, + }} + }} + end + end +end + +function SMODS.create_loc_dump() + local _old, _new = SMODS.dump_loc.pre_inject, G.localization + local _dump = {} + local function recurse(old, new, dump) + for k, _ in pairs(new) do + if type(new[k]) == 'table' then + dump[k] = {} + if not old[k] then + dump[k] = new[k] + else + recurse(old[k], new[k], dump[k]) + end + elseif old[k] ~= new[k] then + dump[k] = new[k] + end + end + end + recurse(_old, _new, _dump) + local function cleanup(dump) + for k, v in pairs(dump) do + if type(v) == 'table' then + cleanup(v) + if not next(v) then dump[k] = nil end + end + end + end + cleanup(_dump) + local str = 'return ' .. serialize(_dump) + NFS.createDirectory(SMODS.dump_loc.path..'localization/') + NFS.write(SMODS.dump_loc.path..'localization/dump.lua', str) +end + +-- Serializes an input table in valid Lua syntax +-- Keys must be of type number or string +-- Values must be of type number, boolean, string or table +function serialize(t, indent) + indent = indent or '' + local str = '{\n' + for k, v in ipairs(t) do + str = str .. indent .. '\t' + if type(v) == 'number' then + str = str .. v + elseif type(v) == 'boolean' then + str = str .. (v and 'true' or 'false') + elseif type(v) == 'string' then + str = str .. serialize_string(v) + elseif type(v) == 'table' then + str = str .. serialize(v, indent .. '\t') + else + -- not serializable + str = str .. 'nil' + end + str = str .. ',\n' + end + for k, v in pairs(t) do + if type(k) == 'string' then + str = str .. indent .. '\t' .. '[' .. serialize_string(k) .. '] = ' + + if type(v) == 'number' then + str = str .. v + elseif type(v) == 'boolean' then + str = str .. (v and 'true' or 'false') + elseif type(v) == 'string' then + str = str .. serialize_string(v) + elseif type(v) == 'table' then + str = str .. serialize(v, indent .. '\t') + else + -- not serializable + str = str .. 'nil' + end + str = str .. ',\n' + end + end + str = str .. indent .. '}' + return str +end + +function serialize_string(s) + return string.format("%q", s) +end + +-- Starting with `t`, insert any key-value pairs from `defaults` that don't already +-- exist in `t` into `t`. Modifies `t`. +-- Returns `t`, the result of the merge. +-- +-- `nil` inputs count as {}; `false` inputs count as a table where +-- every possible key maps to `false`. Therefore, +-- * `t == nil` is weak and falls back to `defaults` +-- * `t == false` explicitly ignores `defaults` +-- (This function might not return a table, due to the above) +function SMODS.merge_defaults(t, defaults) + if t == false then return false end + if defaults == false then return false end + + -- Add in the keys from `defaults`, returning a table + if defaults == nil then return t end + if t == nil then t = {} end + for k, v in pairs(defaults) do + if t[k] == nil then + t[k] = v + end + end + return t +end +V_MT = { + __eq = function(a, b) + local minorWildcard = a.minor == -2 or b.minor == -2 + local patchWildcard = a.patch == -2 or b.patch == -2 + local betaWildcard = a.rev == '~' or b.rev == '~' + return a.major == b.major and + (a.minor == b.minor or minorWildcard) and + (a.patch == b.patch or minorWildcard or patchWildcard) and + (a.rev == b.rev or minorWildcard or patchWildcard or betaWildcard) and + (betaWildcard or a.beta == b.beta) + end, + __le = function(a, b) + local b = { + major = b.major + (b.minor == -2 and 1 or 0), + minor = b.minor == -2 and 0 or (b.minor + (b.patch == -2 and 1 or 0)), + patch = b.patch == -2 and 0 or b.patch, + beta = b.beta, + rev = b.rev, + } + if b.beta == -1 and a.beta == 0 then return false end + if a.major ~= b.major then return a.major < b.major end + if a.minor ~= b.minor then return a.minor < b.minor end + if a.patch ~= b.patch then return a.patch < b.patch end + if a.beta ~= b.beta then return a.beta < b.beta end + return a.rev <= b.rev + end, + __lt = function(a, b) + return a <= b and not (a == b) + end, + __call = function(_, str) + str = str or '0.0.0' + local _, _, major, minorFull, minor, patchFull, patch, rev = string.find(str, '^(%d+)(%.?([%d%*]*))(%.?([%d%*]*))(.*)$') + local minorWildcard = string.match(minor, '%*') + local patchWildcard = string.match(patch, '%*') + if (minorFull ~= "" and minor == "") or (patchFull ~= "" and patch == "") then + sendWarnMessage('Trailing dot found in version "' .. str .. '".') + major, minor, patch = -1, 0, 0 + end + local t = { + major = tonumber(major), + minor = minorWildcard and -2 or tonumber(minor) or 0, + patch = patchWildcard and -2 or tonumber(patch) or 0, + rev = rev or '', + beta = rev and rev:sub(1,1) == '~' and -1 or 0 + } + return setmetatable(t, V_MT) + end +} +V = setmetatable({}, V_MT) +V_MT.__index = V +function V.is_valid(v, allow_wildcard) + if getmetatable(v) ~= V_MT then return false end + return(pcall(function() return V() <= v and (allow_wildcard or (v.minor ~= -2 and v.patch ~= -2 and v.rev ~= '~')) end)) +end + +-- Flatten the given arrays of arrays into one, then +-- add elements of each table to a new table in order, +-- skipping any duplicates. +function SMODS.merge_lists(...) + local t = {} + for _, v in ipairs({...}) do + for _, vv in ipairs(v) do + table.insert(t, vv) + end + end + local ret = {} + local seen = {} + for _, li in ipairs(t) do + assert(type(li) == 'table') + for _, v in ipairs(li) do + if not seen[v] then + ret[#ret+1] = v + seen[v] = true + end + end + end + return ret +end + +--#region Number formatting + +function round_number(num, precision) + precision = 10^(precision or 0) + + return math.floor(num * precision + 0.4999999999999994) / precision +end + +-- Formatting util for UI elements (look number_formatting.toml) +function format_ui_value(value) + if type(value) ~= "number" then + return tostring(value) + end + + return number_format(value, 1000000) +end + +--#endregion + + +function SMODS.poll_seal(args) + args = args or {} + local key = args.key or 'stdseal' + local mod = args.mod or 1 + local guaranteed = args.guaranteed or false + local options = args.options or get_current_pool("Seal") + local type_key = args.type_key or key.."type"..G.GAME.round_resets.ante + key = key..G.GAME.round_resets.ante + + local available_seals = {} + local total_weight = 0 + for _, v in ipairs(options) do + if v ~= "UNAVAILABLE" then + local seal_option = {} + if type(v) == 'string' then + assert(G.P_SEALS[v]) + seal_option = { key = v, weight = G.P_SEALS[v].weight or 5 } -- default weight set to 5 to replicate base game weighting + elseif type(v) == 'table' then + assert(G.P_SEALS[v.key]) + seal_option = { key = v.key, weight = v.weight } + end + if seal_option.weight > 0 then + table.insert(available_seals, seal_option) + total_weight = total_weight + seal_option.weight + end + end + end + total_weight = total_weight + (total_weight / 2 * 98) -- set base rate to 2% + + local type_weight = 0 -- modified weight total + for _,v in ipairs(available_seals) do + v.weight = G.P_SEALS[v.key].get_weight and G.P_SEALS[v.key]:get_weight() or v.weight + type_weight = type_weight + v.weight + end + + local seal_poll = pseudorandom(pseudoseed(key or 'stdseal'..G.GAME.round_resets.ante)) + if seal_poll > 1 - (type_weight*mod / total_weight) or guaranteed then -- is a seal generated + local seal_type_poll = pseudorandom(pseudoseed(type_key)) -- which seal is generated + local weight_i = 0 + for k, v in ipairs(available_seals) do + weight_i = weight_i + v.weight + if seal_type_poll > 1 - (weight_i / type_weight) then + return v.key + end + end + end +end + +function SMODS.get_blind_amount(ante) + local scale = G.GAME.modifiers.scaling + local amounts = { + 300, + 700 + 100*scale, + 1400 + 600*scale, + 2100 + 2900*scale, + 15000 + 5000*scale*math.log(scale), + 12000 + 8000*(scale+1)*(0.4*scale), + 10000 + 25000*(scale+1)*((scale/4)^2), + 50000 * (scale+1)^2 * (scale/7)^2 + } + + if ante < 1 then return 100 end + if ante <= 8 then return amounts[ante] - amounts[ante]%(10^math.floor(math.log10(amounts[ante])-1)) end + local a, b, c, d = amounts[8], amounts[8]/amounts[7], ante-8, 1 + 0.2*(ante-8) + local amount = math.floor(a*(b + (b*0.75*c)^d)^c) + amount = amount - amount%(10^math.floor(math.log10(amount)-1)) + return amount +end + +function SMODS.stake_from_index(index) + local stake = G.P_CENTER_POOLS.Stake[index] or nil + if not stake then return "error" end + return stake.key +end + +function convert_save_data() + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage) do + local first_pass = not v.wins_by_key and not v.losses_by_key + v.wins_by_key = v.wins_by_key or {} + for index, number in pairs(v.wins or {}) do + if index > 8 and not first_pass then break end + v.wins_by_key[SMODS.stake_from_index(index)] = number + end + v.losses_by_key = v.losses_by_key or {} + for index, number in pairs(v.losses or {}) do + if index > 8 and not first_pass then break end + v.losses_by_key[SMODS.stake_from_index(index)] = number + end + end + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].joker_usage) do + local first_pass = not v.wins_by_key and not v.losses_by_key + v.wins_by_key = v.wins_by_key or {} + for index, number in pairs(v.wins or {}) do + if index > 8 and not first_pass then break end + v.wins_by_key[SMODS.stake_from_index(index)] = number + end + v.losses_by_key = v.losses_by_key or {} + for index, number in pairs(v.losses or {}) do + if index > 8 and not first_pass then break end + v.losses_by_key[SMODS.stake_from_index(index)] = number + end + end + G:save_settings() +end + + +function SMODS.poll_rarity(_pool_key, _rand_key) + local rarity_poll = pseudorandom(pseudoseed(_rand_key or ('rarity'..G.GAME.round_resets.ante))) -- Generate the poll value + local available_rarities = copy_table(SMODS.ObjectTypes[_pool_key].rarities) -- Table containing a list of rarities and their rates + local vanilla_rarities = {["Common"] = 1, ["Uncommon"] = 2, ["Rare"] = 3, ["Legendary"] = 4} + + -- Calculate total rates of rarities + local total_weight = 0 + for _, v in ipairs(available_rarities) do + v.mod = G.GAME[tostring(v.key):lower().."_mod"] or 1 + -- Should this fully override the v.weight calcs? + if SMODS.Rarities[v.key] and SMODS.Rarities[v.key].get_weight and type(SMODS.Rarities[v.key].get_weight) == "function" then + v.weight = SMODS.Rarities[v.key]:get_weight(v.weight, SMODS.ObjectTypes[_pool_key]) + end + v.weight = v.weight*v.mod + total_weight = total_weight + v.weight + end + -- recalculate rarities to account for v.mod + for _, v in ipairs(available_rarities) do + v.weight = v.weight / total_weight + end + + -- Calculate selected rarity + local weight_i = 0 + for _, v in ipairs(available_rarities) do + weight_i = weight_i + v.weight + if rarity_poll < weight_i then + if vanilla_rarities[v.key] then + return vanilla_rarities[v.key] + else + return v.key + end + end + end + return nil +end + +function SMODS.poll_enhancement(args) + args = args or {} + local key = args.key or 'std_enhance' + local mod = args.mod or 1 + local guaranteed = args.guaranteed or false + local options = args.options or get_current_pool("Enhanced") + local type_key = args.type_key or key.."type"..G.GAME.round_resets.ante + key = key..G.GAME.round_resets.ante + + local available_enhancements = {} + local total_weight = 0 + for _, v in ipairs(options) do + if v ~= "UNAVAILABLE" then + local enhance_option = {} + if type(v) == 'string' then + assert(G.P_CENTERS[v]) + enhance_option = { key = v, weight = G.P_CENTERS[v].weight or 5 } -- default weight set to 5 to replicate base game weighting + elseif type(v) == 'table' then + assert(G.P_CENTERS[v.key]) + enhance_option = { key = v.key, weight = v.weight } + end + if enhance_option.weight > 0 then + table.insert(available_enhancements, enhance_option) + total_weight = total_weight + enhance_option.weight + end + end + end + total_weight = total_weight + (total_weight / 40 * 60) -- set base rate to 40% + + local type_weight = 0 -- modified weight total + for _,v in ipairs(available_enhancements) do + v.weight = G.P_CENTERS[v.key].get_weight and G.P_CENTERS[v.key]:get_weight() or v.weight + type_weight = type_weight + v.weight + end + + local enhance_poll = pseudorandom(pseudoseed(key)) + if enhance_poll > 1 - (type_weight*mod / total_weight) or guaranteed then -- is an enhancement selected + local seal_type_poll = pseudorandom(pseudoseed(type_key)) -- which enhancement is selected + local weight_i = 0 + for k, v in ipairs(available_enhancements) do + weight_i = weight_i + v.weight + if seal_type_poll > 1 - (weight_i / type_weight) then + return v.key + end + end + end +end + +function time(func, ...) + local start_time = love.timer.getTime() + func(...) + local end_time = love.timer.getTime() + return 1000*(end_time-start_time) +end + +SMODS.collection_pool = function(_base_pool) + local pool = {} + if type(_base_pool) ~= 'table' then return pool end + local is_array = _base_pool[1] + local ipairs = is_array and ipairs or pairs + for _, v in ipairs(_base_pool) do + if (not G.ACTIVE_MOD_UI or v.mod == G.ACTIVE_MOD_UI) and not v.no_collection then + pool[#pool+1] = v + end + end + if not is_array then table.sort(pool, function(a,b) return a.order < b.order end) end + return pool +end \ No newline at end of file diff --git a/Steamodded/tk_debug_window.py b/Steamodded/tk_debug_window.py new file mode 100644 index 0000000..f8e4c61 --- /dev/null +++ b/Steamodded/tk_debug_window.py @@ -0,0 +1,649 @@ +import re +import socket +import string +import threading +import tkinter as tk +from datetime import datetime +from tkinter import filedialog + +log_levels = { + "TRACE": 0, + "DEBUG": 1, + "INFO ": 2, + "WARN ": 3, + "ERROR": 4, + "FATAL": 5 +} + + +# might or might not be a copy paste from https://stackoverflow.com/a/16375233 +class TextLineNumbers(tk.Canvas): + def __init__(self, *args, **kwargs): + tk.Canvas.__init__(self, *args, **kwargs, highlightthickness=0) + self.textwidget = None + + def attach(self, text_widget): + self.textwidget = text_widget + + def redraw(self, *args): + '''redraw line numbers''' + self.delete("all") + + i = self.textwidget.index("@0,0") + while True: + dline = self.textwidget.dlineinfo(i) + if dline is None: + break + y = dline[1] + linenum = str(i).split(".")[0] + self.create_text(2, y, anchor="nw", text=linenum, fill="#606366") + i = self.textwidget.index("%s+1line" % i) + + +class CustomText(tk.Text): + def __init__(self, *args, **kwargs): + tk.Text.__init__(self, *args, **kwargs) + + # create a proxy for the underlying widget + self._orig = self._w + "_orig" + self.tk.call("rename", self._w, self._orig) + self.tk.createcommand(self._w, self._proxy) + + def _proxy(self, *args): + # let the actual widget perform the requested action + cmd = (self._orig,) + args + result = self.tk.call(cmd) + + # generate an event if something was added or deleted, + # or the cursor position changed + if (args[0] in ("insert", "replace", "delete") or + args[0:3] == ("mark", "set", "insert") or + args[0:2] == ("xview", "moveto") or + args[0:2] == ("xview", "scroll") or + args[0:2] == ("yview", "moveto") or + args[0:2] == ("yview", "scroll") + ): + self.event_generate("<>", when="tail") + + # return what the actual widget returned + return result + + +class Log: + def __init__(self, log: str): + self.parse_error = False + log_parts = log.split(" :: ") + if len(log_parts) == 4: + self.timestamp_str = log_parts[0] + self.log_level = log_parts[1] + self.logger = log_parts[2] + self.log_str = log_parts[3] + elif len(log_parts) == 3: + self.timestamp_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') + self.logger = log_parts[0] + self.log_str = log_parts[1] + self.log_str = log_parts[2] + else: + self.parse_error = True + self.timestamp_str = datetime.now().strftime('%Y-%m-%d %H:%M:%S.%f') + self.log_level = "DEBUG" + self.logger = "DefaultLogger" + self.log_str = log + + def __str__(self): + if not self.parse_error: + return f"{self.timestamp_str} :: {self.log_level} :: {self.logger} :: {self.log_str}\n" + return f"{self.timestamp_str} :: {self.log_str}\n" + + +class PlaceholderEntry(tk.Entry): + def __init__(self, *args, placeholder="", **kwargs): + super().__init__(*args, **kwargs) + self.placeholder = placeholder + self.user_has_interacted = False + self.insert(0, self.placeholder) + self.word_pattern = re.compile(r'[\s\W]|$') + self.config(fg='grey') + self.bind('', self.on_focus_out) + self.bind('', self.on_focus_in) + self.bind('', self.handle_ctrl_backspace) + self.bind('', self.handle_ctrl_delete) + self.bind('', self.on_key_press) # Bind key press event + + def on_focus_in(self, event): + if not self.user_has_interacted and self.get() == self.placeholder: + self.delete(0, 'end') + self.config(fg='black') + + def on_focus_out(self, event): + if not self.get(): + self.insert(0, self.placeholder) + self.config(fg='grey') + self.user_has_interacted = False # Reset flag if entry is empty + else: + self.user_has_interacted = True + + def on_key_press(self, event): + self.user_has_interacted = True # User has interacted when any key is pressed + + def reset_interaction_flag(self): + self.user_has_interacted = False + + def handle_ctrl_backspace(self, event: tk.Event): + # Get the current content of the entry and the cursor position + content = self.get() + cursor_pos = self.index(tk.INSERT) + + # If the last character before the cursor is a space or punctuation, delete it + if cursor_pos > 0 and (content[cursor_pos - 1] == ' ' or content[cursor_pos - 1] in string.punctuation): + self.delete(cursor_pos - 1, tk.INSERT) + return "break" # Prevent default behavior + + # Find the start of the word to the left of the cursor + pre_cursor = content[:cursor_pos] + match = self.word_pattern.search(pre_cursor[::-1]) # [\s\W]|$ matches spaces, punctuation, or end of string + word_start = cursor_pos - match.start() if match else 0 + + # Delete the word + self.delete(word_start, cursor_pos) + return "break" # Prevent default behavior + + def handle_ctrl_delete(self, event: tk.Event): + # Get the current content of the entry and the cursor position + content = self.get() + cursor_pos = self.index(tk.INSERT) + + # If the first character after the cursor is a space or punctuation, delete it + if len(content) > cursor_pos and (content[cursor_pos] == ' ' or content[cursor_pos] in string.punctuation): + self.delete(cursor_pos, cursor_pos + 1) + return "break" # Prevent default behavior + + # Find the end of the word to the right of the cursor + post_cursor = content[cursor_pos:] + match = self.word_pattern.search(post_cursor) # [\s\W]|$ matches spaces, punctuation, or end of string + word_end = match.start() if match else len(post_cursor) + + # Delete the word + self.delete(cursor_pos, cursor_pos + word_end) + return "break" # Prevent default behavior + + +class OptionsFrame(tk.Frame): + def __init__(self, parent): + super().__init__(parent) + self.global_search_frame = GlobalSearchFrame(self, parent) + self.specific_search_frame = SpecificSearchFrame(self, parent) + self.create_widgets() + + def inject_console(self, console): + self.global_search_frame.inject_console(console) + self.specific_search_frame.inject_console(console) + + def create_widgets(self): + self.global_search_frame.pack(side=tk.TOP, fill='x', expand=True) + self.specific_search_frame.pack(side=tk.BOTTOM, fill='x', expand=True) + + +class GlobalSearchFrame(tk.Frame): + def __init__(self, parent, root): + super().__init__(parent) + + self.after_id = None + self.root = root + self.console = None + + # Global search entry + self.search_entry_placeholder = "Search" + self.search_entry_var = tk.StringVar() + self.search_entry = PlaceholderEntry( + self, + placeholder=self.search_entry_placeholder, + textvariable=self.search_entry_var + ) + self.search_entry_var.trace("w", self.on_entry_changed) + self.search_entry.bind('', lambda event: self.console.text_widget.focus()) + self.search_entry.config(fg='grey') + + self.search_modes = [] + self.search_mode_var = tk.StringVar(value='normal') + self.search_mode_var.trace("w", self.apply_search_mode) + for mode, text in [('normal', 'normal'), ('match_case', 'match case'), ('regex', 'regex')]: + self.search_modes.append(tk.Radiobutton(self, text=text, variable=self.search_mode_var, value=mode)) + + self.create_widgets() + + def apply_search_mode(self, *args): + self.console.set_filter(global_search_mode=self.search_mode_var.get()) + + def inject_console(self, console): + self.console = console + + def create_widgets(self): + self.search_entry.pack(side=tk.LEFT, fill='x', expand=True, padx=(5, 0)) + for mode in self.search_modes: + mode.pack(side=tk.LEFT, padx=(5, 0)) + self.search_entry.bind('', lambda event: self.console.next_occurrence()) + self.search_entry.bind('', lambda event: self.console.previous_occurrence()) + + def on_entry_changed(self, *args): + if self.after_id: + self.root.after_cancel(self.after_id) + self.after_id = self.root.after(300, self.apply_search_entry_var) + + def apply_search_entry_var(self): + self.console.set_filter(global_search_str=self.search_entry_var.get()) + self.after_id = None + + +class Console(tk.Frame): + def __init__(self, parent, option_frame: OptionsFrame): + super().__init__(parent) + self.global_search_mode = "normal" + self.all_logs = [] + self.shown_logs = [] + self.option_frame = option_frame + self.text_widget = CustomText(self) + self.linenumbers = TextLineNumbers(self, width=30) + self.linenumbers.attach(self.text_widget) + self.text_widget.bind("<>", self._on_change) + self.text_widget.bind("", self._on_change) + self.scrollbar = tk.Scrollbar(self, command=self.text_widget.yview) + self.global_search_str = "" + self.logger_name = "" + self.log_level = "TRACE" + self.and_above = True + self.create_widgets() + + def _on_change(self, event): + self.linenumbers.redraw() + + def create_widgets(self): + self.scrollbar.pack(side=tk.RIGHT, fill='y') + self.linenumbers.pack(side=tk.LEFT, fill="y") + self.text_widget.pack(side=tk.LEFT, expand=True, fill='both') + self.text_widget.config(yscrollcommand=self.scrollbar.set) + self.text_widget.config(state=tk.DISABLED) + + def set_filter( + self, + global_search_str: str = None, + global_search_mode: str = None, + logger_name: str = None, + log_level: str = None, + and_above: bool = None + ): + if global_search_str is not None and self.option_frame.global_search_frame.search_entry.user_has_interacted: + self.global_search_str = global_search_str + + if logger_name is not None and self.option_frame.specific_search_frame.logger_entry.user_has_interacted: + self.logger_name = logger_name + + if global_search_mode is not None: + self.global_search_mode = global_search_mode + + if log_level is not None: + self.log_level = log_level + + if and_above is not None: + self.and_above = and_above + + self.apply_filters() + + def append_log(self, log: str): + log_obj = Log(log) + self.all_logs.append(log_obj) + if self.filter_log(log_obj): + self.shown_logs.append(log_obj) + # Check if the user is at the end before appending + at_end = self.text_widget.yview()[1] == 1.0 + self.text_widget.config(state=tk.NORMAL) + self.text_widget.insert(tk.END, str(log_obj)) + self.text_widget.config(state=tk.DISABLED) + if at_end: + self.text_widget.see(tk.END) + if self.global_search_str: + self.search_text() + + def clear_logs(self): + self.text_widget.config(state=tk.NORMAL) + self.text_widget.delete('1.0', tk.END) + self.text_widget.config(state=tk.DISABLED) + self.shown_logs.clear() + self.all_logs.clear() + self.apply_filters() + + def apply_filters(self): + # Re-filter all logs and update the text widget only if necessary + filtered_logs = [log for log in self.all_logs if self.filter_log(log)] + self.shown_logs = filtered_logs + self.update_text_widget() + + def filter_log(self, log): + # print(self.global_search_str, self.global_search_mode, self.logger_name, self.log_level, self.and_above) + if self.and_above: + flag = log_levels[log.log_level] >= log_levels[self.log_level] + else: + flag = log.log_level == self.log_level + + if self.logger_name: + flag = flag and self.logger_name in log.logger + + return flag + + def update_text_widget(self): + # Preserve the current view position unless at the end + at_end = self.text_widget.yview()[1] == 1.0 + self.text_widget.config(state=tk.NORMAL) + self.text_widget.delete('1.0', tk.END) + self.text_widget.config(state=tk.DISABLED) + for log in self.shown_logs: + self.text_widget.config(state=tk.NORMAL) + self.text_widget.insert(tk.END, str(log)) + self.text_widget.config(state=tk.DISABLED) + if at_end: + self.text_widget.see(tk.END) + + if self.global_search_str: + self.search_text() + + def search_text(self): + self.text_widget.tag_remove('found', '1.0', tk.END) + search_query = self.global_search_str.strip() + if not search_query: + return + + if self.global_search_mode == 'match_case': + pattern = re.escape(search_query) + elif self.global_search_mode == 'regex': + # Directly use the user input for regex, but be cautious of Tkinter's limited regex support + pattern = search_query + else: # normal mode, make it case-insensitive + pattern = '(?i)' + re.escape(search_query) # Add (?i) for case-insensitive search in Tkinter + + start = '1.0' + while True: + match_start = self.text_widget.search(pattern, start, tk.END, regexp=True) + if not match_start: + break + match_end = f"{match_start}+{len(search_query)}c" + self.text_widget.tag_add('found', match_start, match_end) + start = match_end + + self.text_widget.tag_config('found', background='yellow') + at_end = self.text_widget.yview()[1] == 1.0 + if at_end: + first_occurrence = self.text_widget.tag_ranges('found') + if first_occurrence: + self.text_widget.see(first_occurrence[0]) + self.next_occurrence() + + def prepare_occurrence_navigation(self): + current_tags = self.text_widget.tag_ranges('found') + if not current_tags: + return None, None + + # Ensure the 'current_found' tag exists with a blue background. + self.text_widget.tag_config('current_found', background='#ADD8E6') + + # Get the current position of the cursor in the text widget. + cursor_index = self.text_widget.index(tk.INSERT) + + # Remove the 'current_found' tag from the entire text widget. + self.text_widget.tag_remove('current_found', '1.0', tk.END) + + # Convert the current cursor index to a comparable value. + cursor_line, cursor_char = map(int, cursor_index.split('.')) + + return current_tags, (cursor_line, cursor_char) + + def next_occurrence(self): + current_tags, cursor_position = self.prepare_occurrence_navigation() + if not current_tags or not cursor_position: + return + + cursor_line, cursor_char = cursor_position + + for i in range(0, len(current_tags), 2): + tag_start = current_tags[i] + tag_end = current_tags[i + 1] + + # Convert tag start index to comparable values. + tag_start_line, tag_start_char = map(int, str(tag_start).split('.')) + + # Check if the tag start is greater than the cursor position. + if tag_start_line > cursor_line or (tag_start_line == cursor_line and tag_start_char > cursor_char): + self.text_widget.mark_set(tk.INSERT, tag_start) + self.text_widget.see(tag_start) + + # Apply the 'current_found' tag to the current occurrence. + self.text_widget.tag_add('current_found', tag_start, tag_end) + break + else: + # Wrap to the first tag if no next tag is found. + self.text_widget.mark_set(tk.INSERT, str(current_tags[0])) + self.text_widget.see(str(current_tags[0])) + self.text_widget.tag_add('current_found', current_tags[0], current_tags[1]) + + def previous_occurrence(self): + current_tags, cursor_position = self.prepare_occurrence_navigation() + if not current_tags or not cursor_position: + return + + cursor_line, cursor_char = cursor_position + + for i in range(len(current_tags) - 2, -1, -2): + tag_start = current_tags[i] + tag_end = current_tags[i + 1] + + # Convert tag start index to comparable values. + tag_start_line, tag_start_char = map(int, str(tag_start).split('.')) + + # Check if the tag start is less than the cursor position. + if tag_start_line < cursor_line or (tag_start_line == cursor_line and tag_start_char < cursor_char): + self.text_widget.mark_set(tk.INSERT, tag_start) + self.text_widget.see(tag_start) + + # Apply the 'current_found' tag to the current occurrence. + self.text_widget.tag_add('current_found', tag_start, tag_end) + break + else: + # Wrap to the last tag if no previous tag is found. + self.text_widget.mark_set(tk.INSERT, str(current_tags[-2])) + self.text_widget.see(str(current_tags[-2])) + self.text_widget.tag_add('current_found', current_tags[-2], current_tags[-1]) + + +class SpecificSearchFrame(tk.Frame): + def __init__(self, parent, root): + super().__init__(parent) + self.root = root + self.after_id = None + self.console = None + + # Logger name entry + self.logger_entry_placeholder = "Logger Name" + self.logger_entry_var = tk.StringVar() + self.logger_entry = PlaceholderEntry( + self, + placeholder=self.logger_entry_placeholder, + textvariable=self.logger_entry_var + ) + self.logger_entry_var.trace("w", self.on_entry_changed) + self.logger_entry.bind('', lambda event: self.console.text_widget.focus()) + self.logger_entry.config(fg='grey') + + # Log level dropdown + self.log_level_dropdown_var = tk.StringVar() + self.log_level_dropdown_var.set("TRACE") + self.log_level_dropdown = tk.OptionMenu( + self, + self.log_level_dropdown_var, + *log_levels.keys() + ) + self.log_level_dropdown_var.trace( + "w", + lambda *args: self.console.set_filter(log_level=self.log_level_dropdown_var.get()) + ) + + # And above checkbox + self.and_above_var = tk.BooleanVar() + self.and_above_var.set(True) + self.and_above_checkbox = tk.Checkbutton( + self, + text="And above", + variable=self.and_above_var, + onvalue=True, + offvalue=False, + command=lambda: self.console.set_filter(and_above=self.and_above_var.get()) + ) + + self.clear_log_button: tk.Button | None = None + + self.create_widgets() + + def inject_console(self, console): + self.console = console + self.clear_log_button = tk.Button( + self, + text="Clear Logs", + command=self.console.clear_logs + ) + self.clear_log_button.pack(side=tk.RIGHT, padx=(5, 0), fill='x', expand=True) + + def create_widgets(self): + self.logger_entry.pack(side=tk.LEFT, fill='x', expand=True, padx=(5, 0)) + self.log_level_dropdown.pack(side=tk.LEFT, padx=(5, 0), fill='x', expand=True) + self.and_above_checkbox.pack(side=tk.LEFT, padx=(5, 0), fill='x', expand=True) + + def on_entry_changed(self, *args): + if self.after_id: + self.root.after_cancel(self.after_id) + self.after_id = self.root.after(250, self.apply_logger_entry_var) + + def apply_logger_entry_var(self): + if self.logger_entry.user_has_interacted: + self.console.set_filter(logger_name=self.logger_entry_var.get()) + self.after_id = None + + +class ExportMenuBar(tk.Menu): + def __init__(self, parent, console: Console, *args, **kwargs): + super().__init__(parent, *args, **kwargs) + self.parent = parent + self.initialize_menu() + self.console = console + + def initialize_menu(self): + # Create a File menu + file_menu = tk.Menu(self, tearoff=0) + file_menu.add_command(label="All logs", command=self.export_all_logs) + file_menu.add_separator() + file_menu.add_command(label="Filtered logs", command=self.export_filtered_logs) + + # Adding the "File" menu to the menubar + self.add_cascade(label="Export", menu=file_menu) + + def export_all_logs(self): + file_path = filedialog.asksaveasfilename( + defaultextension=".log", + filetypes=[("Log files", "*.log"), ("All files", "*.*")], + initialfile=f"Balatro-AllLogs-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log" + ) + if file_path: + with open(file_path, "w") as f: + for log in self.console.all_logs: + f.write(str(log)) + + def export_filtered_logs(self): + file_path = filedialog.asksaveasfilename( + defaultextension=".log", + filetypes=[("Log files", "*.log"), ("All files", "*.*")], + initialfile=f"Balatro-FilteredLogs-{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.log" + ) + if file_path: + with open(file_path, "w") as f: + for log in self.console.shown_logs: + f.write(str(log)) + + +class MainWindow(tk.Tk): + def __init__(self): + super().__init__() + self.title("Steamodded Debug Console") + self.options_frame = OptionsFrame(self) + self.console = Console(self, self.options_frame) + self.options_frame.inject_console(self.console) + self.menu_bar = ExportMenuBar(self, self.console) + self.create_widgets() + + self.bind('', self.focus_search) + self.bind('', self.focus_search) + + self.bind('', lambda event: self.menu_bar.export_filtered_logs()) + self.bind('', lambda event: self.menu_bar.export_filtered_logs()) + + self.bind('', lambda event: self.menu_bar.export_all_logs()) + self.bind('', lambda event: self.menu_bar.export_all_logs()) + + self.bind('', lambda event: self.console.clear_logs()) + self.bind('', lambda event: self.console.clear_logs()) + + self.bind('', self.focus_logger) + self.bind('', self.focus_logger) + + def create_widgets(self): + self.console.pack(side=tk.TOP, expand=True, fill='both') + self.options_frame.pack(side=tk.BOTTOM, fill='x', expand=False) + self.config(menu=self.menu_bar) + + def get_console(self): + return self.console + + def focus_search(self, event): + self.options_frame.global_search_frame.search_entry.focus() + + def focus_logger(self, event): + self.options_frame.specific_search_frame.logger_entry.focus() + + +def client_handler(client_socket, console: Console): + buffer = [] + while True: + data = client_socket.recv(1024) + if not data: + break + + decoded_data = data.decode() + buffer.append(decoded_data) # Append new data to the buffer list + + # Join the buffer and split by "ENDOFLOG" + # This handles cases where "ENDOFLOG" is spread across multiple recv calls + combined_data = ''.join(buffer) + logs = combined_data.split("ENDOFLOG") + + # The last element might be an incomplete log; keep it in the buffer + buffer = [logs.pop()] if logs[-1] else [] + + # Append each complete log to the console + for log in logs: + if log: + console.append_log(log) + + # Handle any remaining data in the buffer after the connection is closed + if ''.join(buffer): + console.append_log(''.join(buffer)) + + +def listen_for_clients(console: Console): + server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + server_socket.bind(('localhost', 12345)) + server_socket.listen() + while True: + client, addr = server_socket.accept() + threading.Thread(target=client_handler, args=(client, console)).start() + + +if __name__ == "__main__": + root = MainWindow() + threading.Thread(target=listen_for_clients, daemon=True, args=(root.get_console(),)).start() + root.mainloop() diff --git a/Steamodded/version.lua b/Steamodded/version.lua new file mode 100644 index 0000000..7d05e2f --- /dev/null +++ b/Steamodded/version.lua @@ -0,0 +1 @@ +return "1.0.0~ALPHA-1220a-STEAMODDED" diff --git a/Talisman/README.md b/Talisman/README.md new file mode 100644 index 0000000..eadcb02 --- /dev/null +++ b/Talisman/README.md @@ -0,0 +1,15 @@ +# Talisman +A mod for Balatro that increases the score cap from ~10^308 to ~10{1000}10, allowing for endless runs to go past "naneinf" and Ante 39, and removes the long animations that come with these scores. + +The "BigNum" representation used by Talisman is a modified version of [this](https://github.com/veprogames/lua-big-number) library by veprogames. +The "OmegaNum" representation used by Talisman is a port of [OmegaNum.js](https://github.com/Naruyoko/OmegaNum.js/blob/master/OmegaNum.js) by [Mathguy23](https://github.com/Mathguy23) + +## Installation +Talisman requires [Lovely](https://github.com/ethangreen-dev/lovely-injector) to be installed in order to be loaded by Balatro. The code for Talisman must be installed in `%AppData%/Balatro/Mods/Talisman`. + +## Limitations +- High scores will not be saved to your profile (this is to prevent your profile save from being incompatible with an unmodified instance of Balatro) +- Savefiles created/opened with Talisman aren't backwards-compatible with unmodified versions of Balatro. +- Depending on the amount of retriggers, there may be lag when computing a score with Talisman. +- The largest ante before score requirements reach the new limit is approximately 2e153. +- When using Talisman with other mods, comparison operations with numbers used by scoring will not work by default (these must be handled by the mod developer). diff --git a/Talisman/assets/1x/icon.png b/Talisman/assets/1x/icon.png new file mode 100644 index 0000000..fa350d6 Binary files /dev/null and b/Talisman/assets/1x/icon.png differ diff --git a/Talisman/assets/2x/icon.png b/Talisman/assets/2x/icon.png new file mode 100644 index 0000000..65c914b Binary files /dev/null and b/Talisman/assets/2x/icon.png differ diff --git a/Talisman/assets/sounds/ExponentialChips.wav b/Talisman/assets/sounds/ExponentialChips.wav new file mode 100644 index 0000000..40d3b3b Binary files /dev/null and b/Talisman/assets/sounds/ExponentialChips.wav differ diff --git a/Talisman/assets/sounds/ExponentialMult.wav b/Talisman/assets/sounds/ExponentialMult.wav new file mode 100644 index 0000000..8eabc46 Binary files /dev/null and b/Talisman/assets/sounds/ExponentialMult.wav differ diff --git a/Talisman/assets/sounds/MultiplicativeChips.wav b/Talisman/assets/sounds/MultiplicativeChips.wav new file mode 100644 index 0000000..8a3e4d9 Binary files /dev/null and b/Talisman/assets/sounds/MultiplicativeChips.wav differ diff --git a/Talisman/assets/sounds/PentationalChips.wav b/Talisman/assets/sounds/PentationalChips.wav new file mode 100644 index 0000000..32847d6 Binary files /dev/null and b/Talisman/assets/sounds/PentationalChips.wav differ diff --git a/Talisman/assets/sounds/PentationalMult.wav b/Talisman/assets/sounds/PentationalMult.wav new file mode 100644 index 0000000..65a914e Binary files /dev/null and b/Talisman/assets/sounds/PentationalMult.wav differ diff --git a/Talisman/assets/sounds/TetrationalChips.wav b/Talisman/assets/sounds/TetrationalChips.wav new file mode 100644 index 0000000..9e2550e Binary files /dev/null and b/Talisman/assets/sounds/TetrationalChips.wav differ diff --git a/Talisman/assets/sounds/TetrationalMult.wav b/Talisman/assets/sounds/TetrationalMult.wav new file mode 100644 index 0000000..dcf70b4 Binary files /dev/null and b/Talisman/assets/sounds/TetrationalMult.wav differ diff --git a/Talisman/big-num/LICENSE b/Talisman/big-num/LICENSE new file mode 100644 index 0000000..571bb38 --- /dev/null +++ b/Talisman/big-num/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 veprogames + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Talisman/big-num/README.md b/Talisman/big-num/README.md new file mode 100644 index 0000000..1f6a13c --- /dev/null +++ b/Talisman/big-num/README.md @@ -0,0 +1,38 @@ +# lua-big-number + +A simple big number library for Lua, inspired by https://github.com/Patashu/break_infinity.js + +## Features + +1. floating-point precision in the range [~e-e14 - ~ee14] +2. Hard limit should be about ee308, or 10^2¹⁰²⁴ +3. method names identical or very similar to https://github.com/Patashu/break_infinity.js +4. non-mutable methods -> easy chaining `Big:new(2):add(Big:new(2)):mul(Big:new(2))` +5. Operator overloading `Big:new(3) * Big:new(5) ^ 2` + +## Usage + +1. Add `bignumber.lua` into your project +2. `Big = dofile("/path/to/bignumber.lua")` + +## Example + +```lua +Big = dofile("lua-big-number/bignumber.lua") + +local big1 = Big:new(100) +local big2 = Big:new(10) +local added = big1:add(big2) +print(added) -- will output 1.1e2 +print(big1 * big2) -- will output 1.0e3 +print(big1 + (big1 * big2 - big2) / (big1 ^ 0.4)) -- will output 2.5690442605365e2 + +-- comparisons +print(big1 > big2) -- true +print(big1 == big2) -- false + +-- You can define a shorthand for Big:new() +local N = function(n) return Big:new(n) end + +print(N(2) ^ 4096 / N("4.3e401")) -- 2.4288113521237e831 +``` \ No newline at end of file diff --git a/Talisman/big-num/bignumber.lua b/Talisman/big-num/bignumber.lua new file mode 100644 index 0000000..83355aa --- /dev/null +++ b/Talisman/big-num/bignumber.lua @@ -0,0 +1,354 @@ +-- class table +Big = { + m = 0, + e = 0 +} + +-- metatable +BigMeta = {} +BigMeta.__index = Big + +--- Create a new Big number +-- +-- numbers are stored in the form `m * 10 ^ e` +function Big:new(m, e) + if type(m) == "table" then + return setmetatable({m = m.m, e = m.e}, BigMeta):normalized() + end + if e == nil then e = 0 end + + if type(m) == "string" then + return Big.parse(m) + end + + return setmetatable({m = m, e = e}, BigMeta):normalized() +end + +function Big:normalize() + if self.m == 0 then + self.e = 0 + else + local n_log = math.floor(math.log(math.abs(self.m), 10)) + self.m = self.m / 10 ^ n_log + self.e = self.e + n_log + self.e = math.floor(self.e) + end +end + +function Big:normalized() + if (tostring(self.e) == "nan" or tostring(self.e) == "inf") then + self.m = 0/0 + self.e = 10^1000 + return self + end + self:normalize() + return self +end + +function Big:add(b) + if type(b) == "number" then b = Big:new(b) end + local delta = b.e - self.e + + if delta > 14 then return b end + if delta < -14 then return self end + + return Big:new(self.m + b.m * 10 ^ delta, self.e):normalized() +end + +function BigMeta.__add(b1, b2) + if type(b1) == "number" then return Big:new(b1) + b2 end + return b1:add(b2) +end + +function Big:sub(b) + if type(b) == "number" then b = Big:new(b) end + local nb = Big:new(b.m * -1, b.e) --negate b + return self:add(nb) +end + +function BigMeta.__sub(b1, b2) + if type(b1) == "number" then return Big:new(b1) - b2 end + return b1:sub(b2) +end + +function Big:mul(b) + if type(b) == "number" then b = Big:new(b) end + return Big:new(self.m * b.m, self.e + b.e):normalized() +end + +function BigMeta.__mul(b1, b2) + if type(b1) == "number" then return Big:new(b1) * b2 end + return b1:mul(b2) +end + +function Big:div(b) + if type(b) == "number" then b = Big:new(b) end + return Big:new(self.m / b.m, self.e - b.e):normalized() +end + +function BigMeta.__div(b1, b2) + if type(b1) == "number" then return Big:new(b1) / b2 end + return b1:div(b2) +end + +function Big:mod(other) + other = Big:create(other) + if (other:eq(R.ZERO)) then + Big:create(R.ZERO) + end + if (self.sign*other.sign == -1) then + return self:abs():mod(other:abs()):neg() + end + if (self.sign==-1) then + return self:abs():mod(other:abs()) + end + return self:sub(self:div(other):floor():mul(other)) +end + +function Big:negate() + return self:mul(Big:new(-1)) +end + +function BigMeta.__unm(b1) + return b1:negate() +end + +function Big:log10() + -- The default value of 0 is to make Balatro happy... + if self.e == nan and self.m == nan then return 0 end + if self:lte(Big:new(0)) then return 0 end + return self.e + math.log(self.m, 10) +end + +function Big:log(base) + -- The default value of 0 is to make Balatro happy... + if self.e == nan and self.m == nan then return 0 end + if self:lte(Big:new(0)) then return 0 end + return self:log10() / math.log(base, 10) +end + +function Big:ln() + return self:log(math.exp(1)) +end + +function Big:ld() + return self:log(2) +end + +function Big:pow(pow) + -- faster than self:eq(Big:new(0)) + if self.m == 0 and self.e == 0 then + return Big:new(0) + end + local log = self:log10() + local new_log = log * pow + return Big:new(10 ^ (new_log % 1), math.floor(new_log)):normalized() +end + +function BigMeta.__pow(b1, n) + if type(n) == "table" then n = n:to_number() end + if type(b1) ~= "table" then b1 = Big:new(b1) end + return b1:pow(n) +end + +function Big.exp(n) + return Big:new(math.exp(1)):pow(n) +end + +function Big:recp() + return self:pow(-1) +end + +function Big:sqrt() + return self:pow(0.5) +end + +function Big:cbrt() + return self:pow(1 / 3) +end + +function Big:round() + if self.e > 100 then return self end + local num = self:to_number() + if num % 1 < 0.5 then + return Big:new(math.floor(num)) + else + return Big:new(math.ceil(num)) + end +end + +function Big:floor() + if self.e > 100 then return self end + return Big:new(math.floor(self:to_number())) +end + +function Big:ceil() + if self.e > 100 then return self end + return Big:new(math.ceil(self:to_number())) +end + +function Big:floor_m(digits) + if self.e > 100 then return self end + return Big:new(math.floor(self.m * 10 ^ digits) / 10 ^ digits, self.e) +end + +function Big:ceil_m(digits) + if self.e > 100 then return self end + return Big:new(math.ceil(self.m * 10 ^ digits) / 10 ^ digits, self.e) +end + +function Big:sin() + return Big:new(math.sin(self:to_number())) +end + +function Big:asin() + return Big:new(math.asin(self:to_number())) +end + +function Big:cos() + return Big:new(math.cos(self:to_number())) +end + +function Big:acos() + return Big:new(math.acos(self:to_number())) +end + +function Big:tan() + return Big:new(math.tan(self:to_number())) +end + +function Big:is_positive() + return self.m >= 0 +end + +function Big:is_negative() + return self.m < 0 +end + +function Big:is_naneinf() + return tostring(self.m) == "nan" and (tostring(self.e) == "nan" or tostring(self.e) == "inf") +end + +function Big:compare(b) + b = Big:new(b) + if self.m == b.m and self.e == b.e then + return 0 + end + + if self:is_positive() and b:is_negative() then + return 1 + end + if self:is_negative() and b:is_positive() then + return -1 + end + + if self.e > b.e then return 1 end + if self.e < b.e then return -1 end + + if self:is_positive() and self.m > b.m then return 1 end + if self:is_positive() and self.m < b.m then return -1 end + if self:is_negative() and self.m > b.m then return -1 end + if self:is_negative() and self.m < b.m then return 1 end +end + +function Big:gt(b) + return self:compare(b) == 1 +end + +function Big:gte(b) + return self:compare(b) >= 0 +end + +function Big:lt(b) + return self:compare(b) == -1 +end + +function BigMeta.__lt(b1, b2) + if b2:is_naneinf() then + return true + end + if b1:is_naneinf() then + return false + end + return b1:lt(b2) +end + +function Big:lte(b) + return self:compare(b) <= 0 +end + +function BigMeta.__le(b1, b2) + if b2:is_naneinf() then + return true + end + if b1:is_naneinf() then + return false + end + return b1:lte(b2) +end + +function Big:gt(b) + return self:compare(b) == 1 +end + +function Big:eq(b) + return self:compare(b) == 0 +end + +function BigMeta.__eq(b1, b2) + return b1:eq(b2) +end + +function Big:neq(b) + return self:compare(b) ~= 0 +end + +function Big:to_string() + return self.m.."e"..self.e +end + +function Big:to_number() + return self.m * 10 ^ self.e +end + +function BigMeta.__tostring(b) + return number_format(b) +end + +function Big.parse(str) + local to_n = tonumber(str) + if to_n ~= nil and to_n < math.huge then + return Big:new(to_n):normalized() + end + + local parts = {} + for m, e in str:gmatch("(.+)e(.+)") do + parts = {m, e} + end + return Big:new(tonumber(parts[1]), math.floor(tonumber(parts[2]))):normalized() +end + +--Adding things OmegaNum has that this doesn't... +R = {} + +R.ZERO = 0 +R.ONE = 1 +R.E = math.exp(1) +R.LN2 = math.log(2, R.E) +R.LN10 = math.log(10, R.E) +R.LOG2E = math.log(R.E, 2) +R.LOG10E = math.log(R.E, 0) +R.PI = math.pi +R.SQRT1_2 = math.sqrt(0.5) +R.SQRT2 = math.sqrt(2) +R.MAX_SAFE_INTEGER=9007199254740991 +R.MIN_SAFE_INTEGER=-9007199254740992 +R.MAX_DISP_INTEGER=1000000 +R.NaN=0/0 +R.NEGATIVE_INFINITY = -1/0 +R.POSITIVE_INFINITY = 1/0 +R.E_MAX_SAFE_INTEGER="e"..tostring(R.MAX_SAFE_INTEGER) +R.EE_MAX_SAFE_INTEGER="ee"..tostring(R.MAX_SAFE_INTEGER) +R.TETRATED_MAX_SAFE_INTEGER="10^^"..tostring(R.MAX_SAFE_INTEGER) + +return Big \ No newline at end of file diff --git a/Talisman/big-num/notations.lua b/Talisman/big-num/notations.lua new file mode 100644 index 0000000..fc06d11 --- /dev/null +++ b/Talisman/big-num/notations.lua @@ -0,0 +1,18 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +Notations = { + BaseLetterNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/baseletternotation.lua")(), + LetterNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/letternotation.lua")(), + CyrillicNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/cyrillicnotation.lua")(), + GreekNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/greeknotation.lua")(), + HebrewNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/hebrewnotation.lua")(), + ScientificNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/scientificnotation.lua")(), + EngineeringNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/engineeringnotation.lua")(), + BaseStandardNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/basestandardnotation.lua")(), + StandardNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/standardnotation.lua")(), + ThousandNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/thousandnotation.lua")(), + DynamicNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/dynamicnotation.lua")(), + Balatro = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/balatro.lua")(), +} + +return Notations \ No newline at end of file diff --git a/Talisman/big-num/notations/Balatro.lua b/Talisman/big-num/notations/Balatro.lua new file mode 100644 index 0000000..bfd518e --- /dev/null +++ b/Talisman/big-num/notations/Balatro.lua @@ -0,0 +1,87 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +Notation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/notation.lua")() +BalaNotation = {} +BalaNotation.__index = BalaNotation +BalaNotation.__tostring = function () + return "BalaNotation" +end +setmetatable(BalaNotation, Notation) + +function BalaNotation:new() + return setmetatable({}, BalaNotation) +end + +function BalaNotation:format(n, places) + local function e_ify(n) + if (n > 10^6) then + local exponent = math.floor(math.log(n,10)) + local mantissa = n/10^exponent + mantissa = math.floor(mantissa*10^places+0.5)/10^places + return "("..exponent.."e"..mantissa..")" + end + if n < 1 then return 1 end + return n or "ERROR" + end + --The notation here is heavily based on Hyper-E notation. + if to_big(n:log10()) < to_big(1000000) then + --1.234e56789 + if n.m then --BigNum + local mantissa = math.floor(n.m*10^places+0.5)/10^places + local exponent = n.e + return mantissa.."e"..exponent + elseif n.array[2] == 1 then --OmegaNum + local mantissa = 10^(n.array[1]-math.floor(n.array[1])) + mantissa = math.floor(mantissa*10^places+0.5)/10^places + local exponent = math.floor(n.array[1]) + return mantissa.."e"..exponent + else + local exponent = math.floor(math.log(n.array[1],10)) + local mantissa = n.array[1]/10^exponent + mantissa = math.floor(mantissa*10^places+0.5)/10^places + return mantissa.."e"..exponent + end + elseif to_big(n:log10()) < to_big(10)^1000000 then + --e1.234e56789 + if n.m then --BigNum + local exponent = math.floor(math.log(n.e,10)) + local mantissa = n.e/10^exponent + mantissa = math.floor(mantissa*10^places+0.5)/10^places + return "e"..mantissa.."e"..exponent + elseif n.array[2] == 2 then --OmegaNum + local mantissa = 10^(n.array[1]-math.floor(n.array[1])) + mantissa = math.floor(mantissa*10^places+0.5)/10^places + local exponent = math.floor(n.array[1]) + return "e"..mantissa.."e"..exponent + else + local exponent = math.floor(math.log(n.array[1],10)) + local mantissa = n.array[1]/10^exponent + mantissa = math.floor(mantissa*10^places+0.5)/10^places + return "e"..mantissa.."e"..exponent + end + elseif not n.array or not (n.isFinite and n:isFinite()) then + return "Infinity" + elseif n.array[2] == 3 and #n.array == 2 then + --ee1.234e56789 + local mantissa = 10^(n.array[1]-math.floor(n.array[1])) + mantissa = math.floor(mantissa*10^places+0.5)/10^places + local exponent = math.floor(n.array[1]) + return "ee"..mantissa.."e"..exponent + elseif n.array[2] and #n.array == 2 and n.array[2] <= 8 then + --eeeeeeee56789 + local exponent = math.floor(n.array[1]) + return string.rep("e", n.array[2])..exponent + elseif #n.array < 8 then + --e12#34#56#78 + local r = "e"..e_ify(math.floor(n.array[1]*10^places+0.5)/10^places).."#"..n.array[2] + for i = 3, #n.array do + r = r.."#"..(n.array[i]+1) + end + return r + else + --e12#34##5678 + return "e"..e_ify(math.floor(n.array[1]*10^places+0.5)/10^places).."#"..n.array[#n.array].."##"..(#n.array-2) + end +end + +return BalaNotation diff --git a/Talisman/big-num/notations/baseletternotation.lua b/Talisman/big-num/notations/baseletternotation.lua new file mode 100644 index 0000000..998ca4d --- /dev/null +++ b/Talisman/big-num/notations/baseletternotation.lua @@ -0,0 +1,45 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +Notation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/notation.lua")() + +BaseLetterNotation = {} +BaseLetterNotation.__index = BaseLetterNotation +BaseLetterNotation.__tostring = function (notation) + return "BaseLetterNotation {"..notation.letters.."}" +end +setmetatable(BaseLetterNotation, Notation) + +function BaseLetterNotation:new(opt) + opt = opt or {} + return setmetatable({ + letters = opt.letters, + dynamic = opt.dynamic, + reversed = opt.reversed + }, BaseLetterNotation) +end + +function BaseLetterNotation:get_letters(n) + local order = math.floor(n.e / 3) + local result = "" + while order > 0 do + local letter_index = 1 + order % #self.letters + result = self.letters:sub(letter_index, letter_index) .. result + order = math.floor(order / #self.letters) + end + return result +end + +function BaseLetterNotation:get_number(n, places) + local num = n.m * 10 ^ (n.e % 3) + return Notation.format_mantissa(num, places) +end + +function BaseLetterNotation:get_prefix(n) + if self.reversed then return self:get_letters(n) else return "" end +end + +function BaseLetterNotation:get_suffix(n) + if not self.reversed then return self:get_letters(n) else return "" end +end + +return BaseLetterNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/basestandardnotation.lua b/Talisman/big-num/notations/basestandardnotation.lua new file mode 100644 index 0000000..f0aa9f5 --- /dev/null +++ b/Talisman/big-num/notations/basestandardnotation.lua @@ -0,0 +1,57 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +Notation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/notation.lua")() +BaseStandardNotation = {} +BaseStandardNotation.__index = BaseStandardNotation +BaseStandardNotation.__tostring = function (notation) + return "BaseStandardNotation" +end +setmetatable(BaseStandardNotation, Notation) + +function BaseStandardNotation:new(opt) + opt = opt or {} + return setmetatable({ + start = opt.start, + ones = opt.ones, + tens = opt.tens, + hundreds = opt.hundreds, + dynamic = opt.dynamic, + reversed = opt.reversed + }, BaseStandardNotation) +end + +function BaseStandardNotation:get_number_name(e) + if e >= 3003 then + local order_m = math.floor(e / 3000) + return string.format("[%d]-MI-", order_m) .. self:get_number_name(e - 3000 * order_m) + end + + local order = math.floor(e / 3) + + if order < #self.start then + return self.start[1 + order] + end + + order = order - 1 + local order_ten = math.floor(order / #self.ones) + local order_hundred = math.floor(order_ten / #self.tens) + + return self.hundreds[1 + order_hundred % #self.hundreds] .. + self.ones[1 + order % #self.ones] .. + self.tens[1 + order_ten % #self.tens] +end + +function BaseStandardNotation:get_number(n, places) + local num = n.m * 10 ^ (n.e % 3) + return Notation.format_mantissa(num, places) +end + +function BaseStandardNotation:get_prefix(n) + if self.reversed then return self:get_number_name(n.e) else return "" end +end + +function BaseStandardNotation:get_suffix(n) + if not self.reversed then return self:get_number_name(n.e) else return "" end +end + +return BaseStandardNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/cyrillicnotation.lua b/Talisman/big-num/notations/cyrillicnotation.lua new file mode 100644 index 0000000..230d03b --- /dev/null +++ b/Talisman/big-num/notations/cyrillicnotation.lua @@ -0,0 +1,19 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +BaseLetterNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/baseletternotation.lua")() + +CyrillicNotation = {} +CyrillicNotation.__index = LetterNotation +setmetatable(CyrillicNotation, BaseLetterNotation) +CyrillicNotation.__tostring = function () return "CyrillicNotation" end + +function CyrillicNotation:new(opt) + opt = opt or {} + return setmetatable({ + letters = "~абвгдежзиклмнопстуфхцчшщэюяАБВГДЕЖЗИКЛМНОПСТУФХЦЧШЩЭЮЯ", + dynamic = opt.dynamic or false, + reversed = opt.reversed or false + }, CyrillicNotation) +end + +return CyrillicNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/dynamicnotation.lua b/Talisman/big-num/notations/dynamicnotation.lua new file mode 100644 index 0000000..df5a17d --- /dev/null +++ b/Talisman/big-num/notations/dynamicnotation.lua @@ -0,0 +1,50 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +Notation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/notation.lua")() +ThousandNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/thousandnotation.lua")() + +DynamicNotation = {} +DynamicNotation.__index = DynamicNotation +DynamicNotation.__tostring = function () + return "DynamicNotation" +end + +function DynamicNotation:new(opt) + return setmetatable({ + small = opt.small or ThousandNotation:new(), + big = opt.big, + limit = opt.limit + }, DynamicNotation) +end + +function DynamicNotation:get_notation(n) + if n:lte(self.limit) then + return self.small + end + return self.big +end + +function DynamicNotation:get_prefix(n) + return self:get_notation(n):get_prefix(n) +end + +function DynamicNotation:get_number(n, places) + return self:get_notation(n):get_number(n, places) +end + +function DynamicNotation:get_suffix(n) + return self:get_notation(n):get_suffix(n) +end + +function DynamicNotation:format(n, places_big, places_small) + local p = 0 + if n:lte(self.limit) then + p = places_small or 0 + else + p = places_big or 0 + end + + return self:get_prefix(n) .. self:get_number(n, p) .. self:get_suffix(n) +end + +return DynamicNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/engineeringnotation.lua b/Talisman/big-num/notations/engineeringnotation.lua new file mode 100644 index 0000000..98f847f --- /dev/null +++ b/Talisman/big-num/notations/engineeringnotation.lua @@ -0,0 +1,28 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +Notation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/notation.lua")() + +EngineeringNotation = {} +EngineeringNotation.__index = EngineeringNotation +EngineeringNotation.__tostring = function () + return "EngineeringNotation" +end +setmetatable(EngineeringNotation, Notation) + +function EngineeringNotation:new(opt) + opt = opt or {} + return setmetatable({ + dynamic = opt.dynamic or false + }, EngineeringNotation) +end + +function EngineeringNotation:get_number(n, places) + local mantissa = n.m * 10 ^ (n.e % 3) + return self.format_mantissa(mantissa, places) +end + +function EngineeringNotation:get_suffix(n) + return "e" .. 3 * math.floor(n.e / 3) +end + +return EngineeringNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/greeknotation.lua b/Talisman/big-num/notations/greeknotation.lua new file mode 100644 index 0000000..9f6099a --- /dev/null +++ b/Talisman/big-num/notations/greeknotation.lua @@ -0,0 +1,19 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +BaseLetterNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/baseletternotation.lua")() + +GreekNotation = {} +GreekNotation.__index = LetterNotation +setmetatable(GreekNotation, BaseLetterNotation) +GreekNotation.__tostring = function () return "GreekNotation" end + +function GreekNotation:new(opt) + opt = opt or {} + return setmetatable({ + letters = "~αβγδεζηθικλμνξοπρστυφχψωΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡΣΤΥΦΧΨΩ", + dynamic = opt.dynamic or false, + reversed = opt.reversed or false + }, GreekNotation) +end + +return GreekNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/hebrewnotation.lua b/Talisman/big-num/notations/hebrewnotation.lua new file mode 100644 index 0000000..e93eb73 --- /dev/null +++ b/Talisman/big-num/notations/hebrewnotation.lua @@ -0,0 +1,19 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +BaseLetterNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/baseletternotation.lua")() + +HebrewNotation = {} +HebrewNotation.__index = LetterNotation +setmetatable(HebrewNotation, BaseLetterNotation) +HebrewNotation.__tostring = function () return "HebrewNotation" end + +function HebrewNotation:new(opt) + opt = opt or {} + return setmetatable({ + letters = "~אבּבגּגדּדהוזחטיכּכךּךלמםנןסעפּפףּףצץקרשׁשׂתּת", + dynamic = opt.dynamic or false, + reversed = opt.reversed or false + }, HebrewNotation) +end + +return HebrewNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/letternotation.lua b/Talisman/big-num/notations/letternotation.lua new file mode 100644 index 0000000..473661e --- /dev/null +++ b/Talisman/big-num/notations/letternotation.lua @@ -0,0 +1,19 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +BaseLetterNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/baseletternotation.lua")() + +LetterNotation = {} +LetterNotation.__index = LetterNotation +setmetatable(LetterNotation, BaseLetterNotation) +LetterNotation.__tostring = function () return "LetterNotation" end + +function LetterNotation:new(opt) + opt = opt or {} + return setmetatable({ + letters = "~abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", + dynamic = opt.dynamic or false, + reversed = opt.reversed or false + }, LetterNotation) +end + +return LetterNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/notation.lua b/Talisman/big-num/notations/notation.lua new file mode 100644 index 0000000..45495ca --- /dev/null +++ b/Talisman/big-num/notations/notation.lua @@ -0,0 +1,50 @@ +Notation = {} +Notation.__index = Notation +Notation.__tostring = function () + return "Notation" +end + +function Notation:new(opt) + opt = opt or {} + return setmetatable({dynamic = opt.dynamic or false}, Notation) +end + +function Notation:get_prefix(n) + return "" +end + +function Notation:get_number(n, places) + return "" +end + +function Notation:get_suffix(n) + return "" +end + +function Notation:format(n, places, places1000) + if n:is_negative() then + return "-" .. self:format(n:negate(), places, places1000) + end + + local p = places or 0 + if n < Big:new(1000) then + p = places1000 or 0 + end + + if self.dynamic then p = p + self.dp(n) else end + + return self:get_prefix(n) .. self:get_number(n, p) .. self:get_suffix(n) +end + +-- get dynamic places, added to base precision +function Notation.dp(n) + return -(n.e % 3) +end + +-- wrapper function to format numbers +function Notation.format_mantissa(number, places) + places = math.max(0, places) + return string.format("%."..places.."f", number) +end + +return Notation \ No newline at end of file diff --git a/Talisman/big-num/notations/scientificnotation.lua b/Talisman/big-num/notations/scientificnotation.lua new file mode 100644 index 0000000..005d563 --- /dev/null +++ b/Talisman/big-num/notations/scientificnotation.lua @@ -0,0 +1,23 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +Notation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/notation.lua")() +ScientificNotation = {} +ScientificNotation.__index = ScientificNotation +ScientificNotation.__tostring = function () + return "ScientificNotation" +end +setmetatable(ScientificNotation, Notation) + +function ScientificNotation:new() + return setmetatable({}, ScientificNotation) +end + +function ScientificNotation:get_number(n, places) + return self.format_mantissa(n.m, places) +end + +function ScientificNotation:get_suffix(n) + return "e" .. n.e +end + +return ScientificNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/standardnotation.lua b/Talisman/big-num/notations/standardnotation.lua new file mode 100644 index 0000000..6b2fe82 --- /dev/null +++ b/Talisman/big-num/notations/standardnotation.lua @@ -0,0 +1,24 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +BaseStandardNotation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/basestandardnotation.lua")() + +StandardNotation = {} +StandardNotation.__index = StandardNotation +StandardNotation.__tostring = function (notation) + return "StandardNotation" +end +setmetatable(StandardNotation, BaseStandardNotation) + +function StandardNotation:new(opt) + opt = opt or {} + return setmetatable({ + start = {"", "K", "M", "B", "T"}, + ones = {"", "U", "D", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No"}, + tens = {"", "Dc", "Vg", "Tg", "Qag", "Qig", "Sxg", "Spg", "Ocg", "Nog"}, + hundreds = {"", "C", "DC", "TC", "QaC", "QiC", "SxC", "SpC", "OC", "NoC"}, + dynamic = opt.dynamic, + reversed = opt.reversed + }, StandardNotation) +end + +return StandardNotation \ No newline at end of file diff --git a/Talisman/big-num/notations/thousandnotation.lua b/Talisman/big-num/notations/thousandnotation.lua new file mode 100644 index 0000000..f6ca0c3 --- /dev/null +++ b/Talisman/big-num/notations/thousandnotation.lua @@ -0,0 +1,36 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") +Notation = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations/notation.lua")() + +ThousandNotation = {} +ThousandNotation.__index = ThousandNotation +ThousandNotation.__tostring = function () + return "ThousandNotation" +end +setmetatable(ThousandNotation, Notation) + +function ThousandNotation:new() + return setmetatable({}, ThousandNotation) +end + +function ThousandNotation:get_number(n, places) + local raw = self.format_mantissa(n:to_number(), places) + local result = "" + local comma = string.find(raw, "%.") + + if comma == nil then + comma = #raw + else + comma = comma - 1 + end + + for i = 1, #raw do + result = result .. string.sub(raw, i, i) + if (comma - i) % 3 == 0 and i < comma then + result = result .. "," + end + end + return result +end + +return ThousandNotation \ No newline at end of file diff --git a/Talisman/big-num/omeganum.lua b/Talisman/big-num/omeganum.lua new file mode 100644 index 0000000..bf67253 --- /dev/null +++ b/Talisman/big-num/omeganum.lua @@ -0,0 +1,1339 @@ +--OmegaNum port by Mathguy +Big = { + array = {}, + sign = 1 +} + +maxArrow = 1e3 + +OmegaMeta = {} +OmegaMeta.__index = Big + +external = true + +omegaNumError = "[OmegaNumError] " +invalidArgument = omegaNumError .. "Invalid argument: " + +isOmegaNum = "/^[-\\+]*(Infinity|NaN|(10(\\^+|\\{[1-9]\\d*\\})|\\(10(\\^+|\\{[1-9]\\d*\\})\\)\\^[1-9]\\d* )*((\\d+(\\.\\d*)?|\\d*\\.\\d+)?([Ee][-\\+]*))*(0|\\d+(\\.\\d*)?|\\d*\\.\\d+))$/" + +MAX_SAFE_INTEGER = 9007199254740991 +MAX_E = math.log(MAX_SAFE_INTEGER, 10) +LONG_STRING_MIN_LENGTH = 17 + +R = {} + +R.ZERO = 0 +R.ONE = 1 +R.E = math.exp(1) +R.LN2 = math.log(2, R.E) +R.LN10 = math.log(10, R.E) +R.LOG2E = math.log(R.E, 2) +R.LOG10E = math.log(R.E, 0) +R.PI = math.pi +R.SQRT1_2 = math.sqrt(0.5) +R.SQRT2 = math.sqrt(2) +R.MAX_SAFE_INTEGER=MAX_SAFE_INTEGER +R.MIN_SAFE_INTEGER=-9007199254740992 +R.MAX_DISP_INTEGER=1000000 +R.NaN=0/0 +R.NEGATIVE_INFINITY = -1/0 +R.POSITIVE_INFINITY = 1/0 +R.E_MAX_SAFE_INTEGER="e"..tostring(R.MAX_SAFE_INTEGER) +R.EE_MAX_SAFE_INTEGER="ee"..tostring(R.MAX_SAFE_INTEGER) +R.TETRATED_MAX_SAFE_INTEGER="10^^"..tostring(R.MAX_SAFE_INTEGER) + +--------------make the numbers look good---------------------- +function thousands_format(number) + return string.format("%.2f", number) +end + +function AThousandNotation(n, places) + local raw = string.format("%." .. places .."f", n) + local result = "" + local comma = string.find(raw, "%.") + + if comma == nil then + comma = #raw + else + comma = comma - 1 + end + + for i = 1, #raw do + result = result .. string.sub(raw, i, i) + if (comma - i) % 3 == 0 and i < comma then + result = result .. "," + end + end + return result +end + +------------------------------------------------------ + +function Big:new(arr) + return setmetatable({array = arr, sign = 1}, OmegaMeta):normalize() +end + +function Big:isNaN() + return self.array[1] ~= self.array[1] +end + +function Big:isInfinite() + return (self.array[1] == R.POSITIVE_INFINITY) or (self.array[1] == R.NEGATIVE_INFINITY) +end + +function Big:isFinite() + return (not self:isInfinite() and not self:isNaN()) +end + +function Big:isint() + if (self.sign==-1) then + return self:abs():isint() + end + if (self:gt(R.MAX_SAFE_INTEGER)) then + return true; + end + local num = self:to_number() + return (math.floor(num) == num); +end + +function Big:compareTo(other) + other = Big:create(other) + if ((self.array[1] ~= self.array[1]) or (other.array[1] ~= other.array[1])) then + return R.NaN; + end + if ((self.array[1]==R.POSITIVE_INFINITY) and (other.array[1]~=R.POSITIVE_INFINITY)) then + return self.sign + end + if ((self.array[1]~=R.POSITIVE_INFINITY) and (other.array[1]==R.POSITIVE_INFINITY)) then + return other.sign + end + if ((#self.array==1) and (self.array[1]==0) and (#other.array==1) and (other.array[1]==0)) then + return 0 + end + if (self.sign~=other.sign) then + return self.sign + end + local m = self.sign; + local r = nil; + if (#self.array>#other.array) then + r = 1; + elseif (#self.array<#other.array) then + r = -1; + else + for i=#self.array,1,-1 do + if (self.array[i]>other.array[i]) then + r = 1; + break; + elseif (self.array[i] 0 +end + +function Big:lte(other) + return self:compareTo(other) <= 0 +end + +function Big:gte(other) + return self:compareTo(other) >= 0 +end + +function Big:eq(other) + return self:compareTo(other) == 0 +end + +function Big:neg() + local x = self:clone(); + x.sign = x.sign * -1; + return x; +end + +function Big:abs() + local x = self:clone(); + x.sign = 1; + return x; +end + +function Big:min(other) + if (self:lt(other)) then + return self:clone() + else + return Big:create(other) + end +end + +function Big:max(other) + if (self:gt(other)) then + return self:clone() + else + return Big:create(other) + end +end + +function Big:normalize() + local b = nil + local x = self + if ((x.array == nil) or (type(x.array) ~= "table") or (#x.array == 0)) then + x.array = {0} + end + if (#x.array == 1) and (x.array[1] == 0) then + x.sign = 1 + return x + end + if (#x.array == 1) and (x.array[1] < 0) then + x.sign = -1 + x.array[1] = -x.array[1] + end + if ((x.sign~=1) and (x.sign~=-1)) then + -- if (typeof x.sign!="number") x.sign=Number(x.sign); + if (x.sign < 0) then + x.sign = -1; + else + x.sign = 1; + end + end + local l = 0 + for i ,j in pairs(x.array) do + if i > l then + l = i + end + end + for i=1,l do + local e = x.array[i]; + if ((e == nil)) then + x.array[i] = 0 + e = 0 + end + if (e ~= e) then + x.array={R.NaN}; + return x; + end + if (e == R.POSITIVE_INFINITY) or (e == R.NEGATIVE_INFINITY) then + x.array = {R.POSITIVE_INFINITY}; + return x; + end + if (i ~= 1) then + x.array[i]=math.floor(e) + end + end + local doOnce = true + while (doOnce or b) do + -- if (OmegaNum.debug>=OmegaNum.ALL) console.log(x.toString()); + b=false; + while ((#x.array ~= 0) and (x.array[#x.array]==0)) do + x.array[#x.array] = nil; + b=true; + end + if (x.array[1] > R.MAX_DISP_INTEGER) then --modified, should make printed values easier to display + x.array[2]=(x.array[2] or 0) + 1; + x.array[1]= math.log(x.array[1], 10); + b=true; + end + while ((x.array[1] < math.log(R.MAX_DISP_INTEGER,10)) and ((x.array[2] ~= nil) and (x.array[2] ~= 0))) do + x.array[1] = math.pow(10,x.array[1], 10); + x.array[2] = x.array[2] - 1 + b=true; + end + -- if ((#x.array>2) and ((x.array[2] == nil) or (x.array[2] == 0))) then + -- local i = 3 + -- while (x.array[i] == nil) or (x.array[i] == 0) do + -- i = i + 1 + -- end + -- x.array[i-1]=x.array[1]; + -- x.array[1]=1; + -- x.array[i] = x.array[i] - 1 + -- b=true; + -- end + doOnce = false; + l = #x.array + for i=1,l do + if (x.array[i]>R.MAX_SAFE_INTEGER) then + x.array[i+1]=(x.array[i+1] or 0)+1; + x.array[1]=x.array[i]+1; + for j=2,i do + x.array[j]=0; + end + b=true; + end + end + end + if (#x.array == 0) then + x.array = {0} + end + return x; +end + +function Big:toString() + if (self.sign==-1) then + return "-" .. self:abs():toString() + end + if (self.array[1] ~= self.array[1]) then + return "NaN" + end + -- if (!isFinite(this.array[0])) return "Infinity"; + local s = ""; + if (#self.array>=2) then + for i=#self.array,3,-1 do + local q = nil + if (i >= 6) then + q = "{"..(i-1).."}" + else + q = string.rep("^", i-1) + end + if (self.array[i]>1) then + s = s .."(10" .. q .. ")^" .. AThousandNotation(self.array[i], 0) .. " " + elseif (self.array[i]==1) then + s= s .."10" .. q; + end + end + end + if (self.array[2] == nil) or (self.array[2] == 0) then + if (self.array[1] <= 9e9) then + s = s .. AThousandNotation(self.array[1], 2) + else + local exponent = math.floor(math.log(self.array[1], 10)) + local mantissa = math.floor((self.array[1] / (10^exponent))*100)/100 + s = s .. AThousandNotation(mantissa, 2) .. "e" .. AThousandNotation(exponent, 0) + end + elseif (self.array[2]<3) then + s = s .. string.rep("e", self.array[2]-1) .. AThousandNotation(math.pow(10,self.array[1]-math.floor(self.array[1])), 2) .. "e" .. AThousandNotation(math.floor(self.array[1]), 0); + elseif (self.array[2]<8) then + s = s .. string.rep("e", self.array[2]) .. AThousandNotation(self.array[1], 0) + else + s = s .. "(10^)^" .. AThousandNotation(self.array[2], 0) .. " " .. AThousandNotation(self.array[1],0) + end + return s +end + +function log10LongString(str) + return math.log(tonumber(string.sub(str, 1, LONG_STRING_MIN_LENGTH)), 10)+(string.len(str)- LONG_STRING_MIN_LENGTH); +end + +function Big:parse(input) + -- if (typeof input!="string") throw Error(invalidArgument+"Expected String"); + -- var isJSON=false; + -- if (typeof input=="string"&&(input[0]=="["||input[0]=="{")){ + -- try { + -- JSON.parse(input); + -- }finally{ + -- isJSON=true; + -- } + -- } + -- if (isJSON){ + -- return OmegaNum.fromJSON(input); + -- } + local x = Big:new({0}) + -- if (!isOmegaNum.test(input)){ + -- console.warn(omegaNumError+"Malformed input: "+input); + -- x.array=[NaN]; + -- return x; + -- } + local negateIt = false + while ((string.sub(input, 1, 1)=="-") or (string.sub(input, 1, 1)=="+")) do + if (string.sub(input, 1, 1)=="-") then + negateIt = not negateIt + end + input = string.sub(input, 2); + end + if (input=="NaN") or (input=="nan") then + x.array = {R.NaN} + elseif (input=="Infinity") or (input=="inf") then + x.array = {R.POSITIVE_INFINITY} + else + local a = 0 + local b = 0 + local c = 0 + local d = 0 + local i = 0 + while (string.len(input) > 0) do + local passTest = false + if true then + local j = 1 + if string.sub(input, 1, 1) == "(" then + j = j + 1 + end + if (string.sub(input, j, j+1) == "10") and ((string.sub(input, j+2, j+2) == "^") or (string.sub(input, j+2, j+2) == "{")) then + passTest = true + end + end + if (passTest) then + if (string.sub(input, 1, 1) == "(") then + input = string.sub(input, 2); + end + local arrows = -1; + if (string.sub(input, 3, 3)=="^") then + arrows = 3 + while (string.sub(input, arrows, arrows) == "^") do + arrows = arrows + 1 + end + arrows = arrows - 3 + a = arrows + b = arrows + 2; + else + a = 1 + while (string.sub(input, a, a) ~= "}") do + a = a + 1 + end + arrows=tonumber(string.sub(input, 4, a - 1))+1; + b = a + 1 + end + --[[if (arrows >= maxArrow) then + -- console.warn("Number too large to reasonably handle it: tried to "+arrows.add(2)+"-ate."); + x.array = {R.POSITIVE_INFINITY}; + break; + end--]] + input = string.sub(input, b + 1); + if (string.sub(input, 1, 1) == ")") then + a = 1 + while (string.sub(input, a, a) ~= " ") do + a = a + 1 + end + c = tonumber(string.sub(input, 3, a - 1)); + input = string.sub(input, a+1); + else + c = 1 + end + if (arrows==1) then + x.array[2] = (x.array[2] or 0) + c; + elseif (arrows==2) then + a = x.array[2] or 0; + b = x.array[1] or 0; + if (b>=1e10) then + a = a + 1 + end + if (b>=10) then + a = a + 1 + end + x.array[1]=a; + x.array[2]=0; + x.array[3]=(x.array[3] or 0)+c; + else + a=x.array[arrows] or 0; + b=x.array[arrows-1] or 0; + if (b>=10) then + a = a + 1 + end + for i=1, arrows do + x.array[i] = 0; + end + x.array[1]=a; + x.array[arrows+1] = (x.array[arrows+1] or 0) + c; + end + else + break + end + end + a = {""} + while (string.len(input) > 0) do + if ((string.sub(input, 1, 1) == "e") or (string.sub(input, 1, 1) == "E")) then + a[#a + 1] = "" + else + a[#a] = a[#a] .. string.sub(input, 1, 1) + end + input = string.sub(input, 2); + end + if a[#a] == "" then + a[#a] = nil + end + b={x.array[1],0}; + c=1; + for i=#a, 1, -1 do + if ((b[1] < MAX_E) and (b[2]==0)) then + b[1] = math.pow(10,c*b[1]); + elseif (c==-1) then + if (b[2]==0) then + b[1]=math.pow(10,c*b[1]); + elseif ((b[2]==1) and (b[1]<=308)) then + b[1] = math.pow(10,c*math.pow(10,b[1])); + else + b[1]=0; + end + b[2]=0; + else + b[2] = b[2] + 1; + end + local decimalPointPos = 1; + while ((string.sub(a[i], decimalPointPos, decimalPointPos) ~= ".") and (decimalPointPos <= #a[i])) do + decimalPointPos = decimalPointPos + 1 + end + if decimalPointPos == #a[i] + 1 then + decimalPointPos = -1 + end + local intPartLen = -1 + if (decimalPointPos == -1) then + intPartLen = #a[i] + 1 + else + intPartLen = decimalPointPos + end + if (b[2] == 0) then + if (intPartLen - 1 >= LONG_STRING_MIN_LENGTH) then + b[1] = math.log10(b[1]) + log10LongString(string.sub(a[i], 1, intPartLen - 1)) + b[2] = 1; + elseif ((a[i] ~= nil) and (a[i] ~= "")) then + b[1] = b[1] * tonumber(a[i]); + end + else + d=-1 + if (intPartLen - 1 >= LONG_STRING_MIN_LENGTH) then + d = log10LongString(string.sub(a[i], 1,intPartLen - 1)) + else + if (a[i] ~= nil) and (a[i] ~= "") and (tonumber(a[i]) ~= nil) then + d = math.log(tonumber(a[i]), 10) + else + d = 0 + end + end + if (b[2]==1) then + b[1] = b[1] + d; + elseif ((b[2]==2) and (b[1]MAX_SAFE_INTEGER) then + b[1] = math.log(b[1], 10); + b[2] = b[2] + 1; + end + end + x.array[1]= b[1]; + x.array[2]= (x.array[2] or 0) + b[2]; + end + if (negateIt) then + x.sign = x.sign * -1 + end + x:normalize(); + return x; +end + +function Big:to_number() + -- //console.log(this.array); + if (self.sign==-1) then + return -1*(self:neg():to_number()); + end + if ((#self.array>=2) and ((self.array[2]>=2) or (self.array[2]==1) and (self.array[1]>308))) then + return R.POSITIVE_INFINITY; + end + if (#self.array >= 3) and ((self.array[1] >= 3) or (self.array[2] >= 1) or (self.array[3] >= 1)) then + return R.POSITIVE_INFINITY; + end + if (#self.array >= 4) and ((self.array[1] > 1) or (self.array[2] >= 1) or (self.array[3] >= 1)) then + for i = 4, #self.array do + if self.array[i] > 0 then + return R.POSITIVE_INFINITY; + end + end + end + if (type(self.array[1]) == "table") then + self.array[1] = self.array[1]:to_number() + end + if (self.array[2]==1) then + return math.pow(10,self.array[1]); + end + return self.array[1]; +end + +function Big:floor() + if (self:isint()) then + return self:clone() + end + return Big:create(math.floor(self:to_number())); +end + +function Big:ceil() + if (self:isint()) then + return self:clone() + end + return Big:create(math.ceil(self:to_number())); +end + +function Big:clone() + local newArr = {} + for i, j in ipairs(self.array) do + newArr[i] = j + end + local result = Big:new(newArr) + result.sign = self.sign + return result +end + +function Big:create(input) + if ((type(input) == "number")) then + return Big:new({input}) + elseif ((type(input) == "string")) then + return Big:parse(input) + elseif ((type(input) == "table") and getmetatable(input) == OmegaMeta) then + return input:clone() + else + return Big:new(input) + end +end + +function Big:add(other) + local x = self:clone() + other = Big:create(other) + -- if (OmegaNum.debug>=OmegaNum.NORMAL){ + -- console.log(this+"+"+other); + -- if (!debugMessageSent) console.warn(omegaNumError+"Debug output via 'debug' is being deprecated and will be removed in the future!"),debugMessageSent=true; + -- } + if (x.sign==-1) then + return x:neg():add(other:neg()):neg() + end + if (other.sign==-1) then + return x:sub(other:neg()); + end + if (x:eq(R.ZERO)) then + return other; + end + if (other:eq(R.ZERO)) then + return x; + end + if (x:isNaN() or other:isNaN() or (x:isInfinite() and other:isInfinite() and x:eq(other:neg()))) then + return Big:create(R.NaN); + end + if (x:isInfinite()) then + return x; + end + if (other:isInfinite()) then + return other; + end + local p=x:min(other); + local q=x:max(other); + local t = -1; + if (p.array[2] == 2) and not p:gt(R.E_MAX_SAFE_INTEGER) then + p.array[2] = 1 + p.array[1] = 10 ^ p.array[1] + end + if (q.array[2] == 2) and not q:gt(R.E_MAX_SAFE_INTEGER) then + q.array[2] = 1 + q.array[1] = 10 ^ q.array[1] + end + if (q:gt(R.E_MAX_SAFE_INTEGER) or q:div(p):gt(R.MAX_SAFE_INTEGER)) then + t = q; + elseif (q.array[2] == nil) or (q.array[2] == 0) then + t= Big:create(x:to_number()+other:to_number()); + elseif (q.array[2]==1) then + if (p.array[2] ~= nil) and (p.array[2] ~= 0) then + a = p.array[1] + else + a = math.log(p.array[1], 10) + end + t = Big:new({a+math.log(math.pow(10,q.array[1]-a)+1, 10),1}); + end + p = nil + q = nil + return t; +end + +function Big:sub(other) + local x = self:clone() + other = Big:create(other) + -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log(x+"-"+other); + if (x.sign==-1) then + return x:neg():sub(other:neg()):neg() + end + if (other.sign==-1) then + return x:add(other:neg()) + end + if (x:eq(other)) then + return Big:create(R.ZERO) + end + if (other:eq(R.ZERO)) then + return x; + end + if (x:isNaN() or other:isNaN() or (x:isInfinite() and other:isInfinite() and x:eq(other:neg()))) then + return Big:create(R.NaN) + end + if (x:isInfinite()) then + return x + end + if (other:isInfinite()) then + return other:neg() + end + local p = x:min(other); + local q = x:max(other); + local n = other:gt(x); + local t = -1; + if (p.array[2] == 2) and not p:gt(R.E_MAX_SAFE_INTEGER) then + p.array[2] = 1 + p.array[1] = 10 ^ p.array[1] + end + if (q.array[2] == 2) and not q:gt(R.E_MAX_SAFE_INTEGER) then + q.array[2] = 1 + q.array[1] = 10 ^ q.array[1] + end + if (q:gt(R.E_MAX_SAFE_INTEGER) or q:div(p):gt(R.MAX_SAFE_INTEGER)) then + t = q; + if n then + t = t:neg() + else + t = t + end + elseif (q.array[2] == nil) or (q.array[2] == 0) then + t = Big:create(x:to_number()-other:to_number()); + elseif (q.array[2]==1) then + if (p.array[2] ~= nil) and (p.array[2] ~= 0) then + a = p.array[1] + else + a = math.log(p.array[1], 10) + end + + t = Big:new({a+math.log(math.pow(10,q.array[1]-a)-1, 10),1}); + if n then + t = t:neg() + else + t = t + end + end + p = nil + q = nil + return t; +end + +function Big:div(other) + local x = self:clone(); + other = Big:create(other); + -- if (OmegaNum.debug>=OmegaNum.NORMAL) then + -- console.log(x+"/"+other); + if (x.sign*other.sign==-1) then + return x:abs():div(other:abs()):neg() + end + if (x.sign==-1) then + return x:abs():div(other:abs()) + end + if (x:isNaN() or other:isNaN() or (x:isInfinite() and other:isInfinite() and x:eq(other:neg()))) then + return Big:create(R.NaN) + end + if (other:eq(R.ZERO)) then + Big:create(R.POSITIVE_INFINITY) + end + if (other:eq(R.ONE)) then + return x:clone() + end + if (x:eq(other)) then + return Big:create(R.ONE) + end + if (x:isInfinite()) then + return x + end + if (other:isInfinite()) then + return Big:create(R.ZERO) + end + if (x:max(other):gt(R.EE_MAX_SAFE_INTEGER)) then + if x:gt(other) then + return x:clone() + else + return Big:create(R.ZERO) + end + end + local n = x:to_number()/other:to_number(); + if (n<=MAX_SAFE_INTEGER) then + return Big:create(n) + end + local pw = Big:create(10):pow(x:log10():sub(other:log10())) + local fp = pw:floor() + if (pw:sub(fp):lt(Big:create(1e-9))) then + return fp + end + return pw +end + +function Big:mul(other) + local x = self:clone(); + other = Big:create(other); + -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log(x+"*"+other); + if (x.sign*other.sign==-1) then + return x:abs():mul(other:abs()):neg() + end + if (x.sign==-1) then + return x:abs():mul(other:abs()) + end + if (x:isNaN() or other:isNaN() or (x:isInfinite() and other:isInfinite() and x:eq(other:neg()))) then + return Big:create(R.NaN) + end + if (other:eq(R.ZERO)) then + return Big:create(R.ZERO) + end + if (other:eq(R.ONE)) then + return x:clone() + end + if (x:isInfinite()) then + return x + end + if (other:isInfinite()) then + return other + end + if (x:max(other):gt(R.EE_MAX_SAFE_INTEGER)) then + return x:max(other) + end + local n = x:to_number()*other:to_number() + if (n<=MAX_SAFE_INTEGER) then + return Big:create(n) + end + return Big:create(10):pow(x:log10():add(other:log10())); +end + +function Big:rec() + if (self:isNaN() or self:eq(R.ZERO)) then + return Big:create(R.NaN) + end + if (self:abs():gt("2e323")) then + return Big:create(R.ZERO) + end + return Big:create(R.ONE):div(self) +end + +function Big:logBase(base) + if base == nil then + base = Big:create(R.E) + end + return self:log10():div(base:log10()) +end + +function Big:log10() + local x = self:clone(); + -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log("log"+this); + if (x:lt(R.ZERO)) then + return Big:create(R.NaN) + end + if (x:eq(R.ZERO)) then + return Big:create(R.NEGATIVE_INFINITY) + end + if (x:lte(R.MAX_SAFE_INTEGER)) then + return Big:create(math.log(x:to_number(), 10)) + end + if (not x:isFinite()) then + return x; + end + if (x:gt(R.TETRATED_MAX_SAFE_INTEGER)) then + return x; + end + x.array[2] = x.array[2] - 1; + return x:normalize() +end + +function Big:ln() + base = Big:create(R.E) + return self:log10():div(base:log10()) +end + +function Big:pow(other) + other = Big:create(other); + -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log(this+"^"+other); + if (other:eq(R.ZERO)) then + return Big:create(R.ONE) + end + if (other:eq(R.ONE)) then + return self:clone() + end + if (other:lt(R.ZERO)) then + return self:pow(other:neg()):rec() + end + if (self:lt(R.ZERO) and other:isint()) then + if (other:mod(2):lt(R.ONE)) then + return self:abs():pow(other) + end + return self:abs():pow(other):neg() + end + if (self:lt(R.ZERO)) then + return Big:create(R.NaN) + end + if (self:eq(R.ONE)) then + return Big:create(R.ONE) + end + if (self:eq(R.ZERO)) then + return Big:create(R.ZERO) + end + if (self:max(other):gt(R.TETRATED_MAX_SAFE_INTEGER)) then + return self:max(other); + end + if (self:eq(10)) then + if (other:gt(R.ZERO)) then + other.array[2] = (other.array[2] or 0) + 1; + other:normalize(); + return other; + else + return Big:create(math.pow(10,other:to_number())); + end + end + if (other:lt(R.ONE)) then + return self:root(other:rec()) + end + local n = math.pow(self:to_number(),other:to_number()) + if (n<=MAX_SAFE_INTEGER) then + return Big:create(n); + end + return Big:create(10):pow(self:log10():mul(other)); +end + +function Big:exp() + return Big:create(R.E, self) +end + +function Big:root(other) + other = Big:create(other) + -- if (OmegaNum.debug>=OmegaNum.NORMAL) console.log(this+"root"+other); + if (other:eq(R.ONE)) then + return self:clone() + end + if (other:lt(R.ZERO)) then + return self:root(other:neg()):rec() + end + if (other:lt(R.ONE)) then + return self:pow(other:rec()) + end + if (self:lt(R.ZERO) and other:isint() and other:mod(2):eq(R.ONE)) then + return self:neg():root(other):neg() + end + if (self:lt(R.ZERO)) then + return Big:create(R.NaN) + end + if (self:eq(R.ONE)) then + return Big:create(R.ONE) + end + if (self:eq(R.ZERO)) then + return Big:create(R.ZERO) + end + if (self:max(other):gt(R.TETRATED_MAX_SAFE_INTEGER)) then + if self:gt(other) then + return self:clone() + else + Big:create(R.ZERO) + end + end + return Big:create(10):pow(self:log10():div(other)); +end + +function Big:slog(base) + if base == nil then + base = 10 + end + local x = Big:create(self) + base = Big:create(base) + if (x:isNaN() or base:isNaN() or (x:isInfinite() and base:isInfinite())) then + return Big:create(R.NaN) + end + if (x:isInfinite()) then + return x; + end + if (base:isInfinite()) then + return Big:create(R.ZERO) + end + if (x:lt(R.ZERO)) then + return Big:create(-R.ONE) + end + if (x:lt(R.ONE)) then + return Big:create(R.ZERO) + end + if (x:eq(base)) then + return Big:create(R.ONE) + end + if (base:lt(math.exp(1/R.E))) then + local a = base:tetrate(1/0) + if (x:eq(a)) then + return Big:create(R.POSITIVE_INFINITY) + end + if (x:gt(a)) then + return Big:create(R.NaN) + end + end + if (x:max(base):gt("10^^^" .. R.MAX_SAFE_INTEGER)) then + if (x:gt(base)) then + return x; + end + return Big:create(R.ZERO) + end + if (x:max(base):gt(R.TETRATED_MAX_SAFE_INTEGER)) then + if x:gt(base) then + x.array[3] = x.array[3] - 1 + x:normalize() + return x:sub(x.array[2]) + end + return Big:create(R.ZERO) + end + local r = 0 + local t = (x.array[2] or 0) - (base.array[2] or 0) + if (t > 3) then + local l = t - 3 + r = r + l + x.array[2] = x.array[2] - l + end + for i = 0, 99 do + if x:lt(R.ZERO) then + x = base:pow(x) + r = r - 1 + elseif (x:lte(R.ONE)) then + return Big:create(r + x:to_number() - 1) + else + r = r + 1 + x = x:logBase(base) + end + end + if (x:gt(10)) then + return Big:create(r) + end +end + +function Big:tetrate(other) + local t = self:clone() + other = Big:create(other) + local negln = nil + if (t:isNaN() or other:isNaN()) then + return Big:create(R.NaN) + end + if (other:isInfinite() and other.sign > 0) then + negln = t:ln():neg() + return negln:lambertw():div(negln) + end + if (other:lte(-2)) then + return Big:create(R.NaN) + end + if (t:eq(R.ZERO)) then + if (other:eq(R.ZERO)) then + return Big:create(R.NaN) + end + if (other:mod(2):eq(R.ZERO)) then + return Big:create(R.ZERO) + end + return Big:create(R.ONE) + end + if (t:eq(R.ONE)) then + if (other:eq(-1)) then + return Big:create(R.NaN) + end + return Big:create(R.ONE) + end + if (other:eq(-1)) then + return Big:create(R.ZERO) + end + if other:eq(R.ZERO) then + return Big:create(R.ONE) + end + if other:eq(R.ONE) then + return t + end + if other:eq(2) then + return t:pow(t) + end + if t:eq(2) then + if other:eq(3) then + return Big:create({16}) + end + if other:eq(4) then + return Big:create({65536}) + end + end + local m = t:max(other) + if (m:gt(Big:create("10^^^" .. tostring(R.MAX_SAFE_INTEGER)))) then + return m + end + if (m:gt(R.TETRATED_MAX_SAFE_INTEGER) or other:gt(MAX_SAFE_INTEGER)) then + if (t:lt(math.exp(1/R.E))) then + negln = t:ln():neg() + return negln:lambertw():div(negln) + end + local j = t:slog(10):add(other) + j.array[3]=(j.array[3] or 0) + 1 + j:normalize() + return j + end + local y = other:to_number() + local f = math.floor(y) + local r = t:pow(y-f) + local l = Big:create(R.NaN) + local i = 0 + local m = Big:create(R.E_MAX_SAFE_INTEGER) + while ((f ~= 0) and r:lt(m) and (i < 100)) do + if (f > 0) then + r = t:pow(r) + if (l:eq(r)) then + f = 0 + break + end + l = r + f = f - 1 + else + r = r:logBase(t) + if (l:eq(r)) then + f = 0 + break + end + l = r + f = f + 1 + end + end + if ((i == 100) or t:lt(math.exp(1/R.E))) then + f = 0 + end + r.array[2] = (r.array[2] or 0) + f + r:normalize() + return r; +end + +function Big:arrow(arrows, other) + local t = self:clone() + arrows = Big:create(arrows) + if (not arrows:isint() or arrows:lt(R.ZERO)) then + return Big:create(R.NaN) + end + if arrows:eq(R.ZERO) then + return t:mul(other) + end + if arrows:eq(R.ONE) then + return t:pow(other) + end + if arrows:eq(2) then + return t:tetrate(other) + end + other = Big:create(other) + if (other:lt(R.ZERO)) then + return Big:create(R.NaN) + end + if (other:eq(R.ZERO)) then + return Big:create(R.ONE) + end + if (other:eq(R.ONE)) then + return t:clone() + end + --[[if (arrows:gte(maxArrow)) then + return Big:create(R.POSITIVE_INFINITY) + end--]] + local arrowsNum = arrows:to_number() + if (other:eq(2)) then + return t:arrow(arrows:sub(R.ONE), t) + end + if (t:max(other):gt("10{"..tostring(arrowsNum+1).."}"..tostring(R.MAX_SAFE_INTEGER))) then + return t:max(other) + end + local r = nil + if (t:gt("10{"..tostring(arrowsNum).."}"..tostring(R.MAX_SAFE_INTEGER)) or other:gt(R.MAX_SAFE_INTEGER)) then + if (t:gt("10{"..tostring(arrowsNum).."}"..tostring(R.MAX_SAFE_INTEGER))) then + r = t:clone() + r.array[arrowsNum + 1] = r.array[arrowsNum + 1] - 1 + r:normalize() + elseif (t:gt("10{"..tostring(arrowsNum - 1).."}"..tostring(R.MAX_SAFE_INTEGER))) then + r = Big:create(t.array[arrowsNum]) + else + r = Big:create(R.ZERO) + end + local j = r:add(other) + j.array[arrowsNum+1] = (j.array[arrowsNum+1] or 0) + 1 + j:normalize() + return j + end + local y = other:to_number() + local f = math.floor(y) + local arrows_m1 = arrows:sub(R.ONE) + local i = 0 + local m = Big:create("10{"..tostring(arrowsNum - 1).."}"..tostring(R.MAX_SAFE_INTEGER)) + r = t:arrow(arrows_m1, y-f) + while (f ~= 0) and r:lt(m) and (i<100) do + if (f > 0) then + r = t:arrow(arrows_m1, r) + f = f - 1 + end + i = i + 1 + end + if (i == 100) then + f = 0 + end + r.array[arrowsNum] = (r.array[arrowsNum] or 0) + f + r:normalize() + return r +end + +function Big:mod(other) + other = Big:create(other) + if (other:eq(R.ZERO)) then + Big:create(R.ZERO) + end + if (self.sign*other.sign == -1) then + return self:abs():mod(other:abs()):neg() + end + if (self.sign==-1) then + return self:abs():mod(other:abs()) + end + return self:sub(self:div(other):floor():mul(other)) +end + +function Big:lambertw() + local x = self:clone() + if (x:isNaN()) then + return x; + end + if (x:lt(-0.3678794411710499)) then + print("lambertw is unimplemented for results less than -1, sorry!") + local a = nil + return a.b + end + if (x:gt(R.TETRATED_MAX_SAFE_INTEGER)) then + return x; + end + if (x:gt(R.EE_MAX_SAFE_INTEGER)) then + x.array[1] = x.array[1] - 1 + return x; + end + if (x:gt(R.E_MAX_SAFE_INTEGER)) then + return Big:d_lambertw(x) + else + return Big:create(Big:f_lambertw(x.sign*x.array[1])) + end +end + +function Big:f_lambertw(z) + local tol = 1e-10 + local w = nil + local wn = nil + local OMEGA = 0.56714329040978387299997 + if (not Big:create(z):isFinite()) then + return z; + end + if z == 0 then + return z; + end + if z == 1 then + return OMEGA + end + if (z < 10) then + w = 0 + else + w = math.log(z) - math.log(math.log(z)) + end + for i=0,99 do + wn = (z*math.exp(-w)+w*w)/(w+1) + if (math.abs(wn-w)= to_big(G.GAME.blind.chips) or G.GAME.current_round.hands_left < 1 then''' +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if G.GAME.chips - G.GAME.blind.chips >= 0 then" +position = "at" +payload = "if to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) then" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "blind.lua" +pattern = "if self.boss and G.GAME.chips - G.GAME.blind.chips >= 0 then" +position = "at" +payload = "if self.boss and to_big(G.GAME.chips) - G.GAME.blind.chips >= to_big(0) then" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "G.GAME.chips/G.GAME.blind.chips >= 0.25 then" +position = "at" +payload = "to_big(G.GAME.chips)/G.GAME.blind.chips >= to_big(0.25) then" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "if not G.TAROT_INTERRUPT_PULSE then G.FUNCS.text_super_juice(e, math.max(0,math.floor(math.log10(type(G.GAME.current_round.current_hand.mult) == 'number' and G.GAME.current_round.current_hand.mult or 1)))) end" +position = "at" +payload = "if not G.TAROT_INTERRUPT_PULSE then G.FUNCS.text_super_juice(e, math.min(2,math.max(0,math.floor(math.log10(is_number(G.GAME.current_round.current_hand.mult) and G.GAME.current_round.current_hand.mult or 1))))) end" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "if not G.TAROT_INTERRUPT_PULSE then G.FUNCS.text_super_juice(e, math.max(0,math.floor(math.log10(type(G.GAME.current_round.current_hand.chips) == 'number' and G.GAME.current_round.current_hand.chips or 1)))) end" +position = "at" +payload = "if not G.TAROT_INTERRUPT_PULSE then G.FUNCS.text_super_juice(e, math.min(2,math.max(0,math.floor(math.log10(is_number(G.GAME.current_round.current_hand.chips) and G.GAME.current_round.current_hand.chips or 1))))) end" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "if type(G.GAME.current_round.current_hand.chips) ~= 'number' or type(G.GAME.current_round.current_hand.mult) ~= 'number' then" +position = "at" +payload = "if not is_number(G.GAME.current_round.current_hand.chips) or not is_number(G.GAME.current_round.current_hand.mult) then" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "main.lua" +pattern = "if type(G.GAME.current_round.current_hand.chips) ~= 'number' or type(G.GAME.current_round.current_hand.mult) ~= 'number' then" +position = "at" +payload = "if not is_number(G.GAME.current_round.current_hand.chips) or not is_number(G.GAME.current_round.current_hand.mult) then" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "local delta = (type(vals.chips) == 'number' and type(G.GAME.current_round.current_hand.chips) == 'number') and (vals.chips - G.GAME.current_round.current_hand.chips) or 0" +position = "at" +payload = "local delta = (is_number(vals.chips) and is_number(G.GAME.current_round.current_hand.chips)) and (vals.chips - G.GAME.current_round.current_hand.chips) or 0" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "local delta = (type(vals.mult) == 'number' and type(G.GAME.current_round.current_hand.mult) == 'number')and (vals.mult - G.GAME.current_round.current_hand.mult) or 0" +position = "at" +payload = "local delta = (is_number(vals.mult) and is_number(G.GAME.current_round.current_hand.mult))and (vals.mult - G.GAME.current_round.current_hand.mult) or 0" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if delta < 0 then delta = ''..delta; col = G.C.RED" +position = "at" +payload = "if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "elseif delta > 0 then delta = '+'..delta" +position = "at" +payload = "elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta)" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "else delta = ''..delta" +position = "at" +payload = "else delta = number_format(delta)" +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "engine/string_packer.lua" +pattern = 'if type_v == "table" then' +position = "after" +payload = ''' +if v.m and v.e then +v = "to_big("..v.m..","..v.e..")" +elseif v.array and v.sign then + local v0 = "to_big({" + for qi = 1,#v.array do + v0 = v0 .. v.array[qi] .. ", " + end + v0 = v0 .. "},"..v.sign..")" + v = v0 +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "engine/string_packer.lua" +pattern = 'if v.is and v:is(Object) then' +position = "at" +payload = 'elseif v.is and v:is(Object) then' +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "functions/UI_definitions.lua" +pattern = "{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME.round_resets, ref_value = 'ante'}}, colours = {G.C.IMPORTANT},shadow = true, font = G.LANGUAGES['en-us'].font, scale = 2*scale}),id = 'ante_UI_count'}}," +position = "at" +payload = "{n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME.round_resets, ref_value = 'ante_disp'}}, colours = {G.C.IMPORTANT},shadow = true, font = G.LANGUAGES['en-us'].font, scale = scale_number(G.GAME.round_resets.ante, 2*scale, 100)}),id = 'ante_UI_count'}},--{n=G.UIT.T, config={text = number_format(G.GAME.round_resets.ante), lang = G.LANGUAGES['en-us'], scale = scale_number(G.GAME.round_resets.ante, 2*scale, 100), colour = G.C.IMPORTANT, shadow = true,id = 'ante_UI_count'}}," +match_indent = true +overwrite = true + +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "ante = 1," +position = "after" +payload = "ante_disp = number_format(1)," +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "G.GAME.round_resets.ante = G.GAME.round_resets.ante + mod" +position = "after" +payload = "G.GAME.round_resets.ante_disp = number_format(G.GAME.round_resets.ante)" +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "functions/button_callbacks.lua" +pattern = "if not G.ARGS.hand_chip_total_UI_set or G.ARGS.hand_chip_total_UI_set < G.GAME.current_round.current_hand.chip_total then" +position = "at" +payload = "if not G.ARGS.hand_chip_total_UI_set or to_big(G.ARGS.hand_chip_total_UI_set) < to_big(G.GAME.current_round.current_hand.chip_total) then" +match_indent = true +overwrite = true + + +# For some reason Big leaks into the text engine, this mitigates it +[[patches]] +[patches.pattern] +target = "engine/text.lua" +pattern = "if self.strings[k].W > self.config.W then self.config.W = self.strings[k].W; self.strings[k].W_offset = 0 end" +position = "before" +payload = ''' +if Big then + if type(self.strings[k].W) == 'table' then + self.strings[k].W = self.strings[k].W:to_number() + end + if type(self.strings[k].H) == 'table' then + self.strings[k].H = self.strings[k].H:to_number() + end +end +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "engine/text.lua" +pattern = "function DynaText:draw()" +position = "after" +payload = ''' +if Big then + self.scale = to_big(self.scale):to_number() + if self.shadow_parallax then self.shadow_parallax.x = to_big(self.shadow_parallax.x):to_number() end +end +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "engine/text.lua" +pattern = "for k, letter in ipairs(self.strings[self.focused_string].letters) do" +position = "after" +payload = ''' +if Big then + letter.dims.x = to_big(letter.dims.x):to_number() + letter.dims.y = to_big(letter.dims.y):to_number() + letter.offset.x = to_big(letter.offset.x):to_number() + letter.offset.y = to_big(letter.offset.y):to_number() +end +''' +match_indent = true +overwrite = false + +[[patches]] +[patches.pattern] +target = "engine/ui.lua" +pattern = "if _th and _tw then" +position = "after" +payload = ''' +if Big then + _th = to_big(_th):to_number() + _tw = to_big(_tw):to_number() +end +''' +match_indent = true +overwrite = false + +#these last few are causing lag, so they'll only be applied in the main menu +[[patches]] +[patches.pattern] +target = "engine/ui.lua" +pattern = "local _cw, _ch = w:set_wh()" +position = "after" +payload = "if Big and G.STATE == G.STATES.MENU then _cw = to_big(_cw):to_number(); _ch = to_big(_ch):to_number() end" +match_indent = true + +[[patches]] +[patches.pattern] +target = "engine/moveable.lua" +pattern = "function Moveable:move_wh(dt)" +position = "after" +payload = ''' +if Big and G.STATE == G.STATES.MENU then self.T.w = to_big(self.T.w):to_number() +self.T.h = to_big(self.T.h):to_number() +self.VT.w = to_big(self.VT.w):to_number() +self.VT.h = to_big(self.VT.h):to_number() end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/misc_functions.lua" +pattern = "function prep_draw(moveable, scale, rotate, offset)" +position = "after" +payload = ''' +if Big and G.STATE == G.STATES.MENU then moveable.VT.x = to_big(moveable.VT.x):to_number() +moveable.VT.y = to_big(moveable.VT.y):to_number() +moveable.VT.w = to_big(moveable.VT.w):to_number() +moveable.VT.h = to_big(moveable.VT.h):to_number() end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "engine/ui.lua" +pattern = "if self.config.vert then love.graphics.translate(0,self.VT.h); love.graphics.rotate(-math.pi/2) end" +position = "before" +payload = ''' +if Big and G.STATE == G.STATES.MENU then self.config.scale = to_big(self.config.scale):to_number() end +''' +match_indent = true + +# call init game object appendum after init game object +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "if new_game_obj then self.GAME = self:init_game_object() end" +position = "after" +payload = "if new_game_obj and Talisman and Talisman.igo then self.GAME = Talisman.igo(self.GAME) end" +match_indent = true +overwrite = false + +# call init game object appendum after init game object +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "self.GAME = saveTable and saveTable.GAME or self:init_game_object()" +position = "after" +payload = "if (not saveTable or not saveTable.GAME) and Talisman and Talisman.igo then self.GAME = Talisman.igo(self.GAME) end" +match_indent = true +overwrite = false + +# remove more animations from event queue +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if effects[ii].card then" +position = "at" +payload = "if effects[ii].card and not Talisman.config_file.disable_anims then" +match_indent = true +overwrite = false + +#enhancement animation removing +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if self.edition and not silent then" +position = "at" +payload = "if self.edition and (not Talisman.config_file.disable_anims or (not Talisman.calculating_joker and not Talisman.calculating_score and not Talisman.calculating_card)) and not silent then" +match_indent = true +overwrite = false + +# dollar buffer thingy +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)}))" +position = "at" +payload = "if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end" +match_indent = true +overwrite = false + +# add card +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end" +position = "at" +payload = "if G.GAME.blind and (not SMODS or G.GAME.blind.in_blind) and not (Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card)) then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end" +match_indent = true +overwrite = false + +# Juice Issues +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "func = (function() card:juice_up(0.7);return true end)" +position = "at" +payload = "func = (function() if card and card.juice_up then card:juice_up(0.7) end;return true end)" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "if card then card:juice_up(0.8, 0.5) end" +position = "at" +payload = "if card and card.juice_up then card:juice_up(0.8, 0.5) end" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "card:juice_up(0.6, 0.1)" +position = "at" +payload = "if card and card.juice_up then card:juice_up(0.6, 0.1) end" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)" +position = "at" +payload = "func = (function() if eval_func(card) then if card and card.juice_up then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)" +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)" +position = "at" +payload = "func = (function() if eval_func(card) then if card and card.juice_up then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)" +match_indent = true + +# return eval_round value +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "add_round_eval_row({name = 'bottom', dollars = dollars})" +position = "after" +payload = "Talisman.dollars = dollars" +match_indent = true + +# arbitrary operations on chips and mult +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if effects.jokers.Xmult_mod then mult = mod_mult(mult*effects.jokers.Xmult_mod);extras.mult = true end" +position = "after" +payload = ''' +if effects.jokers.Emult_mod then mult = mod_mult(mult^effects.jokers.Emult_mod);extras.mult = true end +if effects.jokers.EEmult_mod then mult = mod_mult(mult:arrow(2, effects.jokers.EEmult_mod));extras.mult = true end +if effects.jokers.EEEmult_mod then mult = mod_mult(mult:arrow(3, effects.jokers.EEEmult_mod));extras.mult = true end +if effects.jokers.hypermult_mod and type(effects.jokers.hypermult_mod) == 'table' then mult = mod_mult(mult:arrow(effects.jokers.hypermult_mod[1], effects.jokers.hypermult_mod[2]));extras.mult = true end +if effects.jokers.Xchip_mod then hand_chips = mod_chips(hand_chips*effects.jokers.Xchip_mod);extras.hand_chips = true end +if effects.jokers.Echip_mod then hand_chips = mod_chips(hand_chips^effects.jokers.Echip_mod);extras.hand_chips = true end +if effects.jokers.EEchip_mod then hand_chips = mod_chips(hand_chips:arrow(2, effects.jokers.EEchip_mod));extras.hand_chips = true end +if effects.jokers.EEEchip_mod then hand_chips = mod_chips(hand_chips:arrow(3, effects.jokers.EEEchip_mod));extras.hand_chips = true end +if effects.jokers.hyperchip_mod and type(effects.jokers.hyperchip_mod) == 'table' then hand_chips = mod_chips(hand_chips:arrow(effects.jokers.hyperchip_mod[1], effects.jokers.hyperchip_mod[2]));extras.hand_chips = true end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if effect.Xmult_mod then mult = mod_mult(mult*effect.Xmult_mod);extras.mult = true end" +position = "after" +payload = ''' +if effect.Emult_mod then mult = mod_mult(mult^effect.Emult_mod);extras.mult = true end +if effect.EEmult_mod then mult = mod_mult(mult:arrow(2, effect.EEmult_mod));extras.mult = true end +if effect.EEEmult_mod then mult = mod_mult(mult:arrow(3, effect.EEEmult_mod));extras.mult = true end +if effect.hypermult_mod and type(effect.hypermult_mod) == 'table' then mult = mod_mult(mult:arrow(effect.hypermult_mod[1], effect.hypermult_mod[2]));extras.mult = true end +if effect.Xchip_mod then hand_chips = mod_chips(hand_chips*effect.Xchip_mod);extras.hand_chips = true end +if effect.Echip_mod then hand_chips = mod_chips(hand_chips^effect.Echip_mod);extras.hand_chips = true end +if effect.EEchip_mod then hand_chips = mod_chips(hand_chips:arrow(2, effect.EEchip_mod));extras.hand_chips = true end +if effect.EEEchip_mod then hand_chips = mod_chips(hand_chips:arrow(3, effect.EEEchip_mod));extras.hand_chips = true end +if effect.hyperchip_mod and type(effect.hyperchip_mod) == 'table' then hand_chips = mod_chips(hand_chips:arrow(effect.hyperchip_mod[1], effect.hyperchip_mod[2]));extras.hand_chips = true end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "card.lua" +pattern = "x_mult = center.config.Xmult or 1," +position = "after" +payload = ''' +e_mult = center.config.Emult or 0, +ee_mult = center.config.EEmult or 0, +eee_mult = center.config.EEEmult or 0, +hyper_mult = type(center.config.Hmult) == 'table' and center.config.Hmult or {0, 0}, +x_chips = center.config.Xchips or 0, +e_chips = center.config.Echips or 0, +ee_chips = center.config.EEchips or 0, +eee_chips = center.config.EEEchips or 0, +hyper_chips = type(center.config.Hchips) == 'table' and center.config.Hchips or {0, 0}, +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "local p_dollars = card:get_p_dollars()" +position = "before" +payload = ''' +local x_chips = card:get_chip_x_bonus() +if x_chips > 0 then + ret.x_chips = x_chips +end + +local e_chips = card:get_chip_e_bonus() +if e_chips > 0 then + ret.e_chips = e_chips +end + +local ee_chips = card:get_chip_ee_bonus() +if ee_chips > 0 then + ret.ee_chips = ee_chips +end + +local eee_chips = card:get_chip_eee_bonus() +if eee_chips > 0 then + ret.eee_chips = eee_chips +end + +local hyper_chips = card:get_chip_hyper_bonus() +if type(hyper_chips) == 'table' and hyper_chips[1] > 0 and hyper_chips[2] > 0 then + ret.hyper_chips = hyper_chips +end + +local e_mult = card:get_chip_e_mult() +if e_mult > 0 then + ret.e_mult = e_mult +end + +local ee_mult = card:get_chip_ee_mult() +if ee_mult > 0 then + ret.ee_mult = ee_mult +end + +local eee_mult = card:get_chip_eee_mult() +if eee_mult > 0 then + ret.eee_mult = eee_mult +end + +local hyper_mult = card:get_chip_hyper_mult() +if type(hyper_mult) == 'table' and hyper_mult[1] > 0 and hyper_mult[2] > 0 then + ret.hyper_mult = hyper_mult +end +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "elseif eval_type == 'dollars' then" +position = "before" +payload = ''' +elseif eval_type == 'x_chips' then + sound = 'talisman_xchip' + amt = amt + text = 'X' .. amt + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 +elseif eval_type == 'e_chips' then + sound = 'talisman_echip' + amt = amt + text = '^' .. amt + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 +elseif eval_type == 'ee_chips' then + sound = 'talisman_eechip' + amt = amt + text = '^^' .. amt + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 +elseif eval_type == 'eee_chips' then + sound = 'talisman_eeechip' + amt = amt + text = '^^^' .. amt + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 +elseif eval_type == 'hyper_chips' then + sound = 'talisman_eeechip' + text = (amt[1] > 5 and ('{' .. tostring(amt[1]) .. '}') or string.rep('^', amt[1])) .. tostring(amt[2]) + amt = amt[2] + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 +elseif eval_type == 'e_mult' then + sound = 'talisman_emult' + amt = amt + text = '^' .. amt .. ' Mult' + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 +elseif eval_type == 'ee_mult' then + sound = 'talisman_eemult' + amt = amt + text = '^^' .. amt .. ' Mult' + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 +elseif eval_type == 'eee_mult' then + sound = 'talisman_eeemult' + amt = amt + text = '^^^' .. amt .. ' Mult' + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 +elseif eval_type == 'hyper_mult' then + sound = 'talisman_eeemult' + text = (amt[1] > 5 and ('{' .. tostring(amt[1]) .. '}') or string.rep('^', amt[1])) .. tostring(amt[2]) .. ' Mult' + amt = amt[2] + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "if effects[ii].message then" +position = "before" +payload = ''' +if effects[ii].x_chips then + mod_percent = true + hand_chips = mod_chips(hand_chips*effects[ii].x_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'x_chips', effects[ii].x_chips, percent) +end +if effects[ii].e_chips then + mod_percent = true + hand_chips = mod_chips(hand_chips^effects[ii].e_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'e_chips', effects[ii].e_chips, percent) +end +if effects[ii].ee_chips then + mod_percent = true + hand_chips = mod_chips(hand_chips:arrow(2, effects[ii].ee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'ee_chips', effects[ii].ee_chips, percent) +end +if effects[ii].eee_chips then + mod_percent = true + hand_chips = mod_chips(hand_chips:arrow(3, effects[ii].eee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'eee_chips', effects[ii].eee_chips, percent) +end +if effects[ii].hyper_chips and type(effects[ii].hyper_chips) == 'table' then + mod_percent = true + hand_chips = mod_chips(hand_chips:arrow(effects[ii].hyper_chips[1], effects[ii].hyper_chips[2])) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'hyper_chips', effects[ii].hyper_chips, percent) +end +if effects[ii].e_mult then + mod_percent = true + mult = mod_mult(mult^effects[ii].e_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'e_mult', effects[ii].e_mult, percent) +end +if effects[ii].ee_mult then + mod_percent = true + mult = mod_mult(mult:arrow(2, effects[ii].ee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'ee_mult', effects[ii].ee_mult, percent) +end +if effects[ii].eee_mult then + mod_percent = true + mult = mod_mult(mult:arrow(3, effects[ii].eee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'eee_mult', effects[ii].eee_mult, percent) +end +if effects[ii].hyper_mult and type(effects[ii].hyper_mult) == 'table' then + mod_percent = true + mult = mod_mult(mult:arrow(effects[ii].hyper_mult[1], effects[ii].hyper_mult[2])) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'hyper_mult', effects[ii].hyper_mult, percent) +end + +''' +match_indent = true + +# sfx +[[patches]] +[patches.pattern] +target = "functions/common_events.lua" +pattern = "sound = extra.edition and 'foil2' or extra.mult_mod and 'multhit1' or extra.Xmult_mod and 'multhit2' or 'generic1'" +position = "at" +payload = '''sound = extra.edition and 'foil2' or extra.mult_mod and 'multhit1' or extra.Xmult_mod and 'multhit2' or extra.Xchip_mod and 'talisman_xchip' or extra.Echip_mod and 'talisman_echip' or extra.Emult_mod and 'talisman_emult' or extra.EEchip_mod and 'talisman_eechip' or extra.EEmult_mod and 'talisman_eemult' or (extra.EEEchip_mod or extra.hyperchip_mod) and 'talisman_eeechip' or (extra.EEEmult_mod or extra.hypermult_mod) and 'talisman_eeemult' or 'generic1' +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = "functions/state_events.lua" +pattern = "--calculate the card edition effects" +position = "before" +payload = ''' +if effects[ii].x_chips then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips*effects[ii].x_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'x_chips', effects[ii].x_chips, percent) +end +if effects[ii].e_chips then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips^effects[ii].e_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'e_chips', effects[ii].e_chips, percent) +end +if effects[ii].ee_chips then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips:arrow(2, effects[ii].ee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'ee_chips', effects[ii].ee_chips, percent) +end +if effects[ii].eee_chips then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips:arrow(3, effects[ii].eee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'eee_chips', effects[ii].eee_chips, percent) +end +if effects[ii].hyper_chips and type(effects[ii].hyper_chips) == 'table' then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips:arrow(effects[ii].hyper_chips[1], effects[ii].hyper_chips[2])) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'hyper_chips', effects[ii].hyper_chips, percent) +end +if effects[ii].e_mult then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult^effects[ii].e_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'e_mult', effects[ii].e_mult, percent) +end +if effects[ii].ee_mult then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult:arrow(2, effects[ii].ee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'ee_mult', effects[ii].ee_mult, percent) +end +if effects[ii].eee_mult then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult:arrow(3, effects[ii].eee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'eee_mult', effects[ii].eee_mult, percent) +end +if effects[ii].hyper_mult and type(effects[ii].hyper_mult) == 'table' then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult:arrow(effects[ii].hyper_mult[1], effects[ii].hyper_mult[2])) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'hyper_mult', effects[ii].hyper_mult, percent) +end + +''' +match_indent = true + +[[patches]] +[patches.pattern] +target = 'functions/state_events.lua' +pattern = 'if effects[ii].edition.p_dollars_mod then' +position = 'before' +match_indent = true +payload = ''' +if scoring_hand and scoring_hand[i] and scoring_hand[i].edition then + local trg = scoring_hand[i] + local edi = trg.edition + if edi.x_chips then + hand_chips = mod_chips(hand_chips * edi.x_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = 'X'.. edi.x_chips ..' Chips', + edition = true, + x_chips = true}) + end + if edi.e_chips then + hand_chips = mod_chips(hand_chips ^ edi.e_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^'.. edi.e_chips ..' Chips', + edition = true, + e_chips = true}) + end + if edi.ee_chips then + hand_chips = mod_chips(hand_chips:arrow(2, edi.ee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^'.. edi.ee_chips ..' Chips', + edition = true, + ee_chips = true}) + end + if edi.eee_chips then + hand_chips = mod_chips(hand_chips:arrow(3, edi.eee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^^'.. edi.eee_chips ..' Chips', + edition = true, + eee_chips = true}) + end + if edi.hyper_chips and type(edi.hyper_chips) == 'table' then + hand_chips = mod_chips(hand_chips:arrow(edi.hyper_chips[1], edi.hyper_chips[2])) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = (edi.hyper_chips[1] > 5 and ('{' .. edi.hyper_chips[1] .. '}') or string.rep('^', edi.hyper_chips[1])) .. edi.hyper_chips[2] ..' Chips', + edition = true, + eee_chips = true}) + end + if edi.e_mult then + mult = mod_mult(mult ^ edi.e_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^'.. edi.e_mult ..' Mult', + edition = true, + e_mult = true}) + end + if edi.ee_mult then + mult = mod_mult(mult:arrow(2, edi.ee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^'.. edi.ee_mult ..' Mult', + edition = true, + ee_mult = true}) + end + if edi.eee_mult then + mult = mod_mult(mult:arrow(3, edi.eee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^^'.. edi.eee_mult ..' Mult', + edition = true, + eee_mult = true}) + end + if edi.hyper_mult and type(edi.hyper_mult) == 'table' then + mult = mod_mult(mult:arrow(edi.hyper_mult[1], edi.hyper_mult[2])) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = (edi.hyper_mult[1] > 5 and ('{' .. edi.hyper_mult[1] .. '}') or string.rep('^', edi.hyper_mult[1])) .. edi.hyper_mult[2] ..' Mult', + edition = true, + hyper_mult = true}) + end +end +''' + +# my fix for this one is really scuffed +[[patches]] +[patches.pattern] +target = 'functions/state_events.lua' +pattern = 'if edition_effects.jokers.x_mult_mod then' +position = 'before' +match_indent = true +payload = ''' +if edition_effects.jokers.x_mult_mod then + mult = mod_mult(mult*edition_effects.jokers.x_mult_mod) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(_card, 'jokers', nil, percent, nil, { + message = localize{type='variable',key='a_xmult',vars={edition_effects.jokers.x_mult_mod}}, + x_mult_mod = edition_effects.jokers.x_mult_mod, + colour = G.C.EDITION, + edition = true}) +end +if G.jokers.cards and G.jokers.cards[i] and G.jokers.cards[i].edition then + local trg = G.jokers.cards[i] + local edi = trg.edition + if edi.x_chips then + hand_chips = mod_chips(hand_chips * edi.x_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = 'X'.. edi.x_chips ..' Chips', + edition = true, + x_chips = true}) + end + if edi.e_chips then + hand_chips = mod_chips(hand_chips ^ edi.e_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^'.. edi.e_chips ..' Chips', + edition = true, + e_chips = true}) + end + if edi.ee_chips then + hand_chips = mod_chips(hand_chips:arrow(2, edi.ee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^'.. edi.ee_chips ..' Chips', + edition = true, + ee_chips = true}) + end + if edi.eee_chips then + hand_chips = mod_chips(hand_chips:arrow(3, edi.eee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^^'.. edi.eee_chips ..' Chips', + edition = true, + eee_chips = true}) + end + if edi.hyper_chips and type(edi.hyper_chips) == 'table' then + hand_chips = mod_chips(hand_chips:arrow(edi.hyper_chips[1], edi.hyper_chips[2])) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = (edi.hyper_chips[1] > 5 and ('{' .. edi.hyper_chips[1] .. '}') or string.rep('^', edi.hyper_chips[1])) .. edi.hyper_chips[2] ..' Chips', + edition = true, + eee_chips = true}) + end + if edi.e_mult then + mult = mod_mult(mult ^ edi.e_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^'.. edi.e_mult ..' Mult', + edition = true, + e_mult = true}) + end + if edi.ee_mult then + mult = mod_mult(mult:arrow(2, edi.ee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^'.. edi.ee_mult ..' Mult', + edition = true, + ee_mult = true}) + end + if edi.eee_mult then + mult = mod_mult(mult:arrow(3, edi.eee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^^'.. edi.eee_mult ..' Mult', + edition = true, + eee_mult = true}) + end + if edi.hyper_mult and type(edi.hyper_mult) == 'table' then + mult = mod_mult(mult:arrow(edi.hyper_mult[1], edi.hyper_mult[2])) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = (edi.hyper_mult[1] > 5 and ('{' .. edi.hyper_mult[1] .. '}') or string.rep('^', edi.hyper_mult[1])) .. edi.hyper_mult[2] ..' Mult', + edition = true, + hyper_mult = true}) + end +end +edition_effects.jokers.x_mult_mod = nil +''' + +# replace save manager with version that works with Talisman saves +[[patches]] +[patches.pattern] +target = "game.lua" +pattern = "thread = love.thread.newThread('engine/save_manager.lua')," +position = "at" +payload = ''' +thread = love.thread.newThread([[require "love.system" + +if (love.system.getOS() == 'OS X' ) and (jit.arch == 'arm64' or jit.arch == 'arm') then jit.off() end + +require "love.timer" +require "love.thread" +require 'love.filesystem' +require "engine/object" +require "engine/string_packer" + +--vars needed for sound manager thread +CHANNEL = love.thread.getChannel("save_request") + +talisman = "]] .. Talisman.config_file.break_infinity .. [[" + +--untested +function tal_compress_and_save(_file, _data) + local save_string = type(_data) == 'table' and STR_PACK(_data) or _data + local fallback_save = STR_PACK({GAME = {won = true}}) --just bare minimum to not crash, maybe eventually display some info? + if talisman == 'bignumber' then + fallback_save = "if not BigMeta then " .. fallback_save + elseif talisman == 'omeganum' then + fallback_save = "if not OmegaMeta then " .. fallback_save + else + fallback_save = "if BigMeta or OmegaMeta then " .. fallback_save + end + fallback_save = fallback_save .. " end" + save_string = fallback_save .. " " .. save_string + save_string = love.data.compress('string', 'deflate', save_string, 1) + love.filesystem.write(_file,save_string) +end + + while true do + --Monitor the channel for any new requests + local request = CHANNEL:demand() -- Value from channel + if request then + --Saves progress for settings, unlocks, alerts and discoveries + if request.type == 'save_progress' then + local prefix_profile = (request.save_progress.SETTINGS.profile or 1)..'' + if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end + prefix_profile = prefix_profile..'/' + + if not love.filesystem.getInfo(prefix_profile..'meta.jkr') then + love.filesystem.append( prefix_profile..'meta.jkr', 'return {}' ) + end + + local meta = STR_UNPACK(get_compressed(prefix_profile..'meta.jkr') or 'return {}') + meta.unlocked = meta.unlocked or {} + meta.discovered = meta.discovered or {} + meta.alerted = meta.alerted or {} + + local _append = false + + for k, v in pairs(request.save_progress.UDA) do + if string.find(v, 'u') and not meta.unlocked[k] then + meta.unlocked[k] = true + _append = true + end + if string.find(v, 'd') and not meta.discovered[k] then + meta.discovered[k] = true + _append = true + end + if string.find(v, 'a') and not meta.alerted[k] then + meta.alerted[k] = true + _append = true + end + end + if _append then compress_and_save( prefix_profile..'meta.jkr', STR_PACK(meta)) end + + compress_and_save('settings.jkr', request.save_progress.SETTINGS) + compress_and_save(prefix_profile..'profile.jkr', request.save_progress.PROFILE) + + CHANNEL:push('done') + --Saves the settings file + elseif request.type == 'save_settings' then + compress_and_save('settings.jkr', request.save_settings) + compress_and_save(request.profile_num..'/profile.jkr', request.save_profile) + --Saves the metrics file + elseif request.type == 'save_metrics' then + compress_and_save('metrics.jkr', request.save_metrics) + --Saves any notifications + elseif request.type == 'save_notify' then + local prefix_profile = (request.profile_num or 1)..'' + if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end + prefix_profile = prefix_profile..'/' + + if not love.filesystem.getInfo(prefix_profile..'unlock_notify.jkr') then love.filesystem.append( prefix_profile..'unlock_notify.jkr', '') end + local unlock_notify = get_compressed(prefix_profile..'unlock_notify.jkr') or '' + + if request.save_notify and not string.find(unlock_notify, request.save_notify) then + compress_and_save( prefix_profile..'unlock_notify.jkr', unlock_notify..request.save_notify..'\n') + end + + --Saves the run + elseif request.type == 'save_run' then + local prefix_profile = (request.profile_num or 1)..'' + if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end + prefix_profile = prefix_profile..'/' + + tal_compress_and_save(prefix_profile..'save.jkr', request.save_table) + end + end +end]]), +''' +match_indent = true + +# naneinf crash fixes +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = "if G.GAME.round_scores[score] and math.floor(amt) > G.GAME.round_scores[score].amt then" +position = "at" +payload = ''' +if G.GAME.round_scores[score] and math.floor(amt) > (G.GAME.round_scores[score].amt or 0) then''' +match_indent = true + +[[patches]] +[patches.pattern] +target = 'game.lua' +pattern = "function Game:update_hand_played(dt)" +position = "after" +payload = ''' +G.GAME.chips = (G.GAME.chips or 0) +G.GAME.blind.chips = (G.GAME.blind.chips or math.huge)''' +match_indent = true +# Naneinf crash fixes end + +# Scoring coroutine +[[patches]] +[patches.pattern] +target = 'functions/state_events.lua' +pattern = "check_for_unlock({type = 'play_all_hearts'})" +position = 'before' +payload = 'if G.SCORING_COROUTINE then return false end ' +match_indent = true + +[[patches]] +[patches.pattern] +target = 'functions/state_events.lua' +pattern = ''' G.STATE_COMPLETE = false + return true +''' +position = 'before' +payload = 'if G.SCORING_COROUTINE then return false end ' +match_indent = false + +# Don't crash in vanilla with big numbers in UI +[[patches]] +[patches.pattern] +target = 'functions/misc_functions.lua' +pattern = "assembled_string = assembled_string..(type(subpart) == 'string' and subpart or args.vars[tonumber(subpart[1])] or 'ERROR')" +position = 'before' +payload = ''' +if not SMODS and type(subpart) ~= 'string' then + if type(args.vars[tonumber(subpart[ 1 ])]) ~= 'number' then + args.vars[tonumber(subpart[ 1 ])] = tostring(args.vars[tonumber(subpart[ 1 ])]) + else + args.vars[tonumber(subpart[ 1 ])] = number_format(args.vars[tonumber(subpart[ 1 ])], 1000000) + end +end +''' +match_indent = false \ No newline at end of file diff --git a/Talisman/nativefs.lua b/Talisman/nativefs.lua new file mode 100644 index 0000000..f8c2b18 --- /dev/null +++ b/Talisman/nativefs.lua @@ -0,0 +1,495 @@ +--[[ +Copyright 2020 megagrump@pm.me + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +of the Software, and to permit persons to whom the Software is furnished to do +so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]]-- + +-- module("nativefs", package.seeall) + +local ffi, bit = require('ffi'), require('bit') +local C = ffi.C + +local fopen, getcwd, chdir, unlink, mkdir, rmdir +local BUFFERMODE, MODEMAP +local ByteArray = ffi.typeof('unsigned char[?]') +local function _ptr(p) return p ~= nil and p or nil end -- NULL pointer to nil + +local File = { + getBuffer = function(self) return self._bufferMode, self._bufferSize end, + getFilename = function(self) return self._name end, + getMode = function(self) return self._mode end, + isOpen = function(self) return self._mode ~= 'c' and self._handle ~= nil end, +} + +function File:open(mode) + if self._mode ~= 'c' then return false, "File " .. self._name .. " is already open" end + if not MODEMAP[mode] then return false, "Invalid open mode for " .. self._name .. ": " .. mode end + + local handle = _ptr(fopen(self._name, MODEMAP[mode])) + if not handle then return false, "Could not open " .. self._name .. " in mode " .. mode end + + self._handle, self._mode = ffi.gc(handle, C.fclose), mode + self:setBuffer(self._bufferMode, self._bufferSize) + + return true +end + +function File:close() + if self._mode == 'c' then return false, "File is not open" end + C.fclose(ffi.gc(self._handle, nil)) + self._handle, self._mode = nil, 'c' + return true +end + +function File:setBuffer(mode, size) + local bufferMode = BUFFERMODE[mode] + if not bufferMode then + return false, "Invalid buffer mode " .. mode .. " (expected 'none', 'full', or 'line')" + end + + if mode == 'none' then + size = math.max(0, size or 0) + else + size = math.max(2, size or 2) -- Windows requires buffer to be at least 2 bytes + end + + local success = self._mode == 'c' or C.setvbuf(self._handle, nil, bufferMode, size) == 0 + if not success then + self._bufferMode, self._bufferSize = 'none', 0 + return false, "Could not set buffer mode" + end + + self._bufferMode, self._bufferSize = mode, size + return true +end + +function File:getSize() + -- NOTE: The correct way to do this would be a stat() call, which requires a + -- lot more (system-specific) code. This is a shortcut that requires the file + -- to be readable. + local mustOpen = not self:isOpen() + if mustOpen and not self:open('r') then return 0 end + + local pos = mustOpen and 0 or self:tell() + C.fseek(self._handle, 0, 2) + local size = self:tell() + if mustOpen then + self:close() + else + self:seek(pos) + end + return size +end + +function File:read(containerOrBytes, bytes) + if self._mode ~= 'r' then return nil, 0 end + + local container = bytes ~= nil and containerOrBytes or 'string' + if container ~= 'string' and container ~= 'data' then + error("Invalid container type: " .. container) + end + + bytes = not bytes and containerOrBytes or 'all' + bytes = bytes == 'all' and self:getSize() - self:tell() or math.min(self:getSize() - self:tell(), bytes) + + if bytes <= 0 then + local data = container == 'string' and '' or love.data.newFileData('', self._name) + return data, 0 + end + + local data = love.data.newByteData(bytes) + local r = tonumber(C.fread(data:getFFIPointer(), 1, bytes, self._handle)) + + if container == 'data' then + -- FileData from ByteData requires LÖVE 11.4+ + local ok, fd = pcall(love.filesystem.newFileData, data, self._name) + if ok then return fd end + end + + local str = data:getString() + data:release() + data = container == 'data' and love.filesystem.newFileData(str, self._name) or str + return data, r +end + +local function lines(file, autoclose) + local BUFFERSIZE = 4096 + local buffer, bufferPos = ByteArray(BUFFERSIZE), 0 + local bytesRead = tonumber(C.fread(buffer, 1, BUFFERSIZE, file._handle)) + + local offset = file:tell() + return function() + file:seek(offset) + + local line = {} + while bytesRead > 0 do + for i = bufferPos, bytesRead - 1 do + if buffer[i] == 10 then -- end of line + bufferPos = i + 1 + return table.concat(line) + end + + if buffer[i] ~= 13 then -- ignore CR + table.insert(line, string.char(buffer[i])) + end + end + + bytesRead = tonumber(C.fread(buffer, 1, BUFFERSIZE, file._handle)) + offset, bufferPos = offset + bytesRead, 0 + end + + if not line[1] then + if autoclose then file:close() end + return nil + end + return table.concat(line) + end +end + +function File:lines() + if self._mode ~= 'r' then error("File is not opened for reading") end + return lines(self) +end + +function File:write(data, size) + if self._mode ~= 'w' and self._mode ~= 'a' then + return false, "File " .. self._name .. " not opened for writing" + end + + local toWrite, writeSize + if type(data) == 'string' then + writeSize = (size == nil or size == 'all') and #data or size + toWrite = data + else + writeSize = (size == nil or size == 'all') and data:getSize() or size + toWrite = data:getFFIPointer() + end + + if tonumber(C.fwrite(toWrite, 1, writeSize, self._handle)) ~= writeSize then + return false, "Could not write data" + end + return true +end + +function File:seek(pos) + return self._handle and C.fseek(self._handle, pos, 0) == 0 +end + +function File:tell() + if not self._handle then return nil, "Invalid position" end + return tonumber(C.ftell(self._handle)) +end + +function File:flush() + if self._mode ~= 'w' and self._mode ~= 'a' then + return nil, "File is not opened for writing" + end + return C.fflush(self._handle) == 0 +end + +function File:isEOF() + return not self:isOpen() or C.feof(self._handle) ~= 0 or self:tell() == self:getSize() +end + +function File:release() + if self._mode ~= 'c' then self:close() end + self._handle = nil +end + +function File:type() return 'File' end + +function File:typeOf(t) return t == 'File' end + +File.__index = File + +----------------------------------------------------------------------------- + +local nativefs = {} +local loveC = ffi.os == 'Windows' and ffi.load('love') or C + +function nativefs.newFile(name) + if type(name) ~= 'string' then + error("bad argument #1 to 'newFile' (string expected, got " .. type(name) .. ")") + end + return setmetatable({ + _name = name, + _mode = 'c', + _handle = nil, + _bufferSize = 0, + _bufferMode = 'none' + }, File) +end + +function nativefs.newFileData(filepath) + local f = nativefs.newFile(filepath) + local ok, err = f:open('r') + if not ok then return nil, err end + + local data, err = f:read('data', 'all') + f:close() + return data, err +end + +function nativefs.mount(archive, mountPoint, appendToPath) + return loveC.PHYSFS_mount(archive, mountPoint, appendToPath and 1 or 0) ~= 0 +end + +function nativefs.unmount(archive) + return loveC.PHYSFS_unmount(archive) ~= 0 +end + +function nativefs.read(containerOrName, nameOrSize, sizeOrNil) + local container, name, size + if sizeOrNil then + container, name, size = containerOrName, nameOrSize, sizeOrNil + elseif not nameOrSize then + container, name, size = 'string', containerOrName, 'all' + else + if type(nameOrSize) == 'number' or nameOrSize == 'all' then + container, name, size = 'string', containerOrName, nameOrSize + else + container, name, size = containerOrName, nameOrSize, 'all' + end + end + + local file = nativefs.newFile(name) + local ok, err = file:open('r') + if not ok then return nil, err end + + local data, size = file:read(container, size) + file:close() + return data, size +end + +local function writeFile(mode, name, data, size) + local file = nativefs.newFile(name) + local ok, err = file:open(mode) + if not ok then return nil, err end + + ok, err = file:write(data, size or 'all') + file:close() + return ok, err +end + +function nativefs.write(name, data, size) + return writeFile('w', name, data, size) +end + +function nativefs.append(name, data, size) + return writeFile('a', name, data, size) +end + +function nativefs.lines(name) + local f = nativefs.newFile(name) + local ok, err = f:open('r') + if not ok then return nil, err end + return lines(f, true) +end + +function nativefs.load(name) + local chunk, err = nativefs.read(name) + if not chunk then return nil, err end + return loadstring(chunk, name) +end + +function nativefs.getWorkingDirectory() + return getcwd() +end + +function nativefs.setWorkingDirectory(path) + if not chdir(path) then return false, "Could not set working directory" end + return true +end + +function nativefs.getDriveList() + if ffi.os ~= 'Windows' then return { '/' } end + local drives, bits = {}, C.GetLogicalDrives() + for i = 0, 25 do + if bit.band(bits, 2 ^ i) > 0 then + table.insert(drives, string.char(65 + i) .. ':/') + end + end + return drives +end + +function nativefs.createDirectory(path) + local current = path:sub(1, 1) == '/' and '/' or '' + for dir in path:gmatch('[^/\\]+') do + current = current .. dir .. '/' + local info = nativefs.getInfo(current, 'directory') + if not info and not mkdir(current) then return false, "Could not create directory " .. current end + end + return true +end + +function nativefs.remove(name) + local info = nativefs.getInfo(name) + if not info then return false, "Could not remove " .. name end + if info.type == 'directory' then + if not rmdir(name) then return false, "Could not remove directory " .. name end + return true + end + if not unlink(name) then return false, "Could not remove file " .. name end + return true +end + +local function withTempMount(dir, fn, ...) + local mountPoint = _ptr(loveC.PHYSFS_getMountPoint(dir)) + if mountPoint then return fn(ffi.string(mountPoint), ...) end + if not nativefs.mount(dir, '__nativefs__temp__') then return false, "Could not mount " .. dir end + local a, b = fn('__nativefs__temp__', ...) + nativefs.unmount(dir) + return a, b +end + +function nativefs.getDirectoryItems(dir) + local result, err = withTempMount(dir, love.filesystem.getDirectoryItems) + return result or {} +end + +local function getDirectoryItemsInfo(path, filtertype) + local items = {} + local files = love.filesystem.getDirectoryItems(path) + for i = 1, #files do + local filepath = string.format('%s/%s', path, files[i]) + local info = love.filesystem.getInfo(filepath, filtertype) + if info then + info.name = files[i] + table.insert(items, info) + end + end + return items +end + +function nativefs.getDirectoryItemsInfo(path, filtertype) + local result, err = withTempMount(path, getDirectoryItemsInfo, filtertype) + return result or {} +end + +local function getInfo(path, file, filtertype) + local filepath = string.format('%s/%s', path, file) + return love.filesystem.getInfo(filepath, filtertype) +end + +local function leaf(p) + p = p:gsub('\\', '/') + local last, a = p, 1 + while a do + a = p:find('/', a + 1) + if a then + last = p:sub(a + 1) + end + end + return last +end + +function nativefs.getInfo(path, filtertype) + local dir = path:match("(.*[\\/]).*$") or './' + local file = leaf(path) + local result, err = withTempMount(dir, getInfo, file, filtertype) + return result or nil +end + +----------------------------------------------------------------------------- + +MODEMAP = { r = 'rb', w = 'wb', a = 'ab' } +local MAX_PATH = 4096 + +ffi.cdef([[ + int PHYSFS_mount(const char* dir, const char* mountPoint, int appendToPath); + int PHYSFS_unmount(const char* dir); + const char* PHYSFS_getMountPoint(const char* dir); + + typedef struct FILE FILE; + + FILE* fopen(const char* path, const char* mode); + size_t fread(void* ptr, size_t size, size_t nmemb, FILE* stream); + size_t fwrite(const void* ptr, size_t size, size_t nmemb, FILE* stream); + int fclose(FILE* stream); + int fflush(FILE* stream); + size_t fseek(FILE* stream, size_t offset, int whence); + size_t ftell(FILE* stream); + int setvbuf(FILE* stream, char* buffer, int mode, size_t size); + int feof(FILE* stream); +]]) + +if ffi.os == 'Windows' then + ffi.cdef([[ + int MultiByteToWideChar(unsigned int cp, uint32_t flags, const char* mb, int cmb, const wchar_t* wc, int cwc); + int WideCharToMultiByte(unsigned int cp, uint32_t flags, const wchar_t* wc, int cwc, const char* mb, + int cmb, const char* def, int* used); + int GetLogicalDrives(void); + int CreateDirectoryW(const wchar_t* path, void*); + int _wchdir(const wchar_t* path); + wchar_t* _wgetcwd(wchar_t* buffer, int maxlen); + FILE* _wfopen(const wchar_t* path, const wchar_t* mode); + int _wunlink(const wchar_t* path); + int _wrmdir(const wchar_t* path); + ]]) + + BUFFERMODE = { full = 0, line = 64, none = 4 } + + local function towidestring(str) + local size = C.MultiByteToWideChar(65001, 0, str, #str, nil, 0) + local buf = ffi.new('wchar_t[?]', size + 1) + C.MultiByteToWideChar(65001, 0, str, #str, buf, size) + return buf + end + + local function toutf8string(wstr) + local size = C.WideCharToMultiByte(65001, 0, wstr, -1, nil, 0, nil, nil) + local buf = ffi.new('char[?]', size + 1) + C.WideCharToMultiByte(65001, 0, wstr, -1, buf, size, nil, nil) + return ffi.string(buf) + end + + local nameBuffer = ffi.new('wchar_t[?]', MAX_PATH + 1) + + fopen = function(path, mode) return C._wfopen(towidestring(path), towidestring(mode)) end + getcwd = function() return toutf8string(C._wgetcwd(nameBuffer, MAX_PATH)) end + chdir = function(path) return C._wchdir(towidestring(path)) == 0 end + unlink = function(path) return C._wunlink(towidestring(path)) == 0 end + mkdir = function(path) return C.CreateDirectoryW(towidestring(path), nil) ~= 0 end + rmdir = function(path) return C._wrmdir(towidestring(path)) == 0 end +else + BUFFERMODE = { full = 0, line = 1, none = 2 } + + ffi.cdef([[ + char* getcwd(char *buffer, int maxlen); + int chdir(const char* path); + int unlink(const char* path); + int mkdir(const char* path, int mode); + int rmdir(const char* path); + ]]) + + local nameBuffer = ByteArray(MAX_PATH) + + fopen = C.fopen + unlink = function(path) return ffi.C.unlink(path) == 0 end + chdir = function(path) return ffi.C.chdir(path) == 0 end + mkdir = function(path) return ffi.C.mkdir(path, 0x1ed) == 0 end + rmdir = function(path) return ffi.C.rmdir(path) == 0 end + + getcwd = function() + local cwd = _ptr(C.getcwd(nameBuffer, MAX_PATH)) + return cwd and ffi.string(cwd) or nil + end +end + +return nativefs diff --git a/Talisman/steamodded_metadata.lua b/Talisman/steamodded_metadata.lua new file mode 100644 index 0000000..542bf3b --- /dev/null +++ b/Talisman/steamodded_metadata.lua @@ -0,0 +1,90 @@ +--- STEAMODDED HEADER +--- MOD_NAME: Talisman +--- MOD_ID: Talisman +--- MOD_AUTHOR: [MathIsFun_, Mathguy24, jenwalter666, cg-223] +--- MOD_DESCRIPTION: A mod that increases Balatro's score limit and skips scoring animations. +--- PREFIX: talisman +--- VERSION: 2.0.2 + +---------------------------------------------- +------------MOD CODE ------------------------- + +if SMODS.Atlas then + SMODS.Atlas({ + key = "modicon", + path = "icon.png", + px = 32, + py = 32 + }) +end + +if SMODS.Sound then + SMODS.Sound({ + key = "xchip", + path = "MultiplicativeChips.wav" + }) + SMODS.Sound({ + key = "echip", + path = "ExponentialChips.wav" + }) + SMODS.Sound({ + key = "eechip", + path = "TetrationalChips.wav" + }) + SMODS.Sound({ + key = "eeechip", + path = "PentationalChips.wav" + }) + SMODS.Sound({ + key = "emult", + path = "ExponentialMult.wav" + }) + SMODS.Sound({ + key = "eemult", + path = "TetrationalMult.wav" + }) + SMODS.Sound({ + key = "eeemult", + path = "PentationalMult.wav" + }) +end + + +if SMODS.current_mod then + function SMODS.current_mod.load_mod_config() end + function SMODS.current_mod.save_mod_config() end + SMODS.current_mod.config_tab = Talisman.config_tab + SMODS.current_mod.debug_info = { + ["Break Infinity"] = Talisman.config_file.break_infinity + } +end +--[[SMODS.Joker{ + key = "test", + name = "Joker Test", + rarity = "cry_exotic", + discovered = true, + pos = {x = 9, y = 2}, + cost = 4, + loc_txt = { + name = "Infinitus", + text = { + "{C:mult,E:1}+10#2#1000#3#10{} Mult" + } + }, + loc_vars = function(self, info_queue, center) + return {vars = {"#","{","}"}} + end, + calculate = function(self, card, context) + if context.cardarea == G.jokers and not context.before and not context.after then + local mult = Big:create(10):arrow(1000,10) + return { + mult_mod = mult, + colour = G.C.RED, + message = "!" + } + end + end, +}--]] + +---------------------------------------------- +------------MOD CODE END---------------------- diff --git a/Talisman/talisman.lua b/Talisman/talisman.lua new file mode 100644 index 0000000..2d58f1d --- /dev/null +++ b/Talisman/talisman.lua @@ -0,0 +1,718 @@ +local lovely = require("lovely") +local nativefs = require("nativefs") + +if not nativefs.getInfo(lovely.mod_dir .. "/Talisman") then + error( + 'Could not find proper Talisman folder.\nPlease make sure the folder for Talisman is named exactly "Talisman" and not "Talisman-main" or anything else.') +end + +Talisman = {config_file = {disable_anims = true, break_infinity = "omeganum", score_opt_id = 2}} +if nativefs.read(lovely.mod_dir.."/Talisman/config.lua") then + Talisman.config_file = STR_UNPACK(nativefs.read(lovely.mod_dir.."/Talisman/config.lua")) + + if Talisman.config_file.break_infinity and type(Talisman.config_file.break_infinity) ~= 'string' then + Talisman.config_file.break_infinity = "omeganum" + end +end +if not SMODS or not JSON then + local createOptionsRef = create_UIBox_options + function create_UIBox_options() + contents = createOptionsRef() + local m = UIBox_button({ + minw = 5, + button = "talismanMenu", + label = { + "Talisman" + }, + colour = G.C.GOLD + }) + table.insert(contents.nodes[1].nodes[1].nodes[1].nodes, #contents.nodes[1].nodes[1].nodes[1].nodes + 1, m) + return contents + end +end + +Talisman.config_tab = function() + tal_nodes = {{n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = "Select features to enable:", colours = {G.C.WHITE}, shadow = true, scale = 0.4})}}, + }},create_toggle({label = "Disable Scoring Animations", ref_table = Talisman.config_file, ref_value = "disable_anims", + callback = function(_set_toggle) + nativefs.write(lovely.mod_dir .. "/Talisman/config.lua", STR_PACK(Talisman.config_file)) + end}), + create_option_cycle({ + label = "Score Limit (requires game restart)", + scale = 0.8, + w = 6, + options = {"Vanilla (e308)", "BigNum (ee308)", "OmegaNum (e10##1000)"}, + opt_callback = 'talisman_upd_score_opt', + current_option = Talisman.config_file.score_opt_id, + })} + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 10, + align = "cm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = tal_nodes + } + end +G.FUNCS.talismanMenu = function(e) + local tabs = create_tabs({ + snap_to_nav = true, + tabs = { + { + label = "Talisman", + chosen = true, + tab_definition_function = Talisman.config_tab + }, + }}) + G.FUNCS.overlay_menu{ + definition = create_UIBox_generic_options({ + back_func = "options", + contents = {tabs} + }), + config = {offset = {x=0,y=10}} + } +end +G.FUNCS.talisman_upd_score_opt = function(e) + Talisman.config_file.score_opt_id = e.to_key + local score_opts = {"", "bignumber", "omeganum"} + Talisman.config_file.break_infinity = score_opts[e.to_key] + nativefs.write(lovely.mod_dir .. "/Talisman/config.lua", STR_PACK(Talisman.config_file)) +end +if Talisman.config_file.break_infinity then + Big, err = nativefs.load(lovely.mod_dir.."/Talisman/big-num/"..Talisman.config_file.break_infinity..".lua") + if not err then Big = Big() else Big = nil end + Notations = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations.lua")() + -- We call this after init_game_object to leave room for mods that add more poker hands + Talisman.igo = function(obj) + for _, v in pairs(obj.hands) do + v.chips = to_big(v.chips) + v.mult = to_big(v.mult) + v.s_chips = to_big(v.s_chips) + v.s_mult = to_big(v.s_mult) + v.l_chips = to_big(v.l_chips) + v.l_mult = to_big(v.l_mult) + end + return obj + end + + local nf = number_format + function number_format(num, e_switch_point) + if type(num) == 'table' then + num = to_big(num) + G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 + if num < to_big(e_switch_point or G.E_SWITCH_POINT) then + return nf(num:to_number(), e_switch_point) + else + return Notations.Balatro:format(num, 3) + end + else return nf(num, e_switch_point) end + end + + local mf = math.floor + function math.floor(x) + if type(x) == 'table' then return x:floor() end + return mf(x) + end + + local l10 = math.log10 + function math.log10(x) + if type(x) == 'table' then return l10(math.min(x:to_number(),1e300)) end--x:log10() end + return l10(x) + end + + local lg = math.log + function math.log(x, y) + if not y then y = 2.718281828459045 end + if type(x) == 'table' then return lg(math.min(x:to_number(),1e300),y) end --x:log(y) end + return lg(x,y) + end + + if SMODS then + function SMODS.get_blind_amount(ante) + local k = to_big(0.75) + local scale = G.GAME.modifiers.scaling + local amounts = { + to_big(300), + to_big(700 + 100*scale), + to_big(1400 + 600*scale), + to_big(2100 + 2900*scale), + to_big(15000 + 5000*scale*math.log(scale)), + to_big(12000 + 8000*(scale+1)*(0.4*scale)), + to_big(10000 + 25000*(scale+1)*((scale/4)^2)), + to_big(50000 * (scale+1)^2 * (scale/7)^2) + } + + if ante < 1 then return to_big(100) end + if ante <= 8 then + local amount = amounts[ante] + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + end + local a, b, c, d = amounts[8], amounts[8]/amounts[7], ante-8, 1 + 0.2*(ante-8) + local amount = math.floor(a*(b + (b*k*c)^d)^c) + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + end + end + -- There's too much to override here so we just fully replace this function + -- Note that any ante scaling tweaks will need to manually changed... + local gba = get_blind_amount + function get_blind_amount(ante) + if G.GAME.modifiers.scaling and G.GAME.modifiers.scaling > 3 then return SMODS.get_blind_amount(ante) end + if type(to_big(1)) == 'number' then return gba(ante) end + local k = to_big(0.75) + if not G.GAME.modifiers.scaling or G.GAME.modifiers.scaling == 1 then + local amounts = { + to_big(300), to_big(800), to_big(2000), to_big(5000), to_big(11000), to_big(20000), to_big(35000), to_big(50000) + } + if ante < 1 then return to_big(100) end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = a*(b+(k*c)^d)^c + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + elseif G.GAME.modifiers.scaling == 2 then + local amounts = { + to_big(300), to_big(900), to_big(2600), to_big(8000), to_big(20000), to_big(36000), to_big(60000), to_big(100000) + --300, 900, 2400, 7000, 18000, 32000, 56000, 90000 + } + if ante < 1 then return to_big(100) end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = a*(b+(k*c)^d)^c + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + elseif G.GAME.modifiers.scaling == 3 then + local amounts = { + to_big(300), to_big(1000), to_big(3200), to_big(9000), to_big(25000), to_big(60000), to_big(110000), to_big(200000) + --300, 1000, 3000, 8000, 22000, 50000, 90000, 180000 + } + if ante < 1 then return to_big(100) end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = a*(b+(k*c)^d)^c + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + end + end + + function check_and_set_high_score(score, amt) + if G.GAME.round_scores[score] and to_big(math.floor(amt)) > to_big(G.GAME.round_scores[score].amt) then + G.GAME.round_scores[score].amt = to_big(math.floor(amt)) + end + if G.GAME.seeded then return end + --[[if G.PROFILES[G.SETTINGS.profile].high_scores[score] and math.floor(amt) > G.PROFILES[G.SETTINGS.profile].high_scores[score].amt then + if G.GAME.round_scores[score] then G.GAME.round_scores[score].high_score = true end + G.PROFILES[G.SETTINGS.profile].high_scores[score].amt = math.floor(amt) + G:save_settings() + end--]] --going to hold off on modifying this until proper save loading exists + end + + local sn = scale_number + function scale_number(number, scale, max, e_switch_point) + if not Big then return sn(number, scale, max, e_switch_point) end + scale = to_big(scale) + G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 + if not number or not is_number(number) then return scale end + if not max then max = 10000 end + if to_big(number).e and to_big(number).e == 10^1000 then + scale = scale*math.floor(math.log(max*10, 10))/7 + end + if to_big(number) >= to_big(e_switch_point or G.E_SWITCH_POINT) then + if (to_big(to_big(number):log10()) <= to_big(999)) then + scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.log(1000000*10, 10)) + else + scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.max(7,string.len(number_format(number))-1)) + end + elseif to_big(number) >= to_big(max) then + scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.log(number*10, 10)) + end + return math.min(3, scale:to_number()) + end + + local tsj = G.FUNCS.text_super_juice + function G.FUNCS.text_super_juice(e, _amount) + if _amount > 2 then _amount = 2 end + return tsj(e, _amount) + end + + local max = math.max + --don't return a Big unless we have to - it causes nativefs to break + function math.max(x, y) + if type(x) == 'table' or type(y) == 'table' then + x = to_big(x) + y = to_big(y) + if (x > y) then + return x + else + return y + end + else return max(x,y) end + end + + local min = math.min + function math.min(x, y) + if type(x) == 'table' or type(y) == 'table' then + x = to_big(x) + y = to_big(y) + if (x < y) then + return x + else + return y + end + else return min(x,y) end + end + + local sqrt = math.sqrt + function math.sqrt(x) + if type(x) == 'table' then + if getmetatable(x) == BigMeta then return x:sqrt() end + if getmetatable(x) == OmegaMeta then return x:pow(0.5) end + end + return sqrt(x) + end + + + + local old_abs = math.abs + function math.abs(x) + if type(x) == 'table' then + x = to_big(x) + if (x < to_big(0)) then + return -1 * x + else + return x + end + else return old_abs(x) end + end +end + +function is_number(x) + if type(x) == 'number' then return true end + if type(x) == 'table' and ((x.e and x.m) or (x.array and x.sign)) then return true end + return false +end + +function to_big(x, y) + if Big and Big.m then + return Big:new(x,y) + elseif Big and Big.array then + local result = Big:create(x) + result.sign = y or result.sign or x.sign or 1 + return result + elseif is_number(x) then + return x * 10^(y or 0) + + elseif type(x) == "nil" then + return 0 + else + if ((#x>=2) and ((x[2]>=2) or (x[2]==1) and (x[1]>308))) then + return 1e309 + end + if (x[2]==1) then + return math.pow(10,x[1]) + end + return x[1]*(y or 1); + end +end +function to_number(x) + if type(x) == 'table' and (getmetatable(x) == BigMeta or getmetatable(x) == OmegaMeta) then + return x:to_number() + else + return x + end +end + +--patch to remove animations +local cest = card_eval_status_text +function card_eval_status_text(a,b,c,d,e,f) + if not Talisman.config_file.disable_anims then cest(a,b,c,d,e,f) end +end +local jc = juice_card +function juice_card(x) + if not Talisman.config_file.disable_anims then jc(x) end +end +function tal_uht(config, vals) + local col = G.C.GREEN + if vals.chips and G.GAME.current_round.current_hand.chips ~= vals.chips then + local delta = (is_number(vals.chips) and is_number(G.GAME.current_round.current_hand.chips)) and (vals.chips - G.GAME.current_round.current_hand.chips) or 0 + if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED + elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta) + else delta = number_format(delta) + end + if type(vals.chips) == 'string' then delta = vals.chips end + G.GAME.current_round.current_hand.chips = vals.chips + if G.hand_text_area.chips.config.object then + G.hand_text_area.chips:update(0) + end + end + if vals.mult and G.GAME.current_round.current_hand.mult ~= vals.mult then + local delta = (is_number(vals.mult) and is_number(G.GAME.current_round.current_hand.mult))and (vals.mult - G.GAME.current_round.current_hand.mult) or 0 + if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED + elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta) + else delta = number_format(delta) + end + if type(vals.mult) == 'string' then delta = vals.mult end + G.GAME.current_round.current_hand.mult = vals.mult + if G.hand_text_area.mult.config.object then + G.hand_text_area.mult:update(0) + end + end + if vals.handname and G.GAME.current_round.current_hand.handname ~= vals.handname then + G.GAME.current_round.current_hand.handname = vals.handname + end + if vals.chip_total then G.GAME.current_round.current_hand.chip_total = vals.chip_total;G.hand_text_area.chip_total.config.object:pulse(0.5) end + if vals.level and G.GAME.current_round.current_hand.hand_level ~= ' '..localize('k_lvl')..tostring(vals.level) then + if vals.level == '' then + G.GAME.current_round.current_hand.hand_level = vals.level + else + G.GAME.current_round.current_hand.hand_level = ' '..localize('k_lvl')..tostring(vals.level) + if type(vals.level) == 'number' then + G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[math.min(vals.level, 7)] + else + G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[1] + end + end + end + return true +end +local uht = update_hand_text +function update_hand_text(config, vals) + if Talisman.config_file.disable_anims then + if G.latest_uht then + local chips = G.latest_uht.vals.chips + local mult = G.latest_uht.vals.mult + if not vals.chips then vals.chips = chips end + if not vals.mult then vals.mult = mult end + end + G.latest_uht = {config = config, vals = vals} + else uht(config, vals) + end +end +local upd = Game.update +function Game:update(dt) + upd(self, dt) + if G.latest_uht and G.latest_uht.config and G.latest_uht.vals then + tal_uht(G.latest_uht.config, G.latest_uht.vals) + G.latest_uht = nil + end + if Talisman.dollar_update then + G.HUD:get_UIE_by_ID('dollar_text_UI').config.object:update() + G.HUD:recalculate() + Talisman.dollar_update = false + end +end +--scoring coroutine +local oldplay = G.FUNCS.evaluate_play + +function G.FUNCS.evaluate_play() + G.SCORING_COROUTINE = coroutine.create(oldplay) + G.LAST_SCORING_YIELD = love.timer.getTime() + G.CARD_CALC_COUNTS = {} -- keys = cards, values = table containing numbers + local success, err = coroutine.resume(G.SCORING_COROUTINE) + if not success then + error(err) + end +end + + +local oldupd = love.update +function love.update(dt, ...) + oldupd(dt, ...) + if G.SCORING_COROUTINE then + if collectgarbage("count") > 1024*1024 then + collectgarbage("collect") + end + if coroutine.status(G.SCORING_COROUTINE) == "dead" then + G.SCORING_COROUTINE = nil + G.FUNCS.exit_overlay_menu() + local totalCalcs = 0 + for i, v in pairs(G.CARD_CALC_COUNTS) do + totalCalcs = totalCalcs + v[1] + end + G.GAME.LAST_CALCS = totalCalcs + else + G.SCORING_TEXT = nil + if not G.OVERLAY_MENU then + G.scoring_text = {"Calculating...", "", "", ""} + G.SCORING_TEXT = { + {n = G.UIT.C, nodes = { + {n = G.UIT.R, config = {padding = 0.1, align = "cm"}, nodes = { + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 1}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 1, silent = true})}}, + }},{n = G.UIT.R, nodes = { + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 2}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}}, + }},{n = G.UIT.R, nodes = { + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 3}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}}, + }},{n = G.UIT.R, nodes = { + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 4}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}}, + }}}}} + G.FUNCS.overlay_menu({ + definition = + {n=G.UIT.ROOT, minw = G.ROOM.T.w*5, minh = G.ROOM.T.h*5, config={align = "cm", padding = 9999, offset = {x = 0, y = -3}, r = 0.1, colour = {G.C.GREY[1], G.C.GREY[2], G.C.GREY[3],0.7}}, nodes= G.SCORING_TEXT}, + config = {align="cm", offset = {x=0,y=0}, major = G.ROOM_ATTACH, bond = 'Weak'} + }) + else + + if G.OVERLAY_MENU and G.scoring_text then + local totalCalcs = 0 + for i, v in pairs(G.CARD_CALC_COUNTS) do + totalCalcs = totalCalcs + v[1] + end + local jokersYetToScore = #G.jokers.cards + #G.play.cards - #G.CARD_CALC_COUNTS + G.scoring_text[1] = "Calculating..." + G.scoring_text[2] = "Elapsed calculations: "..tostring(totalCalcs) + G.scoring_text[3] = "Cards yet to score: "..tostring(jokersYetToScore) + G.scoring_text[4] = "Calculations last played hand: " .. tostring(G.GAME.LAST_CALCS or "Unknown") + end + + end + --this coroutine allows us to stagger GC cycles through + --the main source of waste in terms of memory (especially w joker retriggers) is through local variables that become garbage + --this practically eliminates the memory overhead of scoring + --event queue overhead seems to not exist if Talismans Disable Scoring Animations is off. + --event manager has to wait for scoring to finish until it can keep processing events anyways. + + + G.LAST_SCORING_YIELD = love.timer.getTime() + + local success, msg = coroutine.resume(G.SCORING_COROUTINE) + if not success then + error(msg) + end + end + end +end + + + +TIME_BETWEEN_SCORING_FRAMES = 0.03 -- 30 fps during scoring +-- we dont want overhead from updates making scoring much slower +-- originally 10 fps, I think 30 fps is a good way to balance it while making it look smooth, too +--wrap everything in calculating contexts so we can do more things with it +Talisman.calculating_joker = false +Talisman.calculating_score = false +Talisman.calculating_card = false +Talisman.dollar_update = false +local ccj = Card.calculate_joker +function Card:calculate_joker(context) + --scoring coroutine + G.CURRENT_SCORING_CARD = self + G.CARD_CALC_COUNTS = G.CARD_CALC_COUNTS or {} + if G.CARD_CALC_COUNTS[self] then + G.CARD_CALC_COUNTS[self][1] = G.CARD_CALC_COUNTS[self][1] + 1 + else + G.CARD_CALC_COUNTS[self] = {1, 1} + end + + + if G.LAST_SCORING_YIELD and ((love.timer.getTime() - G.LAST_SCORING_YIELD) > TIME_BETWEEN_SCORING_FRAMES) and coroutine.running() then + coroutine.yield() + end + Talisman.calculating_joker = true + local ret = ccj(self, context) + + if ret and type(ret) == "table" and ret.repetitions then + G.CARD_CALC_COUNTS[ret.card] = G.CARD_CALC_COUNTS[ret.card] or {1,1} + G.CARD_CALC_COUNTS[ret.card][2] = G.CARD_CALC_COUNTS[ret.card][2] + ret.repetitions + end + Talisman.calculating_joker = false + return ret +end +local cuc = Card.use_consumable +function Card:use_consumable(x,y) + Talisman.calculating_score = true + local ret = cuc(self, x,y) + Talisman.calculating_score = false + return ret +end +local gfep = G.FUNCS.evaluate_play +G.FUNCS.evaluate_play = function(e) + Talisman.calculating_score = true + local ret = gfep(e) + Talisman.calculating_score = false + return ret +end +--[[local ec = eval_card +function eval_card() + Talisman.calculating_card = true + local ret = ec() + Talisman.calculating_card = false + return ret +end--]] +local sm = Card.start_materialize +function Card:start_materialize(a,b,c) + if Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then return end + return sm(self,a,b,c) +end +local sd = Card.start_dissolve +function Card:start_dissolve(a,b,c,d) + if Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then self:remove() return end + return sd(self,a,b,c,d) +end +local ss = Card.set_seal +function Card:set_seal(a,b,immediate) + return ss(self,a,b,Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) or immediate) +end + +function Card:get_chip_x_bonus() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.x_chips or 0) <= 1 then return 0 end + return self.ability.x_chips +end + +function Card:get_chip_e_bonus() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.e_chips or 0) <= 1 then return 0 end + return self.ability.e_chips +end + +function Card:get_chip_ee_bonus() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.ee_chips or 0) <= 1 then return 0 end + return self.ability.ee_chips +end + +function Card:get_chip_eee_bonus() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.eee_chips or 0) <= 1 then return 0 end + return self.ability.eee_chips +end + +function Card:get_chip_hyper_bonus() + if self.debuff then return {0,0} end + if self.ability.set == 'Joker' then return {0,0} end + if type(self.ability.hyper_chips) ~= 'table' then return {0,0} end + if (self.ability.hyper_chips[1] <= 0 or self.ability.hyper_chips[2] <= 0) then return {0,0} end + return self.ability.hyper_chips +end + +function Card:get_chip_e_mult() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.e_mult or 0) <= 1 then return 0 end + return self.ability.e_mult +end + +function Card:get_chip_ee_mult() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.ee_mult or 0) <= 1 then return 0 end + return self.ability.ee_mult +end + +function Card:get_chip_eee_mult() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.eee_mult or 0) <= 1 then return 0 end + return self.ability.eee_mult +end + +function Card:get_chip_hyper_mult() + if self.debuff then return {0,0} end + if self.ability.set == 'Joker' then return {0,0} end + if type(self.ability.hyper_mult) ~= 'table' then return {0,0} end + if (self.ability.hyper_mult[1] <= 0 or self.ability.hyper_mult[2] <= 0) then return {0,0} end + return self.ability.hyper_mult +end + +--Easing fixes +--Changed this to always work; it's less pretty but fine for held in hand things +local edo = ease_dollars +function ease_dollars(mod, instant) + if Talisman.config_file.disable_anims then--and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then + mod = mod or 0 + if mod < 0 then inc_career_stat('c_dollars_earned', mod) end + G.GAME.dollars = G.GAME.dollars + mod + Talisman.dollar_update = true + else return edo(mod, instant) end +end + +local su = G.start_up +function safe_str_unpack(str) + local chunk, err = loadstring(str) + if chunk then + setfenv(chunk, {Big = Big, BigMeta = BigMeta, OmegaMeta = OmegaMeta, to_big = to_big, inf = 1.79769e308}) -- Use an empty environment to prevent access to potentially harmful functions + local success, result = pcall(chunk) + if success then + return result + else + print("Error unpacking string: " .. result) + return nil + end + else + print("Error loading string: " .. err) + return nil + end + end +function G:start_up() + STR_UNPACK = safe_str_unpack + su(self) + STR_UNPACK = safe_str_unpack +end + +--Skip round animation things +local gfer = G.FUNCS.evaluate_round +function G.FUNCS.evaluate_round() + if Talisman.config_file.disable_anims then + if to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) then + add_round_eval_row({dollars = G.GAME.blind.dollars, name='blind1', pitch = 0.95}) + else + add_round_eval_row({dollars = 0, name='blind1', pitch = 0.95, saved = true}) + end + local arer = add_round_eval_row + add_round_eval_row = function() return end + local dollars = gfer() + add_round_eval_row = arer + add_round_eval_row({name = 'bottom', dollars = Talisman.dollars}) + else + return gfer() + end +end + +--some debugging functions +--[[local callstep=0 +function printCallerInfo() + -- Get debug info for the caller of the function that called printCallerInfo + local info = debug.getinfo(3, "Sl") + callstep = callstep+1 + if info then + print("["..callstep.."] "..(info.short_src or "???")..":"..(info.currentline or "unknown")) + else + print("Caller information not available") + end +end +local emae = EventManager.add_event +function EventManager:add_event(x,y,z) + printCallerInfo() + return emae(self,x,y,z) +end--]] diff --git a/lovely/dump/back.lua b/lovely/dump/back.lua new file mode 100644 index 0000000..997b2bc --- /dev/null +++ b/lovely/dump/back.lua @@ -0,0 +1,321 @@ +LOVELY_INTEGRITY = 'd3c881055f5938773f6b44e7c8b207117963612b2eaf889f5b7ef8b61e1143cd' + +--Class +Back = Object:extend() + +--Class Methods +function Back:init(selected_back) + if not selected_back then selected_back = G.P_CENTERS.b_red end + self.atlas = selected_back.unlocked and selected_back.atlas or nil + self.name = selected_back.name or 'Red Deck' + + self.effect = { + center = selected_back, + text_UI = '', + config = copy_table(selected_back.config) + } + self.loc_name = localize{type = 'name_text', set = 'Back', key = self.effect.center.key} + + local pos = (self.effect.center.unlocked and self.effect.center.pos) or {x = 4, y = 0} + self.pos = self.pos or {} + self.pos.x = pos.x + self.pos.y = pos.y +end + +function Back:get_name() + if self.effect.center.unlocked then return self.loc_name else return localize('k_locked') end +end + +function Back:generate_UI(other, ui_scale, min_dims, challenge) + min_dims = min_dims or 0.7 + ui_scale = ui_scale or 0.9 + local back_config = other or self.effect.center + local name_to_check = other and other.name or self.name + local effect_config = other and other.config or self.effect.config + challenge = G.CHALLENGES[get_challenge_int_from_id(challenge or '') or ''] or {name = 'ERROR'} + + local loc_args, loc_nodes = nil, {} + + if not back_config.unlocked then + local localized_by_smods + local key_override + if back_config.locked_loc_vars and type(back_config.locked_loc_vars) == 'function' then + local res = back_config:locked_loc_vars() or {} + loc_args = res.vars or {} + key_override = res.key + end + if G.localization.descriptions.Back[key_override or back_config.key].unlock_parsed then + localize{type = 'unlocks', key = key_override or back_config.key, set = 'Back', nodes = loc_nodes, vars = loc_args} + localized_by_smods = true + end + if not back_config.unlock_condition then + if not localized_by_smods then + localize{type = 'descriptions', key = 'demo_locked', set = "Other", nodes = loc_nodes, vars = loc_args} + end + elseif back_config.unlock_condition.type == 'win_deck' then + local other_name = localize('k_unknown') + if G.P_CENTERS[back_config.unlock_condition.deck].unlocked then + other_name = localize{type = 'name_text', set = 'Back', key = back_config.unlock_condition.deck} + end + loc_args = loc_args or {other_name} + localize{type = 'descriptions', key = 'deck_locked_win', set = "Other", nodes = loc_nodes, vars = loc_args} + elseif back_config.unlock_condition.type == 'discover_amount' then + loc_args = loc_args or {tostring(back_config.unlock_condition.amount)} + localize{type = 'descriptions', key = 'deck_locked_discover', set = "Other", nodes = loc_nodes, vars = loc_args} + elseif back_config.unlock_condition.type == 'win_stake' then + local other_name = localize{type = 'name_text', set = 'Stake', key = G.P_CENTER_POOLS.Stake[back_config.unlock_condition.stake].key} + loc_args = loc_args or {other_name, colours = {get_stake_col(back_config.unlock_condition.stake)}} + localize{type = 'descriptions', key = 'deck_locked_stake', set = "Other", nodes = loc_nodes, vars = loc_args} + end + else + local key_override + if back_config.loc_vars and type(back_config.loc_vars) == 'function' then + local res = back_config:loc_vars() or {} + loc_args = res.vars or {} + key_override = res.key + elseif name_to_check == 'Blue Deck' then loc_args = {effect_config.hands} + elseif name_to_check == 'Red Deck' then loc_args = {effect_config.discards} + elseif name_to_check == 'Yellow Deck' then loc_args = {effect_config.dollars} + elseif name_to_check == 'Green Deck' then loc_args = {effect_config.extra_hand_bonus, effect_config.extra_discard_bonus} + elseif name_to_check == 'Black Deck' then loc_args = {effect_config.joker_slot, -effect_config.hands} + elseif name_to_check == 'Magic Deck' then loc_args = {localize{type = 'name_text', key = 'v_crystal_ball', set = 'Voucher'}, localize{type = 'name_text', key = 'c_fool', set = 'Tarot'}} + elseif name_to_check == 'Nebula Deck' then loc_args = {localize{type = 'name_text', key = 'v_telescope', set = 'Voucher'}, -1} + elseif name_to_check == 'Ghost Deck' then + elseif name_to_check == 'Abandoned Deck' then + elseif name_to_check == 'Checkered Deck' then + elseif name_to_check == 'Zodiac Deck' then loc_args = {localize{type = 'name_text', key = 'v_tarot_merchant', set = 'Voucher'}, + localize{type = 'name_text', key = 'v_planet_merchant', set = 'Voucher'}, + localize{type = 'name_text', key = 'v_overstock_norm', set = 'Voucher'}} + elseif name_to_check == 'Painted Deck' then loc_args = {effect_config.hand_size,effect_config.joker_slot} + elseif name_to_check == 'Anaglyph Deck' then loc_args = {localize{type = 'name_text', key = 'tag_double', set = 'Tag'}} + elseif name_to_check == 'Plasma Deck' then loc_args = {effect_config.ante_scaling} + elseif name_to_check == 'Erratic Deck' then + end + localize{type = 'descriptions', key = key_override or back_config.key, set = 'Back', nodes = loc_nodes, vars = loc_args} + end + + return + {n=G.UIT.ROOT, config={align = "cm", minw = min_dims*5, minh = min_dims*2.5, id = self.name, colour = G.C.CLEAR}, nodes={ + name_to_check == 'Challenge Deck' and UIBox_button({button = 'deck_view_challenge', label = {localize(challenge.id, 'challenge_names')}, minw = 2.2, minh = 1, scale = 0.6, id = challenge}) + or desc_from_rows(loc_nodes, true, min_dims*5) + }} +end + +function Back:change_to(new_back) + if not new_back then new_back = G.P_CENTERS.b_red end + self.atlas = new_back.unlocked and new_back.atlas or nil + self.name = new_back.name or 'Red Deck' + self.effect = { + center = new_back, + text_UI = '', + config = copy_table(new_back.config) + } + self.loc_name = localize{type = 'name_text', set = 'Back', key = self.effect.center.key} + local pos = self.effect.center.unlocked and copy_table(new_back.pos) or {x = 4, y = 0} + self.pos.x = pos.x + self.pos.y = pos.y +end + +function Back:save() + local backTable = { + name = self.name, + pos = self.pos, + effect = self.effect, + key = self.effect.center.key or 'b_red' + } + + return backTable +end + +function Back:trigger_effect(args) + if not args then return end + local obj = self.effect.center + if obj.trigger_effect and type(obj.trigger_effect) == 'function' then + local o = {obj:trigger_effect(args)} + if o then return unpack(o) end + end + + if self.name == 'Anaglyph Deck' and args.context == 'eval' and G.GAME.last_blind and G.GAME.last_blind.boss then + G.E_MANAGER:add_event(Event({ + func = (function() + add_tag(Tag('tag_double')) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + return true + end) + })) + end + if self.name == 'Plasma Deck' and args.context == 'blind_amount' then + return + end + + if self.name == 'Plasma Deck' and args.context == 'final_scoring_step' then + local tot = args.chips + args.mult + args.chips = math.floor(tot/2) + args.mult = math.floor(tot/2) + update_hand_text({delay = 0}, {mult = args.mult, chips = args.chips}) + + G.E_MANAGER:add_event(Event({ + func = (function() + local text = localize('k_balanced') + play_sound('gong', 0.94, 0.3) + play_sound('gong', 0.94*1.5, 0.2) + play_sound('tarot1', 1.5) + ease_colour(G.C.UI_CHIPS, {0.8, 0.45, 0.85, 1}) + ease_colour(G.C.UI_MULT, {0.8, 0.45, 0.85, 1}) + attention_text({ + scale = 1.4, text = text, hold = 2, align = 'cm', offset = {x = 0,y = -2.7},major = G.play + }) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + blocking = false, + delay = 4.3, + func = (function() + ease_colour(G.C.UI_CHIPS, G.C.BLUE, 2) + ease_colour(G.C.UI_MULT, G.C.RED, 2) + return true + end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + blocking = false, + no_delete = true, + delay = 6.3, + func = (function() + G.C.UI_CHIPS[1], G.C.UI_CHIPS[2], G.C.UI_CHIPS[3], G.C.UI_CHIPS[4] = G.C.BLUE[1], G.C.BLUE[2], G.C.BLUE[3], G.C.BLUE[4] + G.C.UI_MULT[1], G.C.UI_MULT[2], G.C.UI_MULT[3], G.C.UI_MULT[4] = G.C.RED[1], G.C.RED[2], G.C.RED[3], G.C.RED[4] + return true + end) + })) + return true + end) + })) + + delay(0.6) + return args.chips, args.mult + end +end + +function Back:apply_to_run() + local obj = self.effect.center + if obj.apply and type(obj.apply) == 'function' then + obj:apply() + end + + if self.effect.config.voucher then + G.GAME.used_vouchers[self.effect.config.voucher] = true + G.GAME.cry_owned_vouchers[self.effect.config.voucher] = true + G.GAME.starting_voucher_count = (G.GAME.starting_voucher_count or 0) + 1 + Card.apply_to_run(nil, G.P_CENTERS[self.effect.config.voucher]) + end + if self.effect.config.hands then + G.GAME.starting_params.hands = G.GAME.starting_params.hands + self.effect.config.hands + end + if self.effect.config.consumables then + delay(0.4) + G.E_MANAGER:add_event(Event({ + func = function() + for k, v in ipairs(self.effect.config.consumables) do + local card = create_card('Tarot', G.consumeables, nil, nil, nil, nil, v, 'deck') + card:add_to_deck() + G.consumeables:emplace(card) + end + return true + end + })) + end + + + if self.effect.config.dollars then + G.GAME.starting_params.dollars = G.GAME.starting_params.dollars + self.effect.config.dollars + end + if self.effect.config.remove_faces then + G.GAME.starting_params.no_faces = true + end + + if self.effect.config.spectral_rate then + G.GAME.spectral_rate = self.effect.config.spectral_rate + end + if self.effect.config.discards then + G.GAME.starting_params.discards = G.GAME.starting_params.discards + self.effect.config.discards + end + if self.effect.config.reroll_discount then + G.GAME.starting_params.reroll_cost = G.GAME.starting_params.reroll_cost - self.effect.config.reroll_discount + end + + + if self.effect.config.edition then + G.E_MANAGER:add_event(Event({ + func = function() + local i = 0 + while i < self.effect.config.edition_count do + local card = pseudorandom_element(G.playing_cards, pseudoseed('edition_deck')) + if not card.edition then + i = i + 1 + card:set_edition({[self.effect.config.edition] = true}, nil, true) + end + end + return true + end + })) + end + if self.effect.config.vouchers then + for k, v in pairs(self.effect.config.vouchers) do + G.GAME.used_vouchers[v ] = true + G.GAME.cry_owned_vouchers[v ] = true + G.GAME.starting_voucher_count = (G.GAME.starting_voucher_count or 0) + 1 + Card.apply_to_run(nil, G.P_CENTERS[v]) + end + end + if self.name == 'Checkered Deck' then + G.E_MANAGER:add_event(Event({ + func = function() + for k, v in pairs(G.playing_cards) do + if v.base.suit == 'Clubs' then + v:change_suit('Spades') + end + if v.base.suit == 'Diamonds' then + v:change_suit('Hearts') + end + end + return true + end + })) + end + if self.effect.config.randomize_rank_suit then + G.GAME.starting_params.erratic_suits_and_ranks = true + end + if self.effect.config.joker_slot then + G.GAME.starting_params.joker_slots = G.GAME.starting_params.joker_slots + self.effect.config.joker_slot + end + if self.effect.config.hand_size then + G.GAME.starting_params.hand_size = G.GAME.starting_params.hand_size + self.effect.config.hand_size + end + if self.effect.config.ante_scaling then + G.GAME.starting_params.ante_scaling = self.effect.config.ante_scaling + end + if self.effect.config.consumable_slot then + G.GAME.starting_params.consumable_slots = G.GAME.starting_params.consumable_slots + self.effect.config.consumable_slot + end + if self.effect.config.no_interest then + G.GAME.modifiers.no_interest = true + end + if self.effect.config.extra_hand_bonus then + G.GAME.modifiers.money_per_hand = self.effect.config.extra_hand_bonus + end + if self.effect.config.extra_discard_bonus then + G.GAME.modifiers.money_per_discard = self.effect.config.extra_discard_bonus + end +end + +function Back:load(backTable) + self.name = backTable.name + self.pos = backTable.pos + self.effect = backTable.effect + self.effect.center = G.P_CENTERS[backTable.key] or G.P_CENTERS.b_red + + + self.loc_name = localize{type = 'name_text', set = 'Back', key = self.effect.center.key} +end diff --git a/lovely/dump/blind.lua b/lovely/dump/blind.lua new file mode 100644 index 0000000..cfb9e88 --- /dev/null +++ b/lovely/dump/blind.lua @@ -0,0 +1,863 @@ +LOVELY_INTEGRITY = 'e8cd6ceef85971448b1b28bb95d2a4b68928ef66a2261cc19c1e9c7de9c6d3b8' + +--class +Blind = Moveable:extend() + +--class methods +function Blind:init(X, Y, W, H) + Moveable.init(self,X, Y, W, H) + + self.children = {} + self.config = {} + self.tilt_var = {mx = 0, my = 0, amt = 0} + self.ambient_tilt = 0.3 + self.chips = 0 + self.zoom = true + self.states.collide.can = true + self.colour = copy_table(G.C.BLACK) + self.dark_colour = darken(self.colour, 0.2) + self.children.animatedSprite = AnimatedSprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ANIMATION_ATLAS['blind_chips'], G.P_BLINDS.bl_small.pos) + self.children.animatedSprite.states = self.states + self.children.animatedSprite.states.visible = false + self.children.animatedSprite.states.drag.can = true + self.states.collide.can = true + self.states.drag.can = true + self.loc_debuff_lines = {'',''} + + self.shadow_height = 0 + + if getmetatable(self) == Blind then + table.insert(G.I.CARD, self) + end +end + +function Blind:change_colour(blind_col) + blind_col = blind_col or get_blind_main_colour(self.config.blind.key or '') + local dark_col = mix_colours(blind_col, G.C.BLACK, 0.4) + ease_colour(G.C.DYN_UI.MAIN, blind_col) + ease_colour(G.C.DYN_UI.DARK, dark_col) + + if not self.boss and self.name then + blind_col = darken(G.C.BLACK, 0.05) + dark_col = lighten(G.C.BLACK, 0.07) + else + dark_col = mix_colours(blind_col, G.C.BLACK, 0.2) + end + ease_colour(G.C.DYN_UI.BOSS_MAIN, blind_col) + ease_colour(G.C.DYN_UI.BOSS_DARK, dark_col) +end + +function Blind:set_text() + if self.config.blind then + if self.disabled then + self.loc_name = self.name == '' and self.name or localize{type ='name_text', key = self.config.blind.key, set = 'Blind'} + self.loc_debuff_text = '' + EMPTY(self.loc_debuff_lines) + else + local loc_vars = nil + if self.name == 'The Ox' then + loc_vars = {localize(G.GAME.current_round.most_played_poker_hand, 'poker_hands')} + end + local obj = self.config.blind + if obj.loc_vars and type(obj.loc_vars) == 'function' then + local res = obj:loc_vars() or {} + loc_vars = res.vars or {} + end + local loc_target = localize{type = 'raw_descriptions', key = self.config.blind.key, set = 'Blind', vars = loc_vars or self.config.blind.vars} + if loc_target then + self.loc_name = self.name == '' and self.name or localize{type ='name_text', key = self.config.blind.key, set = 'Blind'} + self.loc_debuff_text = '' + EMPTY(self.loc_debuff_lines) + for k, v in ipairs(loc_target) do + self.loc_debuff_text = self.loc_debuff_text..v..(k <= #loc_target and ' ' or '') + self.loc_debuff_lines[k] = v + end + else + self.loc_name = ''; self.loc_debuff_text = '' + EMPTY(self.loc_debuff_lines) + end + end + end +end + +function Blind:set_blind(blind, reset, silent) + if not reset then + if blind then + self.in_blind = true + end + self.config.blind = blind or {} + self.name = blind and blind.name or '' + self.dollars = blind and blind.dollars or 0 + self.sound_pings = self.dollars + 2 + if G.GAME.modifiers.no_blind_reward and G.GAME.modifiers.no_blind_reward[self:get_type()] then self.dollars = 0 end + self.debuff = blind and blind.debuff or {} + self.pos = blind and blind.pos + self.mult = blind and blind.mult or 0 + self.disabled = false + self.discards_sub = nil + self.hands_sub = nil + self.boss = blind and not not blind.boss + self.blind_set = false + self.triggered = nil + self.prepped = true + self:set_text() + + local obj = self.config.blind + self.children.animatedSprite.atlas = G.ANIMATION_ATLAS[obj.atlas] or G.ANIMATION_ATLAS['blind_chips'] + G.GAME.last_blind = G.GAME.last_blind or {} + G.GAME.last_blind.boss = self.boss + G.GAME.last_blind.name = self.name + + if blind and blind.name then + self:change_colour() + else + self:change_colour(G.C.BLACK) + end + + self.chips = get_blind_amount(G.GAME.round_resets.ante)*self.mult*G.GAME.starting_params.ante_scaling + self.chip_text = number_format(self.chips) + + if not blind then self.chips = 0 end + + G.GAME.current_round.dollars_to_be_earned = self.dollars > 0 and (string.rep(localize('$'), self.dollars)..'') or ('') + G.HUD_blind.alignment.offset.y = -10 + G.HUD_blind:recalculate(false) + + if blind and blind.name and blind.name ~= '' then + self:alert_debuff(true) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.05, + blockable = false, + func = (function() + G.HUD_blind:get_UIE_by_ID("HUD_blind_name").states.visible = false + G.HUD_blind:get_UIE_by_ID("dollars_to_be_earned").parent.parent.states.visible = false + G.HUD_blind.alignment.offset.y = 0 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.15, + blockable = false, + func = (function() + G.HUD_blind:get_UIE_by_ID("HUD_blind_name").states.visible = true + G.HUD_blind:get_UIE_by_ID("dollars_to_be_earned").parent.parent.states.visible = true + G.HUD_blind:get_UIE_by_ID("dollars_to_be_earned").config.object:pop_in(0) + G.HUD_blind:get_UIE_by_ID("HUD_blind_name").config.object:pop_in(0) + G.HUD_blind:get_UIE_by_ID("HUD_blind_count"):juice_up() + self.children.animatedSprite:set_sprite_pos(self.config.blind.pos) + self.blind_set = true + G.ROOM.jiggle = G.ROOM.jiggle + 3 + if not reset and not silent then + self:juice_up() + if blind then play_sound('chips1', math.random()*0.1 + 0.55, 0.42);play_sound('gold_seal', math.random()*0.1 + 1.85, 0.26)--play_sound('cancel') + end + end + return true + end) + })) + return true + end) + })) + end + + + self.config.h_popup_config ={align="tm", offset = {x=0,y=-0.1},parent = self} + end + + if self.name == 'The Eye' and not reset then + self.hands = { + ["Flush Five"] = false, + ["Flush House"] = false, + ["Five of a Kind"] = false, + ["Straight Flush"] = false, + ["Four of a Kind"] = false, + ["Full House"] = false, + ["Flush"] = false, + ["Straight"] = false, + ["Three of a Kind"] = false, + ["Two Pair"] = false, + ["Pair"] = false, + ["High Card"] = false, + } + end + if self.name == 'The Mouth' and not reset then + self.only_hand = false + end + if self.name == 'The Fish' and not reset then + self.prepped = nil + end + if self.name == 'The Water' and not reset then + self.discards_sub = G.GAME.current_round.discards_left + ease_discard(-self.discards_sub) + end + if self.name == 'The Needle' and not reset then + self.hands_sub = G.GAME.round_resets.hands - 1 + ease_hands_played(-self.hands_sub) + end + if self.name == 'The Manacle' and not reset then + G.hand:change_size(-1) + end + if self.name == 'Amber Acorn' and not reset and #G.jokers.cards > 0 then + G.jokers:unhighlight_all() + for k, v in ipairs(G.jokers.cards) do + v:flip() + end + if #G.jokers.cards > 1 then + G.E_MANAGER:add_event(Event({ trigger = 'after', delay = 0.2, func = function() + G.E_MANAGER:add_event(Event({ func = function() G.jokers:shuffle('aajk'); play_sound('cardSlide1', 0.85);return true end })) + delay(0.15) + G.E_MANAGER:add_event(Event({ func = function() G.jokers:shuffle('aajk'); play_sound('cardSlide1', 1.15);return true end })) + delay(0.15) + G.E_MANAGER:add_event(Event({ func = function() G.jokers:shuffle('aajk'); play_sound('cardSlide1', 1);return true end })) + delay(0.5) + return true end })) + end + end + + if not reset then + if blind then + self.in_blind = true + end + local obj = self.config.blind + if obj.set_blind and type(obj.set_blind) == 'function' then + obj:set_blind() + end + end + --add new debuffs + for _, v in ipairs(G.playing_cards) do + self:debuff_card(v) + end + for _, v in ipairs(G.jokers.cards) do + if not reset then self:debuff_card(v, true) end + end + + G.ARGS.spin.real = (G.SETTINGS.reduced_motion and 0 or 1)*(self.config.blind.boss and (self.config.blind.boss.showdown and 0.5 or 0.25) or 0) +end + +function Blind:alert_debuff(first) + if self.loc_debuff_text and self.loc_debuff_text ~= '' then + self.block_play = true + G.E_MANAGER:add_event(Event({ + blockable = false, + blocking = false, + func = (function() + if self.disabled then self.block_play = nil; return true end + if G.STATE == G.STATES.SELECTING_HAND then + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = G.SETTINGS.GAMESPEED*0.05, + blockable = false, + func = (function() + play_sound('whoosh1', 0.55, 0.62) + for i = 1, 4 do + local wait_time = (0.1*(i-1)) + G.E_MANAGER:add_event(Event({ blockable = false, trigger = 'after', delay = G.SETTINGS.GAMESPEED*wait_time, + func = function() + if i == 1 then self:juice_up() end + play_sound('cancel', 0.7 + 0.05*i, 0.7) + return true end })) + end + local hold_time = G.SETTINGS.GAMESPEED*(#self.loc_debuff_text*0.035 + 1.3) + local disp_text = self:get_loc_debuff_text() + attention_text({ + scale = 0.7, text = disp_text, maxw = 12, hold = hold_time, align = 'cm', offset = {x = 0,y = -1},major = G.play + }) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 1, + blocking = false, + blockable = false, + func = (function() + self.block_play = nil + if G.buttons then + local _buttons = G.buttons:get_UIE_by_ID('play_button') + _buttons.disable_button = nil + end + return true + end) + })) + return true + end) + })) + return true + end + end) + })) + end +end + +function Blind:get_loc_debuff_text() + local obj = self.config.blind + if obj.get_loc_debuff_text and type(obj.get_loc_debuff_text) == 'function' then + return obj:get_loc_debuff_text() + end + local disp_text = (self.config.blind.name == 'The Wheel' and G.GAME.probabilities.normal or '')..self.loc_debuff_text + if (self.config.blind.name == 'The Mouth') and self.only_hand then disp_text = disp_text..' ['..localize(self.only_hand, 'poker_hands')..']' end + return disp_text +end + +function Blind:defeat(silent) + local dissolve_time = 1.3 + local extra_time = 0 + self.dissolve = 0 + self.dissolve_colours = {G.C.BLACK, G.C.RED} + self:juice_up() + self.children.particles = Particles(0, 0, 0,0, { + timer_type = 'TOTAL', + timer = 0.01*dissolve_time, + scale = 0.1, + speed = 1.5, + lifespan = 0.7*dissolve_time, + attach = self, + colours = self.dissolve_colours, + fill = true + }) + + local blind_name_dynatext = G.HUD_blind:get_UIE_by_ID('HUD_blind_name').config.object + blind_name_dynatext:pop_out(2) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.5*dissolve_time, + func = (function() self.children.particles.max = 0 return true end) + })) + if not silent then + for i = 1, math.min(self.sound_pings or 3, 7) do + extra_time = extra_time + (0.4+0.15*i)*dissolve_time + G.E_MANAGER:add_event(Event({ blockable = false, trigger = 'after', delay = (0.15 - 0.01*(self.sound_pings or 3))*i*dissolve_time, + func = function() + play_sound('cancel', 0.8 - 0.05*i, 1.7) + if i == math.min((self.sound_pings or 3)+1, 6) then play_sound('whoosh2', 0.7, 0.42) end + return true end })) + end + end + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = false, + ref_table = self, + ref_value = 'dissolve', + ease_to = 1, + delay = 0.7*dissolve_time, + func = (function(t) return t end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.8*dissolve_time, + func = (function() + G.HUD_blind.alignment.offset.y = -10 + return true + end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.95*dissolve_time, + func = (function() + self.dissolve = nil + self:set_blind(nil, nil, true) return true end) + })) + for k, v in ipairs(G.jokers.cards) do + if v.facing == 'back' then v:flip() end + end + local obj = self.config.blind + if obj.defeat and type(obj.defeat) == 'function' then + obj:defeat() + end + if self.name == 'Crimson Heart' then + for _, v in ipairs(G.jokers.cards) do + v.ability.crimson_heart_chosen = nil + end + end + if self.name == 'The Manacle' and not self.disabled then + G.hand:change_size(1) + end +end + +function Blind:get_type() + if self.name == "Small Blind" then + return 'Small' + elseif self.name == "Big Blind" then + return 'Big' + elseif self.name and self.name ~= '' then + return 'Boss' + end +end + +function Blind:disable() + self.disabled = true + for k, v in ipairs(G.jokers.cards) do + if v.facing == 'back' then v:flip() end + end + local obj = self.config.blind + if obj.disable and type(obj.disable) == 'function' then + obj:disable() + end + if self.name == 'Crimson Heart' then + for _, v in ipairs(G.jokers.cards) do + v.ability.crimson_heart_chosen = nil + end + end + if self.name == 'The Water' then + ease_discard(self.discards_sub) + end + if self.name == 'The Wheel' or self.name == 'The House' or self.name == 'The Mark' or self.name == 'The Fish' then + for i = 1, #G.hand.cards do + if G.hand.cards[i].facing == 'back' then + G.hand.cards[i]:flip() + end + end + for k, v in pairs(G.playing_cards) do + v.ability.wheel_flipped = nil + end + end + if self.name == 'The Needle' then + ease_hands_played(self.hands_sub) + end + if self.name == 'The Wall' then + self.chips = self.chips/2 + self.chip_text = number_format(self.chips) + end + if self.name == 'Cerulean Bell' then + for k, v in ipairs(G.playing_cards) do + v.ability.forced_selection = nil + end + end + if self.name == 'The Manacle' then + G.hand:change_size(1) + end + if self.name == 'The Serpent' then + end + if self.name == 'Violet Vessel' then + self.chips = self.chips/3 + self.chip_text = number_format(self.chips) + end + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + if self.boss and to_big(G.GAME.chips) - G.GAME.blind.chips >= to_big(0) then + G.STATE = G.STATES.NEW_ROUND + G.STATE_COMPLETE = false + end + return true + end + })) + for _, v in ipairs(G.playing_cards) do + self:debuff_card(v) + end + for _, v in ipairs(G.jokers.cards) do + self:debuff_card(v) + end + self:set_text() + self:wiggle() +end + +function Blind:wiggle() + self.children.animatedSprite:juice_up(0.3) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + play_sound('tarot2', 1, 0.4) +end + +function Blind:juice_up(_a, _b) + self.children.animatedSprite:juice_up(_a or 0.2, _b or 0.2) +end + +function Blind:hover() + if not G.CONTROLLER.dragging.target or G.CONTROLLER.using_touch then + if not self.hovering and self.states.visible and self.children.animatedSprite.states.visible then + self.hovering = true + self.hover_tilt = 2 + self.children.animatedSprite:juice_up(0.05, 0.02) + play_sound('chips1', math.random()*0.1 + 0.55, 0.12) + Node.hover(self) + end + end +end + +function Blind:stop_hover() + self.hovering = false + self.hover_tilt = 0 + Node.stop_hover(self) +end + +function Blind:draw() + if not self.states.visible then return end + self.tilt_var = self.tilt_var or {} + self.tilt_var.mx, self.tilt_var.my =G.CONTROLLER.cursor_position.x,G.CONTROLLER.cursor_position.y + + self.children.animatedSprite.role.draw_major = self + self.children.animatedSprite:draw_shader('dissolve', 0.1) + self.children.animatedSprite:draw_shader('dissolve') + + for k, v in pairs(self.children) do + if k ~= 'animatedSprite' then + v.VT.scale = self.VT.scale + v:draw() + end + end + add_to_drawhash(self) +end + +function Blind:press_play() + if self.disabled then return end + local obj = self.config.blind + if obj.press_play and type(obj.press_play) == 'function' then + return obj:press_play() + end + if self.name == "The Hook" then + G.E_MANAGER:add_event(Event({ func = function() + local any_selected = nil + local _cards = {} + for k, v in ipairs(G.hand.cards) do + _cards[#_cards+1] = v + end + for i = 1, 2 do + if G.hand.cards[i] then + local selected_card, card_key = pseudorandom_element(_cards, pseudoseed('hook')) + G.hand:add_to_highlighted(selected_card, true) + table.remove(_cards, card_key) + any_selected = true + play_sound('card1', 1) + end + end + if any_selected then G.FUNCS.discard_cards_from_highlighted(nil, true) end + return true end })) + self.triggered = true + delay(0.7) + return true + end + if self.name == 'Crimson Heart' then + if G.jokers.cards[1] then + self.triggered = true + self.prepped = true + end + end + if self.name == 'The Fish' then + self.prepped = true + end + if self.name == "The Tooth" then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function() + for i = 1, #G.play.cards do + G.E_MANAGER:add_event(Event({func = function() G.play.cards[i]:juice_up(); return true end })) + ease_dollars(-1) + delay(0.23) + end + return true end })) + self.triggered = true + return true + end +end + +function Blind:modify_hand(cards, poker_hands, text, mult, hand_chips) + if self.disabled then return mult, hand_chips, false end + local obj = self.config.blind + if obj.modify_hand and type(obj.modify_hand) == 'function' then + return obj:modify_hand(cards, poker_hands, text, mult, hand_chips) + end + if self.name == "The Flint" then + self.triggered = true + return math.max(math.floor(mult*0.5 + 0.5), 1), math.max(math.floor(hand_chips*0.5 + 0.5), 0), true + end + return mult, hand_chips, false +end + +function Blind:debuff_hand(cards, hand, handname, check) + if self.disabled then return end + local obj = self.config.blind + if obj.debuff_hand and type(obj.debuff_hand) == 'function' then + return obj:debuff_hand(cards, hand, handname, check) + end + if self.debuff then + self.triggered = false + if self.debuff.hand and next(hand[self.debuff.hand]) then + self.triggered = true + return true + end + if self.name == "The Psychic" and #cards > 5 then + self.triggered = true + return true + end + if self.debuff.h_size_ge and #cards < self.debuff.h_size_ge then + self.triggered = true + return true + end + if self.debuff.h_size_le and #cards > self.debuff.h_size_le then + self.triggered = true + return true + end + if self.name == "The Eye" then + if self.hands[handname] then + self.triggered = true + return true + end + if not check then self.hands[handname] = true end + end + if self.name == "The Mouth" then + if self.only_hand and self.only_hand ~= handname then + self.triggered = true + return true + end + if not check then self.only_hand = handname end + end + end + if self.name == 'The Arm' then + self.triggered = false + if G.GAME.hands[handname].level > 1 then + self.triggered = true + if not check then + level_up_hand(self.children.animatedSprite, handname, nil, -1) + self:wiggle() + end + end + end + if self.name == 'The Ox' then + self.triggered = false + if handname == G.GAME.current_round.most_played_poker_hand then + self.triggered = true + if not check then + ease_dollars(-G.GAME.dollars, true) + self:wiggle() + end + end + end +end + +function Blind:drawn_to_hand() + if not self.disabled then + local obj = self.config.blind + if obj.drawn_to_hand and type(obj.drawn_to_hand) == 'function' then + obj:drawn_to_hand() + end if self.name == 'Cerulean Bell' then + local any_forced = nil + for k, v in ipairs(G.hand.cards) do + if v.ability.forced_selection then + any_forced = true + end + end + if not any_forced then + G.hand:unhighlight_all() + local forced_card = pseudorandom_element(G.hand.cards, pseudoseed('cerulean_bell')) + forced_card.ability.forced_selection = true + G.hand:add_to_highlighted(forced_card) + end + end + if self.name == 'Crimson Heart' and self.prepped and G.jokers.cards[1] then + local prev_chosen_set = {} + local fallback_jokers = {} + local jokers = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.crimson_heart_chosen then + prev_chosen_set[G.jokers.cards[i]] = true + G.jokers.cards[i].ability.crimson_heart_chosen = nil + if G.jokers.cards[i].debuff then SMODS.recalc_debuff(G.jokers.cards[i]) end + end + end + for i = 1, #G.jokers.cards do + if not G.jokers.cards[i].debuff then + if not prev_chosen_set[G.jokers.cards[i]] then + jokers[#jokers+1] = G.jokers.cards[i] + end + table.insert(fallback_jokers, G.jokers.cards[i]) + end + end + if #jokers == 0 then jokers = fallback_jokers end + local _card = pseudorandom_element(jokers, pseudoseed('crimson_heart')) + if _card then + _card.ability.crimson_heart_chosen = true + SMODS.recalc_debuff(_card) + _card:juice_up() + self:wiggle() + end + end + end + self.prepped = nil +end + +function Blind:stay_flipped(area, card) + if not self.disabled then + local obj = self.config.blind + if obj.stay_flipped and type(obj.stay_flipped) == 'function' then + return obj:stay_flipped(area, card) + end + if area == G.hand then + if self.name == 'The Wheel' and pseudorandom(pseudoseed('wheel')) < G.GAME.probabilities.normal/7 then + return true + end + if self.name == 'The House' and G.GAME.current_round.hands_played == 0 and G.GAME.current_round.discards_used == 0 then + return true + end + if self.name == 'The Mark' and card:is_face(true) then + return true + end + if self.name == 'The Fish' and self.prepped then + return true + end + end + end +end + +function Blind:debuff_card(card, from_blind) + local obj = self.config.blind + if not self.disabled and obj.recalc_debuff and type(obj.recalc_debuff) == 'function' then + if obj:recalc_debuff(card, from_blind) then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + else + card:set_debuff(false) + end + return + elseif not self.disabled and obj.debuff_card and type(obj.debuff_card) == 'function' then + sendWarnMessage(("Blind object %s has debuff_card function, recalc_debuff is preferred"):format(obj.key), obj.set) + if obj:debuff_card(card, from_blind) then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + else + card:set_debuff(false) + end + return + end + if self.debuff and not self.disabled and card.area ~= G.jokers then + if self.debuff.suit and card:is_suit(self.debuff.suit, true) then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + return + end + if self.debuff.is_face =='face' and card:is_face(true) then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + return + end + if self.name == 'The Pillar' and card.ability.played_this_ante then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + return + end + if self.debuff.value and self.debuff.value == card.base.value then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + return + end + if self.debuff.nominal and self.debuff.nominal == card.base.nominal then + card:set_debuff(true) + if card.debuff then card.debuffed_by_blind = true end + return + end + end + if self.name == 'Crimson Heart' and not self.disabled and card.area == G.jokers then + if card.ability.crimson_heart_chosen then + card:set_debuff(true); + if card.debuff then card.debuffed_by_blind = true end + return + end + end + if self.name == 'Verdant Leaf' and not self.disabled and card.area ~= G.jokers then card:set_debuff(true); if card.debuff then card.debuffed_by_blind = true end; return end + card:set_debuff(false) +end + +function Blind:move(dt) + Moveable.move(self, dt) + self:align() +end + +function Blind:change_dim(w, h) + self.T.w = w or self.T.w + self.T.h = h or self.T.h + self.children.animatedSprite.T.w = w or self.T.w + self.children.animatedSprite.T.h = h or self.T.h + self.children.animatedSprite:rescale() +end + +function Blind:align() + for k, v in pairs(self.children) do + if k == 'animatedSprite' then + if not v.states.drag.is then + v.T.r = 0.02*math.sin(2*G.TIMERS.REAL+self.T.x) + v.T.y = self.T.y + 0.03*math.sin(0.666*G.TIMERS.REAL+self.T.x) + self.shadow_height = 0.1 - (0.04 + 0.03*math.sin(0.666*G.TIMERS.REAL+self.T.x)) + v.T.x = self.T.x + 0.03*math.sin(0.436*G.TIMERS.REAL+self.T.x) + end + else + v.T.x = self.T.x + v.T.y = self.T.y + v.T.r = self.T.r + end + end +end + +function Blind:save() + local blindTable = { + in_blind = self.in_blind, + name = self.name, + dollars = self.dollars, + debuff = self.debuff, + pos = self.pos, + mult = self.mult, + disabled = self.disabled, + discards_sub = self.discards_sub, + hands_sub = self.hands_sub, + boss = self.boss, + config_blind = '', + chips = self.chips, + chip_text =self.chip_text, + hands = self.hands, + only_hand = self.only_hand, + triggered = self.triggered + } + + for k, v in pairs(G.P_BLINDS) do + if v and v.name and v.name == blindTable.name then + blindTable.config_blind = k + end + end + + return blindTable +end + +function Blind:load(blindTable) + self.in_blind = blindTable.in_blind + self.config.blind = G.P_BLINDS[blindTable.config_blind] or {} + + self.name = blindTable.name + self.dollars = blindTable.dollars + self.debuff = blindTable.debuff + self.pos = blindTable.pos + self.mult = blindTable.mult + self.disabled = blindTable.disabled + self.discards_sub = blindTable.discards_sub + self.hands_sub = blindTable.hands_sub + self.boss = blindTable.boss + self.chips = blindTable.chips + self.chip_text = blindTable.chip_text + self.hands = blindTable.hands + self.only_hand = blindTable.only_hand + self.triggered = blindTable.triggered + + G.ARGS.spin.real = (G.SETTINGS.reduced_motion and 0 or 1)*(self.config.blind.boss and (self.config.blind.boss.showdown and 1 or 0.3) or 0) + + if G.P_BLINDS[blindTable.config_blind] then + if self.config.blind.atlas then + self.children.animatedSprite.atlas = G.ANIMATION_ATLAS[self.config.blind.atlas] + end + self.blind_set = true + self.children.animatedSprite.states.visible = true + self.children.animatedSprite:set_sprite_pos(self.config.blind.pos) + self.children.animatedSprite:juice_up(0.3) + self:align() + self.children.animatedSprite:hard_set_VT() + else + self.children.animatedSprite.states.visible = false + end + + self.children.animatedSprite.states = self.states + self:change_colour() + if self.dollars > 0 then + G.GAME.current_round.dollars_to_be_earned = string.rep(localize('$'), self.dollars)..'' + G.HUD_blind:get_UIE_by_ID("dollars_to_be_earned").config.object:pop_in(0) + end + if G.GAME.blind.name and G.GAME.blind.name ~= '' then + G.HUD_blind.alignment.offset.y = 0 + end + self:set_text() +end diff --git a/lovely/dump/card.lua b/lovely/dump/card.lua new file mode 100644 index 0000000..815d1af --- /dev/null +++ b/lovely/dump/card.lua @@ -0,0 +1,5431 @@ +LOVELY_INTEGRITY = '68c2ebd87be76547897e6d6962d4d584197f285bae1b2b46c4916611b072fcd7' + +--class +Card = Moveable:extend() + +--class methods +function Card:init(X, Y, W, H, card, center, params) + self.params = (type(params) == 'table') and params or {} + + Moveable.init(self,X, Y, W, H) + + self.CT = self.VT + self.config = { + card = card or {}, + center = center + } + self.tilt_var = {mx = 0, my = 0, dx = 0, dy = 0, amt = 0} + self.ambient_tilt = 0.2 + + self.states.collide.can = true + self.states.hover.can = true + self.states.drag.can = true + self.states.click.can = true + + self.playing_card = self.params.playing_card + G.sort_id = (G.sort_id or 0) + 1 + self.sort_id = G.sort_id + + if self.params.viewed_back then self.back = 'viewed_back' + else self.back = 'selected_back' end + self.bypass_discovery_center = self.params.bypass_discovery_center + self.bypass_discovery_ui = self.params.bypass_discovery_ui + self.bypass_lock = self.params.bypass_lock + self.no_ui = self.config.card and self.config.card.no_ui + self.children = {} + self.base_cost = 0 + self.extra_cost = 0 + self.cost = 0 + self.sell_cost = 0 + self.sell_cost_label = 0 + self.children.shadow = Moveable(0, 0, 0, 0) + self.unique_val = 1-self.ID/1603301 + self.edition = nil + self.zoom = true + self:set_ability(center, true) + self:set_base(card, true) + + self.discard_pos = { + r = 3.6*(math.random()-0.5), + x = math.random(), + y = math.random() + } + + self.facing = 'front' + self.sprite_facing = 'front' + self.flipping = nil + self.area = nil + self.highlighted = false + self.click_timeout = 0.3 + self.T.scale = 0.95 + self.debuff = false + + self.rank = nil + self.added_to_deck = nil + + if self.children.front then self.children.front.VT.w = 0 end + self.children.back.VT.w = 0 + self.children.center.VT.w = 0 + + if self.children.front then self.children.front.parent = self; self.children.front.layered_parallax = nil end + self.children.back.parent = self; self.children.back.layered_parallax = nil + self.children.center.parent = self; self.children.center.layered_parallax = nil + + self:set_cost() + + if getmetatable(self) == Card then + table.insert(G.I.CARD, self) + end +end + +function Card:update_alert() + if self.ability.set == 'Default' and self.config.center and self.config.center.key == 'c_base' and self.seal then + if G.P_SEALS[self.seal].alerted and self.children.alert then + self.children.alert:remove() + self.children.alert = nil + elseif not G.P_SEALS[self.seal].alerted and not self.children.alert and G.P_SEALS[self.seal].discovered then + self.children.alert = UIBox{ + definition = create_UIBox_card_alert(), + config = {align="tli", + offset = {x = 0.1, y = 0.1}, + parent = self} + } + end + end + if (self.ability.set == 'Joker' or self.ability.set == 'Voucher' or self.ability.consumeable or self.ability.set == 'Edition' or self.ability.set == 'Booster') then + if self.area and self.area.config.collection and self.config.center then + if self.config.center.alerted and self.children.alert then + self.children.alert:remove() + self.children.alert = nil + elseif not self.config.center.alerted and not self.children.alert and self.config.center.discovered then + self.children.alert = UIBox{ + definition = create_UIBox_card_alert(), + config = {align=(self.ability.set == 'Voucher' and (self.config.center.order%2)==1) and "tli" or "tri", + offset = {x = (self.ability.set == 'Voucher' and (self.config.center.order%2)==1) and 0.1 or -0.1, y = 0.1}, + parent = self} + } + end + end + end +end + +function Card:set_base(card, initial) + card = card or {} + + self.config.card = card + for k, v in pairs(G.P_CARDS) do + if card == v then self.config.card_key = k end + end + + if next(card) then + self:set_sprites(nil, card) + end + + local suit_base_nominal_original = nil + if self.base and self.base.suit_nominal_original then suit_base_nominal_original = self.base.suit_nominal_original end + self.base = { + name = self.config.card.name, + suit = self.config.card.suit, + value = self.config.card.value, + nominal = 0, + suit_nominal = 0, + face_nominal = 0, + colour = G.C.SUITS[self.config.card.suit], + times_played = 0 + } + + local rank = SMODS.Ranks[self.base.value] or {} + self.base.nominal = rank.nominal or 0 + self.base.face_nominal = rank.face_nominal or 0 + self.base.id = rank.id + + if initial then self.base.original_value = self.base.value end + + local suit = SMODS.Suits[self.base.suit] or {} + self.base.suit_nominal = suit.suit_nominal or 0 + self.base.suit_nominal_original = suit_base_nominal_original or suit.suit_nominal or 0 + + if not initial and G.GAME and G.GAME.blind then G.GAME.blind:debuff_card(self) end + if self.playing_card and not initial then check_for_unlock({type = 'modify_deck'}) end +end + +function Card:set_sprites(_center, _front) + if _front then + local _atlas, _pos = get_front_spriteinfo(_front) + if self.children.front then + self.children.front.atlas = _atlas + self.children.front:set_sprite_pos(_pos) + else + self.children.front = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, _atlas, _pos) + self.children.front.states.hover = self.states.hover + self.children.front.states.click = self.states.click + self.children.front.states.drag = self.states.drag + self.children.front.states.collide.can = false + self.children.front:set_role({major = self, role_type = 'Glued', draw_major = self}) + end + end + if _center then + if _center.set then + if self.children.center then + self.children.center.atlas = G.ASSET_ATLAS[(_center.atlas or (_center.set == 'Joker' or _center.consumeable or _center.set == 'Voucher') and _center.set) or 'centers'] + self.children.center:set_sprite_pos(_center.pos) + else + if _center.set == 'Joker' and not _center.unlocked and not self.params.bypass_discovery_center then + self.children.center = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ASSET_ATLAS["Joker"], G.j_locked.pos) + elseif self.config.center.set == 'Voucher' and not self.config.center.unlocked and not self.params.bypass_discovery_center then + self.children.center = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ASSET_ATLAS["Voucher"], G.v_locked.pos) + elseif self.config.center.consumeable and self.config.center.demo then + self.children.center = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ASSET_ATLAS["Tarot"], G.c_locked.pos) + elseif not self.params.bypass_discovery_center and (_center.set == 'Edition' or _center.set == 'Joker' or _center.consumeable or _center.set == 'Voucher' or _center.set == 'Booster') and not _center.discovered then + self.children.center = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ASSET_ATLAS[(_center.undiscovered and (_center.undiscovered[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.undiscovered.atlas)) + or (SMODS.UndiscoveredSprites[_center.set] and (SMODS.UndiscoveredSprites[_center.set][G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or SMODS.UndiscoveredSprites[_center.set].atlas)) + or _center.set] or G.ASSET_ATLAS["Joker"], + (_center.undiscovered and _center.undiscovered.pos) or (SMODS.UndiscoveredSprites[_center.set] and SMODS.UndiscoveredSprites[_center.set].pos) or + (_center.set == 'Joker' and G.j_undiscovered.pos) or + (_center.set == 'Edition' and G.j_undiscovered.pos) or + (_center.set == 'Tarot' and G.t_undiscovered.pos) or + (_center.set == 'Planet' and G.p_undiscovered.pos) or + (_center.set == 'Spectral' and G.s_undiscovered.pos) or + (_center.set == 'Voucher' and G.v_undiscovered.pos) or + (_center.set == 'Booster' and G.booster_undiscovered.pos) or G.j_undiscovered.pos) + elseif _center.set == 'Joker' or _center.consumeable or _center.set == 'Voucher' then + self.children.center = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ASSET_ATLAS[_center[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.atlas or _center.set], self.config.center.pos) + else + self.children.center = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ASSET_ATLAS[_center.atlas or 'centers'], _center.pos) + end + self.children.center.states.hover = self.states.hover + self.children.center.states.click = self.states.click + self.children.center.states.drag = self.states.drag + self.children.center.states.collide.can = false + self.children.center:set_role({major = self, role_type = 'Glued', draw_major = self}) + end + if _center.name == 'Half Joker' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.y/1.7 + end + if _center.name == "cry-Cube" and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.y*0.1 + self.children.center.scale.x = self.children.center.scale.x*0.1 + end + if _center.name == 'cry-Jimball' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.x + self.children.center.scale.y = self.children.center.scale.y*57/69 + self.children.center.scale.x = self.children.center.scale.x*57/69 + end + if _center.name == 'cry-Potion' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.x + self.children.center.scale.y = self.children.center.scale.y*35/69 + self.children.center.scale.x = self.children.center.scale.x*35/69 + end + if _center.name == 'cry-magnet' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.x + self.children.center.scale.y = self.children.center.scale.y*35/71 + self.children.center.scale.x = self.children.center.scale.x*35/71 + end + if _center.name == 'Photograph' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.y/1.2 + end + if _center.name == 'Square Joker' and (_center.discovered or self.bypass_discovery_center) then + self.children.center.scale.y = self.children.center.scale.x + end + end + + if _center.soul_pos then + self.children.floating_sprite = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ASSET_ATLAS[_center[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or _center.atlas or _center.set], self.config.center.soul_pos) + self.children.floating_sprite.role.draw_major = self + self.children.floating_sprite.states.hover.can = false + self.children.floating_sprite.states.click.can = false + end + + if _center.set_sprites and type(_center.set_sprites) == 'function' then + _center:set_sprites(self, _front) + end + if true then + self.children.back = Sprite(self.T.x, self.T.y, self.T.w, self.T.h, G.ASSET_ATLAS[(G.GAME.viewed_back or G.GAME.selected_back) and ((G.GAME.viewed_back or G.GAME.selected_back)[G.SETTINGS.colourblind_option and 'hc_atlas' or 'lc_atlas'] or (G.GAME.viewed_back or G.GAME.selected_back).atlas) or 'centers'], self.params.bypass_back or (self.playing_card and G.GAME[self.back].pos or G.P_CENTERS['b_red'].pos)) + self.children.back.states.hover = self.states.hover + self.children.back.states.click = self.states.click + self.children.back.states.drag = self.states.drag + self.children.back.states.collide.can = false + self.children.back:set_role({major = self, role_type = 'Glued', draw_major = self}) + end + end +end + +function Card:set_ability(center, initial, delay_sprites) + local X, Y, W, H = self.T.x, self.T.y, self.T.w, self.T.h + + local old_center = self.config.center + if old_center and not next(SMODS.find_card(old_center.key, true)) then + G.GAME.used_jokers[old_center.key] = nil + end + if self.added_to_deck and old_center and not self.debuff then + self:remove_from_deck() + self.added_to_deck = true + end + self.config.center = center + self.sticker_run = nil + for k, v in pairs(G.P_CENTERS) do + if center == v then self.config.center_key = k end + end + + if self.params.discover and not center.discovered then + unlock_card(center) + discover_card(center) + end + + if center.name == "Half Joker" and (center.discovered or self.bypass_discovery_center) then + H = H/1.7 + self.T.h = H + end + + if center.name == "Photograph" and (center.discovered or self.bypass_discovery_center) then + H = H/1.2 + self.T.h = H + end + + if center.name == "Square Joker" and (center.discovered or self.bypass_discovery_center) then + H = W + self.T.h = H + end + + if (center.name == "cry-Wee Fibonacci" or center.name == "cry-reverse") and (center.discovered or self.bypass_discovery_center) then + H = H*0.7 + W = W*0.7 + self.T.h = H + self.T.w = W + end + if center.name == "cry-biggestm" and (center.discovered or self.bypass_discovery_center) then + H = H*1.7 + W = W*1.7 + self.T.h = H + self.T.w = W + end + if center.name == "cry-Booster Joker" and (center.discovered or self.bypass_discovery_center) then + H = H*1.17 + W = W*1.17 + self.T.h = H + self.T.w = W + end + if center.name == "cry-Cube" and (center.discovered or self.bypass_discovery_center) then + H = H*0.1 + W = W*0.1 + self.T.h = H + self.T.w = W + end + if center.name == "cry-Potion" and (center.discovered or self.bypass_discovery_center) then + H = W + self.T.h = H + H = H*35/69 + W = W*35/69 + self.T.h = H + self.T.w = W + end + if center.name == "cry-Jimball" and (center.discovered or self.bypass_discovery_center) then + H = W + self.T.h = H + H = H*57/69 + W = W*57/69 + self.T.h = H + self.T.w = W + end + if center.name == "cry-magnet" and (center.discovered or self.bypass_discovery_center) then + H = W + self.T.h = H + H = H*35/71 + W = W*35/71 + self.T.h = H + self.T.w = W + end + if center.name == "Wee Joker" and (center.discovered or self.bypass_discovery_center) then + H = H*0.7 + W = W*0.7 + self.T.h = H + self.T.w = W + end + + if delay_sprites then + G.E_MANAGER:add_event(Event({ + func = function() + if not self.REMOVED then + self:set_sprites(center) + end + return true + end + })) + else + self:set_sprites(center) + end + + if self.ability and old_center and old_center.config.bonus then + self.ability.bonus = self.ability.bonus - old_center.config.bonus + end + + local new_ability = { + name = center.name, + effect = center.effect, + set = center.set, + mult = center.config.mult or 0, + h_mult = center.config.h_mult or 0, + h_x_mult = center.config.h_x_mult or 0, + h_dollars = center.config.h_dollars or 0, + p_dollars = center.config.p_dollars or 0, + t_mult = center.config.t_mult or 0, + t_chips = center.config.t_chips or 0, + x_mult = center.config.Xmult or 1, + e_mult = center.config.Emult or 0, + ee_mult = center.config.EEmult or 0, + eee_mult = center.config.EEEmult or 0, + hyper_mult = type(center.config.Hmult) == 'table' and center.config.Hmult or {0, 0}, + x_chips = center.config.Xchips or 0, + e_chips = center.config.Echips or 0, + ee_chips = center.config.EEchips or 0, + eee_chips = center.config.EEEchips or 0, + hyper_chips = type(center.config.Hchips) == 'table' and center.config.Hchips or {0, 0}, + h_size = center.config.h_size or 0, + d_size = center.config.d_size or 0, + extra = copy_table(center.config.extra) or nil, + extra_value = 0, + type = center.config.type or '', + order = center.order or nil, + forced_selection = self.ability and self.ability.forced_selection or nil, + perma_bonus = self.ability and self.ability.perma_bonus or 0, + eternal = self.ability and self.ability.eternal, + perishable = self.ability and self.ability.perishable, + perish_tally = self.ability and self.ability.perish_tally, + rental = self.ability and self.ability.rental + } + self.ability = self.ability or {} + new_ability.extra_value = nil + self.ability.extra_value = self.ability.extra_value or 0 + for k, v in pairs(new_ability) do + self.ability[k] = v + end + -- reset keys do not persist an ability change + local reset_keys = {'name', 'effect', 'set', 'extra', 'played_this_ante'} + for _, mod in ipairs(SMODS.mod_list) do + if mod.set_ability_reset_keys then + local keys = mod.set_ability_reset_keys() + for _, v in pairs(keys) do table.insert(reset_keys, v) end + end + end + for _, k in ipairs(reset_keys) do + self.ability[k] = new_ability[k] + end + + self.ability.bonus = (self.ability.bonus or 0) + (center.config.bonus or 0) + if not self.ability.name then self.ability.name = center.key end + for k, v in pairs(center.config) do + if k ~= 'bonus' then + if type(v) == 'table' then + self.ability[k] = copy_table(v) + else + self.ability[k] = v + end + end + end + + if center.consumeable then + self.ability.consumeable = center.config + end + + if self.ability.name == 'Gold Card' and self.seal == 'Gold' and self.playing_card then + check_for_unlock({type = 'double_gold'}) + end + if self.ability.name == "Invisible Joker" then + self.ability.invis_rounds = 0 + end + if self.ability.name == 'To Do List' then + local _poker_hands = {} + for k, v in pairs(G.GAME.hands) do + if v.visible then _poker_hands[#_poker_hands+1] = k end + end + local old_hand = self.ability.to_do_poker_hand + self.ability.to_do_poker_hand = nil + + while not self.ability.to_do_poker_hand do + self.ability.to_do_poker_hand = pseudorandom_element(_poker_hands, pseudoseed((self.area and self.area.config.type == 'title') and 'false_to_do' or 'to_do')) + if self.ability.to_do_poker_hand == old_hand then self.ability.to_do_poker_hand = nil end + end + end + if self.ability.name == 'Caino' then + self.ability.caino_xmult = 1 + end + if self.ability.name == 'Yorick' then + self.ability.yorick_discards = self.ability.extra.discards + end + if self.ability.name == 'Loyalty Card' then + self.ability.burnt_hand = 0 + self.ability.loyalty_remaining = self.ability.extra.every + end + + self.base_cost = center.cost or 1 + + self.ability.hands_played_at_create = G.GAME and G.GAME.hands_played or 0 + + self.label = center.label or self.config.card and self.config.card.label or self.ability.set + if self.ability.set == 'Joker' then self.label = self.ability.name end + if self.ability.set == 'Edition' then self.label = self.ability.name end + if self.ability.consumeable then self.label = self.ability.name end + if self.ability.set == 'Voucher' then self.label = self.ability.name end + if self.ability.set == 'Booster' then + self.label = self.ability.name + self.mouse_damping = 1.5 + end + + local obj = self.config.center + if obj.set_ability and type(obj.set_ability) == 'function' then + obj:set_ability(self, initial, delay_sprites) + end + + if not G.OVERLAY_MENU then + if self.config.center.key then + G.GAME.used_jokers[self.config.center.key] = true + end + + end + + if G.jokers and self.area == G.jokers then + check_for_unlock({type = 'modify_jokers'}) + end + + if self.added_to_deck and old_center and not self.debuff then + self.added_to_deck = false + self:add_to_deck() + end + if G.consumeables and self.area == G.consumeables then + check_for_unlock({type = 'modify_jokers'}) + end + + if not initial and G.GAME and G.GAME.blind then G.GAME.blind:debuff_card(self) end + if self.playing_card and not initial then check_for_unlock({type = 'modify_deck'}) end +end + +function Card:set_cost() + self.extra_cost = 0 + G.GAME.inflation + if self.edition then + for k, v in pairs(G.P_CENTER_POOLS.Edition) do + if self.edition[v.key:sub(3)] then + if v.extra_cost then + self.extra_cost = self.extra_cost + v.extra_cost + end + end + end + end + self.cost = math.max(1, math.floor((self.base_cost + self.extra_cost + 0.5)*(100-G.GAME.discount_percent)/100)) + if self.ability.set == 'Joker' then + self.cost = cry_format(self.cost * G.GAME.cry_shop_joker_price_modifier,'%.2f') end + if self.misprint_cost_fac then + self.cost = cry_format(self.cost * self.misprint_cost_fac,'%.2f') + if not G.GAME.modifiers.cry_misprint_min then self.cost = math.floor(self.cost) end end + if self.ability.set == 'Booster' and G.GAME.modifiers.booster_ante_scaling then self.cost = self.cost + G.GAME.round_resets.ante - 1 end + if self.ability.set == 'Booster' and (not G.SETTINGS.tutorial_complete) and G.SETTINGS.tutorial_progress and (not G.SETTINGS.tutorial_progress.completed_parts['shop_1']) then + self.cost = self.cost + 3 + end + if (self.ability.set == 'Planet' or (self.ability.set == 'Booster' and self.ability.name:find('Celestial'))) and #find_joker('Astronomer') > 0 then self.cost = 0 end + if self.ability.rental and (not (self.ability.set == "Planet" and #find_joker('Astronomer') > 0) and self.ability.set ~= "Booster") then self.cost = 1 end + self.sell_cost = math.max(1, math.floor(self.cost/2)) + (self.ability.extra_value or 0) + if self.area and self.ability.couponed and (self.area == G.shop_jokers or self.area == G.shop_booster) then self.cost = 0 end + self.sell_cost_label = (self.facing == 'back' and '?') or (G.GAME.modifiers.cry_no_sell_value and 0) or self.sell_cost +end + +function Card:set_edition(edition, immediate, silent) + self.edition = nil + if not edition then return end + if edition.holo then + if not self.edition then self.edition = {} end + self.edition.mult = G.P_CENTERS.e_holo.config.extra + self.edition.holo = true + self.edition.type = 'holo' + elseif edition.foil then + if not self.edition then self.edition = {} end + self.edition.chips = G.P_CENTERS.e_foil.config.extra + self.edition.foil = true + self.edition.type = 'foil' + elseif edition.polychrome then + if not self.edition then self.edition = {} end + self.edition.x_mult = G.P_CENTERS.e_polychrome.config.extra + self.edition.polychrome = true + self.edition.type = 'polychrome' + elseif edition.negative then + if not self.edition then + self.edition = {} + if self.added_to_deck then --Need to override if adding negative to an existing joker + if self.ability.consumeable then + G.consumeables.config.card_limit = G.consumeables.config.card_limit + 1 + else + G.jokers.config.card_limit = G.jokers.config.card_limit + 1 + end + end + end + self.edition.negative = true + self.edition.type = 'negative' + end + + if self.area and self.area == G.jokers then + if self.edition then + if self.edition.type and G.P_CENTERS['e_'..(self.edition.type)] and not G.P_CENTERS['e_'..(self.edition.type)].discovered then + discover_card(G.P_CENTERS['e_'..(self.edition.type)]) + end + else + if not G.P_CENTERS['e_base'].discovered then + discover_card(G.P_CENTERS['e_base']) + end + end + end + + if self.edition and (not Talisman.config_file.disable_anims or (not Talisman.calculating_joker and not Talisman.calculating_score and not Talisman.calculating_card)) and not silent then + G.CONTROLLER.locks.edition = true + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = not immediate and 0.2 or 0, + blockable = not immediate, + func = function() + self:juice_up(1, 0.5) + if self.edition.foil then play_sound('foil1', 1.2, 0.4) end + if self.edition.holo then play_sound('holo1', 1.2*1.58, 0.4) end + if self.edition.polychrome then play_sound('polychrome1', 1.2, 0.7) end + if self.edition.negative then play_sound('negative', 1.5, 0.4) end + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + G.CONTROLLER.locks.edition = false + return true + end + })) + end + + if G.jokers and self.area == G.jokers then + check_for_unlock({type = 'modify_jokers'}) + end + + self:set_cost() +end + +function Card:set_seal(_seal, silent, immediate) + self.seal = nil + if _seal then + self.seal = _seal + if not silent then + G.CONTROLLER.locks.seal = true + local sound = G.P_SEALS[_seal].sound or {sound = 'gold_seal', per = 1.2, vol = 0.4} + if immediate then + self:juice_up(0.3, 0.3) + play_sound(sound.sound, sound.per, sound.vol) + G.CONTROLLER.locks.seal = false + else + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.3, + func = function() + self:juice_up(0.3, 0.3) + play_sound(sound.sound, sound.per, sound.vol) + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.15, + func = function() + G.CONTROLLER.locks.seal = false + return true + end + })) + end + end + end + if self.ability.name == 'Gold Card' and self.seal == 'Gold' and self.playing_card then + check_for_unlock({type = 'double_gold'}) + end + self:set_cost() +end + +function Card:get_seal(bypass_debuff) + if self.debuff and not bypass_debuff then return end + return self.seal +end + +function Card:set_eternal(_eternal) + self.ability.eternal = nil + if self.config.center.eternal_compat and not self.ability.perishable then + self.ability.eternal = _eternal + end +end + +function Card:set_perishable(_perishable) + self.ability.perishable = nil + if self.config.center.perishable_compat and not self.ability.eternal then + self.ability.perishable = true + self.ability.perish_tally = G.GAME.perishable_rounds + end +end + +function Card:set_rental(_rental) + self.ability.rental = _rental + self:set_cost() +end + +function Card:set_debuff(should_debuff) + for _, mod in ipairs(SMODS.mod_list) do + if mod.set_debuff and type(mod.set_debuff) == 'function' then + local res = mod.set_debuff(self) + if res == 'prevent_debuff' then + if self.debuff then + self.debuff = false + if self.area == G.jokers then self:add_to_deck(true) end + self.debuffed_by_blind = false + end + return + end + should_debuff = should_debuff or res + end + end + for k, v in pairs(self.ability.debuff_sources or {}) do + if v == 'prevent_debuff' then + if self.debuff then + self.debuff = false + if self.area == G.jokers then self:add_to_deck(true) end + end + self.debuffed_by_blind = false + return + end + should_debuff = should_debuff or v + end + + if self.ability.perishable and not self.ability.perish_tally then self.ability.perish_tally = G.GAME.perishable_rounds end + if self.ability.perishable and self.ability.perish_tally <= 0 then + if not self.debuff then + self.debuff = true + if self.area == G.jokers then self:remove_from_deck(true) end + end + return + end + if should_debuff ~= self.debuff then + if self.area == G.jokers then if should_debuff then self:remove_from_deck(true) else self:add_to_deck(true) end end + self.debuff = should_debuff + end + if not self.debuff then self.debuffed_by_blind = false end + +end + +function Card:remove_UI() + self.ability_UIBox_table = nil + self.config.h_popup = nil + self.config.h_popup_config = nil + self.no_ui = true +end + +function Card:change_suit(new_suit) + local new_code = SMODS.Suits[new_suit].card_key + local new_val = SMODS.Ranks[self.base.value].card_key + local new_card = G.P_CARDS[new_code..'_'..new_val] + + self:set_base(new_card) + G.GAME.blind:debuff_card(self) +end + +function Card:add_to_deck(from_debuff) + if not self.config.center.discovered then + discover_card(self.config.center) + end + if not self.added_to_deck then + self.added_to_deck = true + if self.ability.set == 'Enhanced' or self.ability.set == 'Default' then + if self.ability.name == 'Gold Card' and self.seal == 'Gold' and self.playing_card then + check_for_unlock({type = 'double_gold'}) + end + return + end + + if self.edition then + if self.edition.type and G.P_CENTERS['e_'..(self.edition.type)] and not G.P_CENTERS['e_'..(self.edition.type)].discovered then + discover_card(G.P_CENTERS['e_'..(self.edition.type)]) + end + else + if not G.P_CENTERS['e_base'].discovered then + discover_card(G.P_CENTERS['e_base']) + end + end + local obj = self.config.center + if obj and obj.add_to_deck and type(obj.add_to_deck) == 'function' then + obj:add_to_deck(self, from_debuff) + end if self.ability.h_size ~= 0 then + G.hand:change_size(self.ability.h_size) + end + if self.ability.d_size > 0 then + G.GAME.round_resets.discards = G.GAME.round_resets.discards + self.ability.d_size + ease_discard(self.ability.d_size) + end + if self.ability.name == 'Credit Card' then + G.GAME.bankrupt_at = G.GAME.bankrupt_at - self.ability.extra + end + if self.ability.name == 'Chicot' and G.GAME.blind and G.GAME.blind.boss and not G.GAME.blind.disabled then + G.GAME.blind:disable() + play_sound('timpani') + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('ph_boss_disabled')}) + end + if self.ability.name == 'Chaos the Clown' then + G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls + 1 + calculate_reroll_cost(true) + end + if self.ability.name == 'Turtle Bean' then + G.hand:change_size(self.ability.extra.h_size) + end + if self.ability.name == 'Oops! All 6s' then + for k, v in pairs(G.GAME.probabilities) do + G.GAME.probabilities[k] = v*2 + end + end + if self.ability.name == 'To the Moon' then + G.GAME.interest_amount = G.GAME.interest_amount + self.ability.extra + end + if self.ability.name == 'Astronomer' then + G.E_MANAGER:add_event(Event({func = function() + for k, v in pairs(G.I.CARD) do + if v.set_cost then v:set_cost() end + end + return true end })) + end + if self.ability.name == 'Troubadour' then + G.hand:change_size(self.ability.extra.h_size) + G.GAME.round_resets.hands = G.GAME.round_resets.hands + self.ability.extra.h_plays + end + if self.ability.name == 'Stuntman' then + G.hand:change_size(-self.ability.extra.h_size) + end + if true then + if from_debuff then + self.ability.joker_added_to_deck_but_debuffed = nil + else + if self.edition and self.edition.card_limit then + if self.ability.consumeable then + G.consumeables.config.card_limit = G.consumeables.config.card_limit + self.edition.card_limit + else + G.jokers.config.card_limit = G.jokers.config.card_limit + self.edition.card_limit + end + end + end + end + if G.GAME.blind and G.GAME.blind.in_blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end + end +end + +function Card:remove_from_deck(from_debuff) + if self.added_to_deck then + self.added_to_deck = false + local obj = self.config.center + if obj and obj.remove_from_deck and type(obj.remove_from_deck) == 'function' then + obj:remove_from_deck(self, from_debuff) + end if self.ability.h_size ~= 0 then + G.hand:change_size(-self.ability.h_size) + end + if self.ability.d_size > 0 then + G.GAME.round_resets.discards = G.GAME.round_resets.discards - self.ability.d_size + ease_discard(-self.ability.d_size) + end + if self.ability.name == 'Credit Card' then + G.GAME.bankrupt_at = G.GAME.bankrupt_at + self.ability.extra + end + if self.ability.name == 'Chaos the Clown' then + G.GAME.current_round.free_rerolls = G.GAME.current_round.free_rerolls - 1 + calculate_reroll_cost(true) + end + if self.ability.name == 'Turtle Bean' then + G.hand:change_size(-self.ability.extra.h_size) + end + if self.ability.name == 'Oops! All 6s' then + for k, v in pairs(G.GAME.probabilities) do + G.GAME.probabilities[k] = v/2 + end + end + if self.ability.name == 'To the Moon' then + G.GAME.interest_amount = G.GAME.interest_amount - self.ability.extra + end + if self.ability.name == 'Astronomer' then + G.E_MANAGER:add_event(Event({func = function() + for k, v in pairs(G.I.CARD) do + if v.set_cost then v:set_cost() end + end + return true end })) + end + if self.ability.name == 'Troubadour' then + G.hand:change_size(-self.ability.extra.h_size) + G.GAME.round_resets.hands = G.GAME.round_resets.hands - self.ability.extra.h_plays + end + if self.ability.name == 'Stuntman' then + G.hand:change_size(self.ability.extra.h_size) + end + if G.jokers then + if from_debuff then + self.ability.joker_added_to_deck_but_debuffed = true + else + if self.edition and self.edition.card_limit then + if self.ability.consumeable then + G.consumeables.config.card_limit = G.consumeables.config.card_limit - self.edition.card_limit + elseif self.ability.set == 'Joker' then + G.jokers.config.card_limit = G.jokers.config.card_limit - self.edition.card_limit + end + end + end + end + if G.GAME.blind and G.GAME.blind.in_blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end + end +end + +function Card:generate_UIBox_unlock_table(hidden) + local loc_vars = {no_name = true, not_hidden = not hidden} + + return generate_card_ui(self.config.center, nil, loc_vars, 'Locked') +end + +function Card:generate_UIBox_ability_table() + local card_type, hide_desc = self.ability.set or "None", nil + local loc_vars = nil + local main_start, main_end = nil,nil + if self.ability.name == 'cry-Machine Code' then + --"Create a random // glitched consumable" + main_start = { + randomchar(codechars6), + randomchar(codechars6), + randomchar(codechars6), + randomchar(codechars6), + randomchar(codechars6), + randomchar(codechars6), + } + end + local no_badge = nil + + if not self.bypass_lock and self.config.center.unlocked ~= false and + (self.ability.set == 'Joker' or self.ability.set == 'Edition' or self.ability.consumeable or self.ability.set == 'Voucher' or self.ability.set == 'Booster') and + not self.config.center.discovered and + ((self.area ~= G.jokers and self.area ~= G.consumeables and self.area) or not self.area) then + card_type = 'Undiscovered' + end + if self.config.center.unlocked == false and not self.bypass_lock then --For everyting that is locked + card_type = "Locked" + if self.area and self.area == G.shop_demo then loc_vars = {}; no_badge = true end + elseif card_type == 'Undiscovered' and not self.bypass_discovery_ui then -- Any Joker or tarot/planet/voucher that is not yet discovered + hide_desc = true + elseif self.debuff then + loc_vars = { debuffed = true, playing_card = not not self.base.colour, value = self.base.value, suit = self.base.suit, colour = self.base.colour } + elseif card_type == 'Default' or card_type == 'Enhanced' then + loc_vars = { playing_card = not not self.base.colour, value = self.base.value, suit = self.base.suit, colour = self.base.colour, + nominal_chips = self.base.nominal > 0 and self.base.nominal or nil, + bonus_chips = (self.ability.bonus + (self.ability.perma_bonus or 0)) > 0 and (self.ability.bonus + (self.ability.perma_bonus or 0)) or nil, + } + elseif self.ability.set == 'Joker' then -- all remaining jokers + if self.ability.name == 'Joker' then loc_vars = {self.ability.mult} + elseif self.ability.name == 'Jolly Joker' or self.ability.name == 'Zany Joker' or + self.ability.name == 'Mad Joker' or self.ability.name == 'Crazy Joker' or + self.ability.name == 'Droll Joker' then + loc_vars = {self.ability.t_mult, localize(self.ability.type, 'poker_hands')} + elseif self.ability.name == 'Sly Joker' or self.ability.name == 'Wily Joker' or + self.ability.name == 'Clever Joker' or self.ability.name == 'Devious Joker' or + self.ability.name == 'Crafty Joker' then + loc_vars = {self.ability.t_chips, localize(self.ability.type, 'poker_hands')} + elseif self.ability.name == 'Half Joker' then loc_vars = {self.ability.extra.mult, self.ability.extra.size} + elseif self.ability.name == 'Fortune Teller' then loc_vars = {self.ability.extra, (G.GAME.consumeable_usage_total and G.GAME.consumeable_usage_total.tarot or 0)} + elseif self.ability.name == 'Steel Joker' then loc_vars = {self.ability.extra, 1 + self.ability.extra*(self.ability.steel_tally or 0)} + elseif self.ability.name == 'Chaos the Clown' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Space Joker' then loc_vars = {''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra} + elseif self.ability.name == 'Stone Joker' then loc_vars = {self.ability.extra, self.ability.extra*(self.ability.stone_tally or 0)} + elseif self.ability.name == 'Drunkard' then loc_vars = {self.ability.d_size} + elseif self.ability.name == 'Green Joker' then loc_vars = {self.ability.extra.hand_add, self.ability.extra.discard_sub, self.ability.mult} + elseif self.ability.name == 'Credit Card' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Greedy Joker' or self.ability.name == 'Lusty Joker' or + self.ability.name == 'Wrathful Joker' or self.ability.name == 'Gluttonous Joker' then loc_vars = {self.ability.extra.s_mult, localize(self.ability.extra.suit, 'suits_singular')} + elseif self.ability.name == 'Blue Joker' then loc_vars = {self.ability.extra, self.ability.extra*((G.deck and G.deck.cards) and #G.deck.cards or 52)} + elseif self.ability.name == 'Sixth Sense' then loc_vars = {} + elseif self.ability.name == 'Mime' then + elseif self.ability.name == 'Hack' then loc_vars = {self.ability.extra+1} + elseif self.ability.name == 'Pareidolia' then + elseif self.ability.name == 'Faceless Joker' then loc_vars = {self.ability.extra.dollars, self.ability.extra.faces} + elseif self.ability.name == 'Oops! All 6s' then + elseif self.ability.name == 'Juggler' then loc_vars = {self.ability.h_size} + elseif self.ability.name == 'Golden Joker' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Joker Stencil' then loc_vars = {self.ability.x_mult} + elseif self.ability.name == 'Four Fingers' then + elseif self.ability.name == 'Ceremonial Dagger' then loc_vars = {self.ability.mult} + elseif self.ability.name == 'Banner' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'cry-Error' then + if G.GAME and G.GAME.pseudorandom and G.STAGE == G.STAGES.RUN then + cry_error_msgs[#cry_error_msgs].string = "%%" .. predict_card_for_shop() + else + cry_error_msgs[#cry_error_msgs].string = "%%J6" + end + main_start = { + {n=G.UIT.O, config={object = DynaText({string = cry_error_operators, colours = {G.C.DARK_EDITION,},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.30, scale = 0.32, min_cycle_time = 0})}}, + {n=G.UIT.O, config={object = DynaText({string = cry_error_numbers, colours = {G.C.DARK_EDITION,},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.33, scale = 0.32, min_cycle_time = 0})}}, + {n=G.UIT.O, config={object = DynaText({string = cry_error_msgs, + colours = {G.C.UI.TEXT_DARK},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.4011, scale = 0.32, min_cycle_time = 0})}}, + } + elseif self.ability.name == 'Misprint' then + local r_mults = {} + for i = self.ability.extra.min, self.ability.extra.max do + r_mults[#r_mults+1] = tostring(i) + end + local loc_mult = ' '..(localize('k_mult'))..' ' + main_start = { + {n=G.UIT.T, config={text = ' +',colour = G.C.MULT, scale = 0.32}}, + {n=G.UIT.O, config={object = DynaText({string = r_mults, colours = {G.C.RED},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.5, scale = 0.32, min_cycle_time = 0})}}, + {n=G.UIT.O, config={object = DynaText({string = { + {string = 'rand()', colour = G.C.JOKER_GREY},{string = "#@"..(G.deck and G.deck.cards[1] and G.deck.cards[#G.deck.cards].base.id or 11)..(G.deck and G.deck.cards[1] and G.deck.cards[#G.deck.cards].base.suit:sub(1,1) or 'D'), colour = G.C.RED}, + loc_mult, loc_mult, loc_mult, loc_mult, loc_mult, loc_mult, loc_mult, loc_mult, loc_mult, loc_mult, loc_mult, loc_mult, loc_mult}, + colours = {G.C.UI.TEXT_DARK},pop_in_rate = 9999999, silent = true, random_element = true, pop_delay = 0.2011, scale = 0.32, min_cycle_time = 0})}}, + } + elseif self.ability.name == 'Mystic Summit' then loc_vars = {self.ability.extra.mult, self.ability.extra.d_remaining} + elseif self.ability.name == 'Marble Joker' then + elseif self.ability.name == 'Loyalty Card' then loc_vars = {self.ability.extra.Xmult, self.ability.extra.every + 1, localize{type = 'variable', key = (self.ability.loyalty_remaining == 0 and 'loyalty_active' or 'loyalty_inactive'), vars = {self.ability.loyalty_remaining}}} + elseif self.ability.name == '8 Ball' then loc_vars = {''..(G.GAME and G.GAME.probabilities.normal or 1),self.ability.extra} + elseif self.ability.name == 'Dusk' then loc_vars = {self.ability.extra+1} + elseif self.ability.name == 'Raised Fist' then + elseif self.ability.name == 'Fibonacci' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Scary Face' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Abstract Joker' then loc_vars = {self.ability.extra, (G.jokers and G.jokers.cards and #G.jokers.cards or 0)*self.ability.extra} + elseif self.ability.name == 'Delayed Gratification' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Gros Michel' then loc_vars = {self.ability.extra.mult, ''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra.odds} + elseif self.ability.name == 'Even Steven' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Odd Todd' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Scholar' then loc_vars = {self.ability.extra.mult, self.ability.extra.chips} + elseif self.ability.name == 'Business Card' then loc_vars = {''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra} + elseif self.ability.name == 'Supernova' then + elseif self.ability.name == 'Spare Trousers' then loc_vars = {self.ability.extra, localize('Two Pair', 'poker_hands'), self.ability.mult} + elseif self.ability.name == 'Superposition' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Ride the Bus' then loc_vars = {self.ability.extra, self.ability.mult} + elseif self.ability.name == 'Egg' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Burglar' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Blackboard' then loc_vars = {self.ability.extra, localize('Spades', 'suits_plural'), localize('Clubs', 'suits_plural')} + elseif self.ability.name == 'Runner' then loc_vars = {self.ability.extra.chips, self.ability.extra.chip_mod} + elseif self.ability.name == 'Ice Cream' then loc_vars = {self.ability.extra.chips, self.ability.extra.chip_mod} + elseif self.ability.name == 'DNA' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Splash' then + elseif self.ability.name == 'Constellation' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Hiker' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'To Do List' then loc_vars = {self.ability.extra.dollars, localize(self.ability.to_do_poker_hand, 'poker_hands')} + elseif self.ability.name == 'Smeared Joker' then + elseif self.ability.name == 'Blueprint' or self.ability.name == 'cry-oldblueprint' then + self.ability.blueprint_compat_ui = self.ability.blueprint_compat_ui or ''; self.ability.blueprint_compat_check = nil + main_end = (self.area and self.area == G.jokers) and { + {n=G.UIT.C, config={align = "bm", minh = 0.4}, nodes={ + {n=G.UIT.C, config={ref_table = self, align = "m", colour = G.C.JOKER_GREY, r = 0.05, padding = 0.06, func = 'blueprint_compat'}, nodes={ + {n=G.UIT.T, config={ref_table = self.ability, ref_value = 'blueprint_compat_ui',colour = G.C.UI.TEXT_LIGHT, scale = 0.32*0.8}}, + }} + }} + } or nil + elseif self.ability.name == 'Cartomancer' then + elseif self.ability.name == 'Astronomer' then loc_vars = {self.ability.extra} + + elseif self.ability.name == 'Golden Ticket' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Mr. Bones' then + elseif self.ability.name == 'Acrobat' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Sock and Buskin' then loc_vars = {self.ability.extra+1} + elseif self.ability.name == 'Swashbuckler' then loc_vars = {self.ability.mult} + elseif self.ability.name == 'Troubadour' then loc_vars = {self.ability.extra.h_size, -self.ability.extra.h_plays} + elseif self.ability.name == 'Certificate' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Throwback' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Hanging Chad' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Rough Gem' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Bloodstone' then loc_vars = {''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra.odds, self.ability.extra.Xmult} + elseif self.ability.name == 'Arrowhead' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Onyx Agate' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Glass Joker' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Showman' then + elseif self.ability.name == 'Flower Pot' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Wee Joker' then loc_vars = {self.ability.extra.chips, self.ability.extra.chip_mod} + elseif self.ability.name == 'Merry Andy' then loc_vars = {self.ability.d_size, self.ability.h_size} + elseif self.ability.name == 'The Idol' then loc_vars = {self.ability.extra, localize(G.GAME.current_round.idol_card.rank, 'ranks'), localize(G.GAME.current_round.idol_card.suit, 'suits_plural'), colours = {G.C.SUITS[G.GAME.current_round.idol_card.suit]}} + elseif self.ability.name == 'Seeing Double' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Matador' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Hit the Road' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'The Duo' or self.ability.name == 'The Trio' + or self.ability.name == 'The Family' or self.ability.name == 'The Order' or self.ability.name == 'The Tribe' then loc_vars = {self.ability.x_mult, localize(self.ability.type, 'poker_hands')} + + elseif self.ability.name == 'Cavendish' then loc_vars = {self.ability.extra.Xmult, ''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra.odds} + elseif self.ability.name == 'Card Sharp' then loc_vars = {self.ability.extra.Xmult} + elseif self.ability.name == 'Red Card' then loc_vars = {self.ability.extra, self.ability.mult} + elseif self.ability.name == 'Madness' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Square Joker' then loc_vars = {self.ability.extra.chips, self.ability.extra.chip_mod} + elseif self.ability.name == 'Seance' then loc_vars = {localize(self.ability.extra.poker_hand, 'poker_hands')} + elseif self.ability.name == 'Riff-raff' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Vampire' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Shortcut' then + elseif self.ability.name == 'Hologram' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Vagabond' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Baron' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Cloud 9' then loc_vars = {self.ability.extra, self.ability.extra*(self.ability.nine_tally or 0)} + elseif self.ability.name == 'Rocket' then loc_vars = {self.ability.extra.dollars, self.ability.extra.increase} + elseif self.ability.name == 'Obelisk' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Midas Mask' then + elseif self.ability.name == 'Luchador' then + local has_message= (G.GAME and self.area and (self.area == G.jokers)) + if has_message then + local disableable = G.GAME.blind and ((not G.GAME.blind.disabled) and (G.GAME.blind:get_type() == 'Boss')) + main_end = { + {n=G.UIT.C, config={align = "bm", minh = 0.4}, nodes={ + {n=G.UIT.C, config={ref_table = self, align = "m", colour = disableable and G.C.GREEN or G.C.RED, r = 0.05, padding = 0.06}, nodes={ + {n=G.UIT.T, config={text = ' '..localize(disableable and 'k_active' or 'ph_no_boss_active')..' ',colour = G.C.UI.TEXT_LIGHT, scale = 0.32*0.9}}, + }} + }} + } + end + elseif self.ability.name == 'Photograph' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Gift Card' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Turtle Bean' then loc_vars = {self.ability.extra.h_size, self.ability.extra.h_mod} + elseif self.ability.name == 'Erosion' then loc_vars = {self.ability.extra, math.max(0,self.ability.extra*(G.playing_cards and (G.GAME.starting_deck_size - #G.playing_cards) or 0)), G.GAME.starting_deck_size} + elseif self.ability.name == 'Reserved Parking' then loc_vars = {self.ability.extra.dollars, ''..(G.GAME and G.GAME.probabilities.normal or 1), self.ability.extra.odds} + elseif self.ability.name == 'Mail-In Rebate' then loc_vars = {self.ability.extra, localize(G.GAME.current_round.mail_card.rank, 'ranks')} + elseif self.ability.name == 'To the Moon' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Hallucination' then loc_vars = {G.GAME.probabilities.normal, self.ability.extra} + elseif self.ability.name == 'Lucky Cat' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Baseball Card' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Bull' then loc_vars = {self.ability.extra, self.ability.extra*math.max(0,G.GAME.dollars) or 0} + elseif self.ability.name == 'Diet Cola' then loc_vars = {localize{type = 'name_text', set = 'Tag', key = 'tag_double', nodes = {}}} + elseif self.ability.name == 'Trading Card' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Flash Card' then loc_vars = {self.ability.extra, self.ability.mult} + elseif self.ability.name == 'Popcorn' then loc_vars = {self.ability.mult, self.ability.extra} + elseif self.ability.name == 'Ramen' then loc_vars = {self.ability.x_mult, self.ability.extra} + elseif self.ability.name == 'Ancient Joker' then loc_vars = {self.ability.extra, localize(G.GAME.current_round.ancient_card.suit, 'suits_singular'), colours = {G.C.SUITS[G.GAME.current_round.ancient_card.suit]}} + elseif self.ability.name == 'Walkie Talkie' then loc_vars = {self.ability.extra.chips, self.ability.extra.mult} + elseif self.ability.name == 'Seltzer' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Castle' then loc_vars = {self.ability.extra.chip_mod, localize(G.GAME.current_round.castle_card.suit, 'suits_singular'), self.ability.extra.chips, colours = {G.C.SUITS[G.GAME.current_round.castle_card.suit]}} + elseif self.ability.name == 'Smiley Face' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Campfire' then loc_vars = {self.ability.extra, self.ability.x_mult} + elseif self.ability.name == 'Stuntman' then loc_vars = {self.ability.extra.chip_mod, self.ability.extra.h_size} + elseif self.ability.name == 'Invisible Joker' then loc_vars = {self.ability.extra, self.ability.invis_rounds} + elseif self.ability.name == 'Brainstorm' or self.config.center.key == 'j_cry_gemino' then + self.ability.blueprint_compat_ui = self.ability.blueprint_compat_ui or ''; self.ability.blueprint_compat_check = nil + main_end = (self.area and self.area == G.jokers) and { + {n=G.UIT.C, config={align = "bm", minh = 0.4}, nodes={ + {n=G.UIT.C, config={ref_table = self, align = "m", colour = G.C.JOKER_GREY, r = 0.05, padding = 0.06, func = 'blueprint_compat'}, nodes={ + {n=G.UIT.T, config={ref_table = self.ability, ref_value = 'blueprint_compat_ui',colour = G.C.UI.TEXT_LIGHT, scale = 0.32*0.8}}, + }} + }} + } or nil + elseif self.ability.name == 'Satellite' then + local planets_used = 0 + for k, v in pairs(G.GAME.consumeable_usage) do if v.set == 'Planet' then planets_used = planets_used + 1 end end + loc_vars = {self.ability.extra, planets_used*self.ability.extra} + elseif self.ability.name == 'Shoot the Moon' then loc_vars = {self.ability.extra} + elseif self.ability.name == "Driver's License" then loc_vars = {self.ability.extra, self.ability.driver_tally or '0'} + elseif self.ability.name == 'Burnt Joker' then + elseif self.ability.name == 'Bootstraps' then loc_vars = {self.ability.extra.mult, self.ability.extra.dollars, self.ability.extra.mult*math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0))/self.ability.extra.dollars)} + elseif self.ability.name == 'Caino' then loc_vars = {self.ability.extra, self.ability.caino_xmult} + elseif self.ability.name == 'Triboulet' then loc_vars = {self.ability.extra} + elseif self.ability.name == 'Yorick' then loc_vars = {self.ability.extra.xmult, self.ability.extra.discards, self.ability.yorick_discards, self.ability.x_mult} + elseif self.ability.name == 'Chicot' then + elseif self.ability.name == 'Perkeo' then loc_vars = {self.ability.extra} + end + end + local badges = {} + if (card_type ~= 'Locked' and card_type ~= 'Undiscovered' and card_type ~= 'Default') or self.debuff then + badges.card_type = card_type + end + if self.ability.set == 'Joker' and self.bypass_discovery_ui and (not no_badge) then + badges.force_rarity = true + end + if self.edition then + if self.edition.type == 'negative' and self.ability.consumeable then + badges[#badges + 1] = 'negative_consumable' + elseif self.edition.type == 'negative' and (self.ability.set == 'Enhanced' or self.ability.set == 'Default') then + badges[#badges + 1] = 'negative_playing_card' + else + badges[#badges + 1] = (self.edition.type == 'holo' and 'holographic' or self.edition.type) + end + end + if self.seal then badges[#badges + 1] = string.lower(self.seal)..'_seal' end + if self.ability.eternal then badges[#badges + 1] = 'eternal' end + if self.ability.perishable and not layer then + loc_vars = loc_vars or {}; loc_vars.perish_tally=self.ability.perish_tally + badges[#badges + 1] = 'perishable' + end + if self.ability.rental then badges[#badges + 1] = 'rental' end + if self.pinned then + if self.ability.set == 'Booster' then + badges[#badges + 1] = 'cry_pinned_booster' + elseif self.ability.set == 'Voucher' then + badges[#badges + 1] = 'cry_pinned_voucher' + elseif self.ability.consumeable then + badges[#badges + 1] = 'cry_pinned_consumeable' + else + badges[#badges + 1] = 'pinned_left' + end +end + + for k, v in ipairs(SMODS.Sticker.obj_buffer) do + if self.ability[v] and not SMODS.Stickers[v].hide_badge then + badges[#badges+1] = v + end + end + if self.sticker or ((self.sticker_run and self.sticker_run~='NONE') and G.SETTINGS.run_stake_stickers) then loc_vars = loc_vars or {}; loc_vars.sticker=(self.sticker or self.sticker_run) end + + return generate_card_ui(self.config.center, nil, loc_vars, card_type, badges, hide_desc, main_start, main_end, self) +end + +function Card:get_nominal(mod) + local mult = 1 + local rank_mult = 1 + if mod == 'suit' then mult = 10000 end + if self.ability.effect == 'Stone Card' or (self.config.center.no_suit and self.config.center.no_rank) then + mult = -10000 + elseif self.config.center.no_suit then + mult = 0 + elseif self.config.center.no_rank then + rank_mult = 0 + end + return 10*self.base.nominal*rank_mult + self.base.suit_nominal*mult + (self.base.suit_nominal_original or 0)*0.0001*mult + 10*self.base.face_nominal*rank_mult + 0.000001*self.unique_val +end + +function Card:get_id() + if (self.ability.effect == 'Stone Card' or self.config.center.no_rank) and not self.vampired then + return -math.random(100, 1000000) + end + return self.base.id +end + +function Card:is_face(from_boss) + if self.debuff and not from_boss then return end + local id = self:get_id() + local rank = SMODS.Ranks[self.base.value] + if not id then return end + if (id > 0 and rank and rank.face) or next(find_joker("Pareidolia")) then + return true + end +end + +function Card:get_original_rank() + return self.base.original_value +end + +function Card:get_chip_bonus() + if self.debuff then return 0 end + if self.ability.effect == 'Stone Card' or self.config.center.replace_base_card then + return self.ability.bonus + (self.ability.perma_bonus or 0) + end + return self.base.nominal + self.ability.bonus + (self.ability.perma_bonus or 0) +end + +function Card:get_chip_mult() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if self.ability.effect == "Lucky Card" then + if pseudorandom('lucky_mult') < G.GAME.probabilities.normal/5 then + self.lucky_trigger = true + return self.ability.mult + else + return 0 + end + else + return self.ability.mult + end +end + +function Card:get_chip_x_mult(context) + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if self.ability.x_mult <= 1 then return 0 end + return self.ability.x_mult +end + +function Card:get_chip_h_mult() + if self.debuff then return 0 end + return self.ability.h_mult +end + +function Card:get_chip_h_x_mult() + if self.debuff then return 0 end + return self.ability.h_x_mult +end + +function Card:get_edition() + if self.debuff then return end + if self.edition then + local ret = {card = self} + if self.edition.p_dollars then + ret.p_dollars_mod = self.edition.p_dollars + end + if self.edition.x_mult then + ret.x_mult_mod = self.edition.x_mult + end + if self.edition.mult then + ret.mult_mod = self.edition.mult + end + if self.edition.chips then + ret.chip_mod = self.edition.chips + end + return ret + end +end + +function Card:get_end_of_round_effect(context) + if self.debuff then return {} end + local ret = {} + if self.ability.h_dollars > 0 then + ret.h_dollars = self.ability.h_dollars + ret.card = self + end + if self.seal == 'Blue' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + local card_type = 'Planet' + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + if G.GAME.last_hand_played then + local _planet = 0 + for k, v in pairs(G.P_CENTER_POOLS.Planet) do + if v.config.hand_type == G.GAME.last_hand_played then + _planet = v.key + end + end + local card = create_card(card_type,G.consumeables, nil, nil, nil, nil, _planet, 'blusl') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + end + return true + end)})) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('k_plus_planet'), colour = G.C.SECONDARY_SET.Planet}) + ret.effect = true + end + return ret +end + + +function Card:get_p_dollars() + if self.debuff then return 0 end + local ret = 0 + local obj = G.P_SEALS[self.seal] or {} + if obj.get_p_dollars and type(obj.get_p_dollars) == 'function' then + ret = ret + obj:get_p_dollars(self) + elseif self.seal == 'Gold' then + ret = ret + 3 + end + if self.ability.p_dollars > 0 then + if self.ability.effect == "Lucky Card" then + if pseudorandom('lucky_money') < G.GAME.probabilities.normal/15 then + self.lucky_trigger = true + ret = ret + self.ability.p_dollars + end + else + ret = ret + self.ability.p_dollars + end + end + if ret > 0 then + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + ret + if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end + end + return ret +end + +function Card:use_consumeable(area, copier) + stop_use() + if not copier then set_consumeable_usage(self) end + if self.debuff then return nil end + local used_tarot = copier or self + if self.ability.rental then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + blocking = false, + blockable = false, + func = (function() + ease_dollars(-G.GAME.cry_consumeable_rental_rate) + return true + end)})) + end + local gone = false + if self.ability.banana then + if not self.ability.extinct then + if (pseudorandom('oops_it_banana') < G.GAME.probabilities.normal/G.GAME.cry_consumeable_banana_odds) then + local gone = true + self.ability.extinct = true + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + if self.area then self.area:remove_card(self) end + self:remove() + self = nil + return true; end})) + return true + end + })) + card_eval_status_text(self, 'jokers', nil, nil, nil, {message = localize('k_extinct_ex'), delay = 0.1}) + return true + end + end + end + if gone == false then + + if self.ability.consumeable.max_highlighted then + update_hand_text({immediate = true, nopulse = true, delay = 0}, {mult = 0, chips = 0, level = '', handname = ''}) + end + + local obj = self.config.center + if obj.use and type(obj.use) == 'function' then + obj:use(self, area, copier) + return + end if self.ability.consumeable.mod_conv or self.ability.consumeable.suit_conv then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true end })) + for i=1, #G.hand.highlighted do + local percent = 1.15 - (i-0.999)/(#G.hand.highlighted-0.998)*0.3 + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.15,func = function() G.hand.highlighted[i]:flip();play_sound('card1', percent);G.hand.highlighted[i]:juice_up(0.3, 0.3);return true end })) + end + delay(0.2) + if self.ability.name == 'Death' then + local rightmost = G.hand.highlighted[1] + for i=1, #G.hand.highlighted do if G.hand.highlighted[i].T.x > rightmost.T.x then rightmost = G.hand.highlighted[i] end end + for i=1, #G.hand.highlighted do + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1,func = function() + if G.hand.highlighted[i] ~= rightmost and not G.hand.highlighted[i].ability.eternal then + copy_card(rightmost, G.hand.highlighted[i]) + end + return true end })) + end + elseif self.ability.name == 'Strength' then + for i=1, #G.hand.highlighted do + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1,func = function() + local card = G.hand.highlighted[i] + local suit_prefix = string.sub(card.base.suit, 1, 1)..'_' + local rank_suffix = card.base.id == 14 and 2 or math.min(card.base.id+1, 14) + if rank_suffix < 10 then rank_suffix = tostring(rank_suffix) + elseif rank_suffix == 10 then rank_suffix = 'T' + elseif rank_suffix == 11 then rank_suffix = 'J' + elseif rank_suffix == 12 then rank_suffix = 'Q' + elseif rank_suffix == 13 then rank_suffix = 'K' + elseif rank_suffix == 14 then rank_suffix = 'A' + end + card:set_base(G.P_CARDS[suit_prefix..rank_suffix]) + return true end })) + end + elseif self.ability.consumeable.suit_conv then + for i=1, #G.hand.highlighted do + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1,func = function() G.hand.highlighted[i]:change_suit(self.ability.consumeable.suit_conv);return true end })) + end + else + for i=1, #G.hand.highlighted do + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1,func = function() G.hand.highlighted[i]:set_ability(G.P_CENTERS[self.ability.consumeable.mod_conv]);return true end })) + end + end + for i=1, #G.hand.highlighted do + local percent = 0.85 + (i-0.999)/(#G.hand.highlighted-0.998)*0.3 + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.15,func = function() G.hand.highlighted[i]:flip();play_sound('tarot2', percent, 0.6);G.hand.highlighted[i]:juice_up(0.3, 0.3);return true end })) + end + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end })) + delay(0.5) + end + if self.ability.name == 'Black Hole' then + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize('k_all_hands'),chips = '...', mult = '...', level=''}) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function() + play_sound('tarot1') + self:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = true + return true end })) + update_hand_text({delay = 0}, {mult = '+', StatusText = true}) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function() + play_sound('tarot1') + self:juice_up(0.8, 0.5) + return true end })) + update_hand_text({delay = 0}, {chips = '+', StatusText = true}) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function() + play_sound('tarot1') + self:juice_up(0.8, 0.5) + G.TAROT_INTERRUPT_PULSE = nil + return true end })) + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.9, delay = 0}, {level='+1'}) + delay(1.3) + for k, v in pairs(G.GAME.hands) do + level_up_hand(self, k, true) + end + update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''}) + end + if self.ability.name == 'Talisman' or self.ability.name == 'Deja Vu' or self.ability.name == 'Trance' or self.ability.name == 'Medium' then + for q = 1, #G.hand.highlighted do + local conv_card = G.hand.highlighted[q] + G.E_MANAGER:add_event(Event({func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true end })) + + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1,func = function() + conv_card:set_seal(self.ability.extra, nil, true) + return true end })) + end + delay(0.5) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end })) + end--[[ + G.E_MANAGER:add_event(Event({func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true end })) + + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1,func = function() + conv_card:set_seal(self.ability.extra, nil, true) + return true end })) + + delay(0.5) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2,func = function() G.hand:unhighlight_all(); return true end })) + end + --]] + if self.ability.name == 'Aura' then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + local over = false + local edition = poll_edition('aura', nil, true, true) + local aura_card = G.hand.highlighted[1] + aura_card:set_edition(edition, true) + used_tarot:juice_up(0.3, 0.5) + return true end })) + end + if self.ability.name == 'Cryptid' then + G.E_MANAGER:add_event(Event({ + func = function() + local _first_dissolve = nil + local new_cards = {} + for i = 1, self.ability.extra do + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + for q = 1, #G.hand.highlighted do + local _card = copy_card(G.hand.highlighted[q], nil, nil, G.playing_card) + _card:add_to_deck() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + table.insert(G.playing_cards, _card) + G.hand:emplace(_card) + _card:start_materialize(nil, _first_dissolve) + _first_dissolve = true + if _card.config.center.key == "c_cryptid" then check_for_unlock({type = "cryptid_the_cryptid"}) end + new_cards[#new_cards+1] = _card + end + end + playing_card_joker_effects(new_cards) + return true + end + })) + end + if self.ability.name == 'Sigil' or self.ability.name == 'Ouija' then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true end })) + for i=1, #G.hand.cards do + local percent = 1.15 - (i-0.999)/(#G.hand.cards-0.998)*0.3 + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.15,func = function() G.hand.cards[i]:flip();play_sound('card1', percent);G.hand.cards[i]:juice_up(0.3, 0.3);return true end })) + end + delay(0.2) + if self.ability.name == 'Sigil' then + local _suit = pseudorandom_element({'S','H','D','C'}, pseudoseed('sigil')) + for i=1, #G.hand.cards do + G.E_MANAGER:add_event(Event({func = function() + local card = G.hand.cards[i] + local suit_prefix = _suit..'_' + local rank_suffix = card.base.id < 10 and tostring(card.base.id) or + card.base.id == 10 and 'T' or card.base.id == 11 and 'J' or + card.base.id == 12 and 'Q' or card.base.id == 13 and 'K' or + card.base.id == 14 and 'A' + card:set_base(G.P_CARDS[suit_prefix..rank_suffix]) + return true end })) + end + end + if self.ability.name == 'Ouija' then + local _rank = pseudorandom_element({'2','3','4','5','6','7','8','9','T','J','Q','K','A'}, pseudoseed('ouija')) + for i=1, #G.hand.cards do + G.E_MANAGER:add_event(Event({func = function() + local card = G.hand.cards[i] + local suit_prefix = string.sub(card.base.suit, 1, 1)..'_' + local rank_suffix =_rank + card:set_base(G.P_CARDS[suit_prefix..rank_suffix]) + return true end })) + end + G.hand:change_size(-1) + end + for i=1, #G.hand.cards do + local percent = 0.85 + (i-0.999)/(#G.hand.cards-0.998)*0.3 + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.15,func = function() G.hand.cards[i]:flip();play_sound('tarot2', percent, 0.6);G.hand.cards[i]:juice_up(0.3, 0.3);return true end })) + end + delay(0.5) + end + if self.ability.consumeable.hand_type then + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize(self.ability.consumeable.hand_type, 'poker_hands'),chips = G.GAME.hands[self.ability.consumeable.hand_type].chips, mult = G.GAME.hands[self.ability.consumeable.hand_type].mult, level=G.GAME.hands[self.ability.consumeable.hand_type].level}) + level_up_hand(used_tarot, self.ability.consumeable.hand_type) + update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''}) + end + if self.ability.consumeable.remove_card then + local destroyed_cards = {} + if self.ability.name == 'The Hanged Man' then + for i=#G.hand.highlighted, 1, -1 do + destroyed_cards[#destroyed_cards+1] = G.hand.highlighted[i] + end + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true end })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.2, + func = function() + for i=#G.hand.highlighted, 1, -1 do + local card = G.hand.highlighted[i] + if card.ability.name == 'Glass Card' then + card:shatter() + else + card:start_dissolve(nil, i == #G.hand.highlighted) + end + end + return true end })) + elseif self.ability.name == 'Familiar' or self.ability.name == 'Grim' or self.ability.name == 'Incantation' then + destroyed_cards[#destroyed_cards+1] = pseudorandom_element(G.hand.cards, pseudoseed('random_destroy')) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true end })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + for i=#destroyed_cards, 1, -1 do + local card = destroyed_cards[i] + if card.ability.name == 'Glass Card' then + card:shatter() + else + card:start_dissolve(nil, i ~= #destroyed_cards) + end + end + return true end })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.7, + func = function() + local cards = {} + for i=1, self.ability.extra do + cards[i] = true + local _suit, _rank = nil, nil + if self.ability.name == 'Familiar' then + _rank = pseudorandom_element({'J', 'Q', 'K'}, pseudoseed('familiar_create')) + _suit = pseudorandom_element({'S','H','D','C'}, pseudoseed('familiar_create')) + elseif self.ability.name == 'Grim' then + _rank = 'A' + _suit = pseudorandom_element({'S','H','D','C'}, pseudoseed('grim_create')) + elseif self.ability.name == 'Incantation' then + _rank = pseudorandom_element({'2', '3', '4', '5', '6', '7', '8', '9', 'T'}, pseudoseed('incantation_create')) + _suit = pseudorandom_element({'S','H','D','C'}, pseudoseed('incantation_create')) + end + _suit = _suit or 'S'; _rank = _rank or 'A' + local cen_pool = {} + for k, v in pairs(G.P_CENTER_POOLS["Enhanced"]) do + if v.key ~= 'm_stone' then + cen_pool[#cen_pool+1] = v + end + end + create_playing_card({front = G.P_CARDS[_suit..'_'.._rank], center = pseudorandom_element(cen_pool, pseudoseed('spe_card'))}, G.hand, nil, i ~= 1, {G.C.SECONDARY_SET.Spectral}) + end + playing_card_joker_effects(cards) + return true end })) + elseif self.ability.name == 'Immolate' then + local temp_hand = {} + for k, v in ipairs(G.hand.cards) do + if not v.ability.eternal then + temp_hand[#temp_hand+1] = v + end + end + table.sort(temp_hand, function (a, b) return not a.playing_card or not b.playing_card or a.playing_card < b.playing_card end) + pseudoshuffle(temp_hand, pseudoseed('immolate')) + + for i = 1, self.ability.extra.destroy do destroyed_cards[#destroyed_cards+1] = temp_hand[i] end + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('tarot1') + used_tarot:juice_up(0.3, 0.5) + return true end })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + for i=#destroyed_cards, 1, -1 do + local card = destroyed_cards[i] + if card.ability.name == 'Glass Card' then + card:shatter() + else + card:start_dissolve(nil, i == #destroyed_cards) + end + end + return true end })) + delay(0.5) + ease_dollars(self.ability.extra.dollars) + end + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({remove_playing_cards = true, removed = destroyed_cards}) + end + end + if self.ability.name == 'The Fool' then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + if G.consumeables.config.card_limit > #G.consumeables.cards then + play_sound('timpani') + local card = create_card('Tarot_Planet', G.consumeables, nil, nil, nil, nil, G.GAME.last_tarot_planet, 'fool') + card:add_to_deck() + G.consumeables:emplace(card) + used_tarot:juice_up(0.3, 0.5) + end + return true end })) + delay(0.6) + end + if self.ability.name == 'The Hermit' then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('timpani') + used_tarot:juice_up(0.3, 0.5) + ease_dollars(math.max(0,math.min(G.GAME.dollars, self.ability.extra)), true) + return true end })) + delay(0.6) + end + if self.ability.name == 'Temperance' then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('timpani') + used_tarot:juice_up(0.3, 0.5) + ease_dollars(self.ability.money, true) + return true end })) + delay(0.6) + end + if self.ability.name == 'The Emperor' or self.ability.name == 'The High Priestess' then + for i = 1, math.min((self.ability.consumeable.tarots or self.ability.consumeable.planets), G.consumeables.config.card_limit - #G.consumeables.cards) do + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + if G.consumeables.config.card_limit > #G.consumeables.cards then + play_sound('timpani') + local card = create_card((self.ability.name == 'The Emperor' and 'Tarot') or (self.ability.name == 'The High Priestess' and 'Planet'), G.consumeables, nil, nil, nil, nil, nil, (self.ability.name == 'The Emperor' and 'emp') or (self.ability.name == 'The High Priestess' and 'pri')) + card:add_to_deck() + G.consumeables:emplace(card) + used_tarot:juice_up(0.3, 0.5) + end + return true end })) + end + delay(0.6) + end + if self.ability.name == 'Judgement' or self.ability.name == 'The Soul' then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('timpani') + local card = create_card('Joker', G.jokers, self.ability.name == 'The Soul', nil, nil, nil, nil, self.ability.name == 'Judgement' and 'jud' or 'sou') + card:add_to_deck() + G.jokers:emplace(card) + if self.ability.name == 'The Soul' then check_for_unlock{type = 'spawn_legendary'} end + used_tarot:juice_up(0.3, 0.5) + return true end })) + delay(0.6) + end + if self.ability.name == 'Ankh' then + --Need to check for edgecases - if there are max Jokers and all are eternal OR there is a max of 1 joker this isn't possible already + --If there are max Jokers and exactly 1 is not eternal, that joker cannot be the one selected + --otherwise, the selected joker can be totally random and all other non-eternal jokers can be removed + local deletable_jokers = {} + for k, v in pairs(G.jokers.cards) do + if not v.ability.eternal then deletable_jokers[#deletable_jokers + 1] = v end + end + local chosen_joker = pseudorandom_element(G.jokers.cards, pseudoseed('ankh_choice')) + local _first_dissolve = nil + G.E_MANAGER:add_event(Event({trigger = 'before', delay = 0.75, func = function() + for k, v in pairs(deletable_jokers) do + if v ~= chosen_joker then + v:start_dissolve(nil, _first_dissolve) + _first_dissolve = true + end + end + return true end })) + G.E_MANAGER:add_event(Event({trigger = 'before', delay = 0.4, func = function() + local card = copy_card(chosen_joker, nil, nil, nil, chosen_joker.edition and chosen_joker.edition.negative) + card:start_materialize() + card:add_to_deck() + if card.edition and card.edition.negative then + card:set_edition(nil, true) + end + G.jokers:emplace(card) + return true end })) + end + if self.ability.name == 'Wraith' then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + play_sound('timpani') + local card = create_card('Joker', G.jokers, nil, 0.99, nil, nil, nil, 'wra') + card:add_to_deck() + G.jokers:emplace(card) + used_tarot:juice_up(0.3, 0.5) + if G.GAME.dollars ~= 0 then + ease_dollars(-G.GAME.dollars, true) + end + return true end })) + delay(0.6) + end + if self.ability.name == 'The Wheel of Fortune' or self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' then + local temp_pool = (self.ability.name == 'The Wheel of Fortune' and self.eligible_strength_jokers) or + ((self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex') and self.eligible_editionless_jokers) or {} + if self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' or pseudorandom('wheel_of_fortune') < G.GAME.probabilities.normal/self.ability.extra then +if self.ability.name == 'The Wheel of Fortune' then self.cry_wheel_success = true end + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + local over = false + local eligible_card = pseudorandom_element(temp_pool, pseudoseed( + (self.ability.name == 'The Wheel of Fortune' and 'wheel_of_fortune') or + (self.ability.name == 'Ectoplasm' and 'ectoplasm') or + (self.ability.name == 'Hex' and 'hex') + )) + local edition = nil + if self.ability.name == 'Ectoplasm' then + edition = {negative = true} + elseif self.ability.name == 'Hex' then + edition = {polychrome = true} + elseif self.ability.name == 'The Wheel of Fortune' then + edition = poll_edition('wheel_of_fortune', nil, true, true) + end + eligible_card:set_edition(edition, true) + if self.ability.name == 'The Wheel of Fortune' or self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' then check_for_unlock({type = 'have_edition'}) end + if self.ability.name == 'Hex' then + local _first_dissolve = nil + for k, v in pairs(G.jokers.cards) do + if v ~= eligible_card and (not v.ability.eternal) then v:start_dissolve(nil, _first_dissolve);_first_dissolve = true end + end + end + if self.ability.name == 'Ectoplasm' then + G.GAME.ecto_minus = G.GAME.ecto_minus or 1 + G.hand:change_size(-G.GAME.ecto_minus) + G.GAME.ecto_minus = G.GAME.ecto_minus + 1 + end + used_tarot:juice_up(0.3, 0.5) + return true end })) + else + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + attention_text({ + text = localize('k_nope_ex'), + scale = 1.3, + hold = 1.4, + major = used_tarot, + backdrop_colour = G.C.SECONDARY_SET.Tarot, + align = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and 'tm' or 'cm', + offset = {x = 0, y = (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and -0.2 or 0}, + silent = true + }) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + play_sound('tarot2', 1, 0.4) + used_tarot:juice_up(0.3, 0.5) + return true end })) + end + delay(0.6) + end +end + +end +function Card:can_use_consumeable(any_state, skip_check) + if not skip_check and ((G.play and #G.play.cards > 0) or + (G.CONTROLLER.locked) or + (G.GAME.STOP_USE and G.GAME.STOP_USE > 0)) + then return false end + if G.GAME.cry_pinned_consumeables > 0 and not self.pinned then + return false + end + if G.STATE ~= G.STATES.HAND_PLAYED and G.STATE ~= G.STATES.DRAW_TO_HAND and G.STATE ~= G.STATES.PLAY_TAROT or any_state then + + if self.ability.name == "The Hanged Man" then + for i = 1, #G.hand.highlighted do + if G.hand.highlighted[i].ability.eternal then return false end + end + end + if self.ability.name == "Death" then + local rightmost = G.hand.highlighted[1] + for i=1, #G.hand.highlighted-1 do if G.hand.highlighted[i].T.x > rightmost.T.x then rightmost = G.hand.highlighted[i] end end + for i=1, #G.hand.highlighted do if G.hand.highlighted[i].ability.eternal and rightmost ~= G.hand.highlighted[i] then return false end end + end + local obj = self.config.center + if obj.can_use and type(obj.can_use) == 'function' then + return obj:can_use(self) + end if self.ability.name == 'The Hermit' or self.ability.consumeable.hand_type or self.ability.name == 'Temperance' or self.ability.name == 'Black Hole' then + return true + end + if self.ability.name == 'The Wheel of Fortune' then + if next(self.eligible_strength_jokers) then return true end + end + if self.ability.name == 'Ankh' then + --if there is at least one joker + for k, v in pairs(G.jokers.cards) do + if v.ability.set == 'Joker' and G.jokers.config.card_limit > 1 then + return true + end + end + end + --]] + if self.ability.name == 'Aura' then + if self.area ~= G.hand then + return G.hand and (#G.hand.highlighted == 1) and G.hand.highlighted[1] and (not G.hand.highlighted[1].edition) + else + local idx = 1 + if G.hand.highlighted[1] == self then + local idx = 2 + end + return (#G.hand.highlighted == 2) and (not G.hand.highlighted[idx].edition) + end + end + if self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' then + if next(self.eligible_editionless_jokers) then return true end + end + if self.ability.name == 'The Emperor' or self.ability.name == 'The High Priestess' then + if #G.consumeables.cards < G.consumeables.config.card_limit or self.area == G.consumeables then return true end + end + if self.ability.name == 'The Fool' then + if (#G.consumeables.cards < G.consumeables.config.card_limit or self.area == G.consumeables) + and G.GAME.last_tarot_planet and G.GAME.last_tarot_planet ~= 'c_fool' then return true end + end + if self.ability.name == 'Judgement' or self.ability.name == 'The Soul' or self.ability.name == 'Wraith' then + if #G.jokers.cards < G.jokers.config.card_limit or self.area == G.jokers then + return true + else + return false + end + end + if G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED then + if self.ability.consumeable.max_highlighted then + if (self.ability.consumeable.mod_num - ((G.GAME.modifiers.cry_consumable_reduce and (self.ability.name ~= 'Death')) and (self.ability.consumeable.mod_num > 1) and 1 or 0)) >= #G.hand.highlighted + (self.area == G.hand and -1 or 0) and #G.hand.highlighted + (self.area == G.hand and -1 or 0) >= 1 then + return true + end + end + if (self.ability.name == 'Familiar' or self.ability.name == 'Grim' or + self.ability.name == 'Incantation' or self.ability.name == 'Immolate' or + self.ability.name == 'Sigil' or self.ability.name == 'Ouija') + and #G.hand.cards > 1 then + return true + end + end + end + return false +end + +function Card:check_use() + if self.ability.name == 'Ankh' then + if #G.jokers.cards >= G.jokers.config.card_limit then + alert_no_space(self, G.jokers) + return true + end + end +end + +function Card:sell_card() + G.CONTROLLER.locks.selling_card = true + stop_use() + local area = self.area + G.CONTROLLER:save_cardarea_focus(area == G.jokers and 'jokers' or 'consumeables') + + if self.children.use_button then self.children.use_button:remove(); self.children.use_button = nil end + if self.children.sell_button then self.children.sell_button:remove(); self.children.sell_button = nil end + + if self.config.center.set == 'Joker' then + if G.GAME.jokers_sold then + local contained = false + for i = 1, #G.GAME.jokers_sold do + if self.config.center.key == G.GAME.jokers_sold[i] then contained = true end + end + if not contained then table.insert(G.GAME.jokers_sold, self.config.center.key) end + else + G.GAME.jokers_sold = {self.config.center.key} + end + end + self:calculate_joker{selling_self = true} + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2,func = function() + if not G.GAME.modifiers.cry_no_sell_value then play_sound('coin2') end + self:juice_up(0.3, 0.4) + return true + end})) + delay(0.2) + G.E_MANAGER:add_event(Event({trigger = 'immediate',func = function() + if not G.GAME.modifiers.cry_no_sell_value then ease_dollars(self.sell_cost) end + if G.GAME.modifiers.cry_no_sell_value then self:start_dissolve({G.C.RED}) else self:start_dissolve({G.C.GOLD}) end + delay(0.3) + + inc_career_stat('c_cards_sold', 1) + if self.ability.set == 'Joker' then + inc_career_stat('c_jokers_sold', 1) + end + if self.ability.set == 'Joker' and G.GAME.blind and G.GAME.blind.name == 'Verdant Leaf' then + G.E_MANAGER:add_event(Event({trigger = 'immediate',func = function() + G.GAME.blind:disable() + return true + end})) + end + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.3, blocking = false, + func = function() + G.E_MANAGER:add_event(Event({trigger = 'immediate', + func = function() + G.E_MANAGER:add_event(Event({trigger = 'immediate', + func = function() + G.CONTROLLER.locks.selling_card = nil + G.CONTROLLER:recall_cardarea_focus(area == G.jokers and 'jokers' or 'consumeables') + return true + end})) + return true + end})) + return true + end})) + return true + end})) +end + +function Card:can_sell_card(context) + if (G.play and #G.play.cards > 0) or + (G.CONTROLLER.locked) or + (G.GAME.STOP_USE and G.GAME.STOP_USE > 0) --or + --G.STATE == G.STATES.BLIND_SELECT + then return false end + if (G.SETTINGS.tutorial_complete or G.GAME.pseudorandom.seed ~= 'TUTORIAL' or G.GAME.round_resets.ante > 1) and + self.area and + self.area.config.type == 'joker' and + not self.ability.eternal then + return true + end + return false +end + +function Card:calculate_dollar_bonus() + if self.debuff then return end + if self.ability.set == "Joker" then + if self.ability.name == 'Golden Joker' then + return self.ability.extra + end + --asdf + local obj = self.config.center + if obj.calc_dollar_bonus and type(obj.calc_dollar_bonus) == 'function' then + return obj:calc_dollar_bonus(self) + end + if self.ability.name == 'Cloud 9' and self.ability.nine_tally and self.ability.nine_tally > 0 then + return self.ability.extra*(self.ability.nine_tally) + end + if self.ability.name == 'Rocket' then + return self.ability.extra.dollars + end + if self.ability.name == 'Satellite' then + local planets_used = 0 + for k, v in pairs(G.GAME.consumeable_usage) do + if v.set == 'Planet' then planets_used = planets_used + 1 end + end + if planets_used == 0 then return end + return self.ability.extra*planets_used + end + if self.ability.name == 'Delayed Gratification' and G.GAME.current_round.discards_used == 0 and G.GAME.current_round.discards_left > 0 then + return G.GAME.current_round.discards_left*self.ability.extra + end + end +end + +function Card:open() + if self.ability.set == "Booster" then + stop_use() + G.STATE_COMPLETE = false + self.opening = true + + if not self.config.center.discovered then + discover_card(self.config.center) + end + self.states.hover.can = false + + if self.ability.extra < 1 then self.ability.extra = 1 end + booster_obj = self.config.center + if booster_obj and SMODS.Centers[booster_obj.key] then + G.STATE = G.STATES.SMODS_BOOSTER_OPENED + SMODS.OPENED_BOOSTER = self + end if self.ability.name:find('Arcana') then + G.STATE = G.STATES.TAROT_PACK + G.GAME.pack_size = self.ability.extra + elseif self.ability.name:find('Celestial') then + G.STATE = G.STATES.PLANET_PACK + G.GAME.pack_size = self.ability.extra + elseif self.ability.name:find('Spectral') then + G.STATE = G.STATES.SPECTRAL_PACK + G.GAME.pack_size = self.ability.extra + elseif self.ability.name:find('Standard') then + G.STATE = G.STATES.STANDARD_PACK + G.GAME.pack_size = self.ability.extra + elseif self.ability.name:find('Buffoon') then + G.STATE = G.STATES.BUFFOON_PACK + G.GAME.pack_size = self.ability.extra + end + + G.GAME.pack_choices = self.config.center.config.choose or 1 + if G.GAME.modifiers.cry_misprint_min then + G.GAME.pack_size = self.ability.extra + if G.GAME.pack_size < 1 then G.GAME.pack_size = 1 end + self.ability.extra = G.GAME.pack_size + G.GAME.pack_choices = math.min(math.floor(G.GAME.pack_size), self.ability.choose) + --G.GAME.pack_choices = math.min(math.floor(G.GAME.pack_size),cry_format(G.GAME.pack_choices * cry_log_random(pseudoseed('cry_misprint_p'..G.GAME.round_resets.ante),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2f")) + end + if G.GAME.cry_oboe then + self.ability.extra = self.ability.extra + G.GAME.cry_oboe + G.GAME.pack_choices = G.GAME.pack_choices + G.GAME.cry_oboe + G.GAME.cry_oboe = nil + G.GAME.pack_size = self.ability.extra + end + if G.GAME.boostertag then + self.ability.extra = self.ability.extra * 2 + G.GAME.pack_choices = G.GAME.pack_choices * 2 + G.GAME.boostertag = nil + G.GAME.pack_size = self.ability.extra + end + + if self.cost > 0 then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function() + inc_career_stat('c_shop_dollars_spent', self.cost) + self:juice_up() + return true end })) + ease_dollars(-self.cost) + else + delay(0.2) + end + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + self:explode() + local pack_cards = {} + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 1.3*math.sqrt(G.SETTINGS.GAMESPEED), blockable = false, blocking = false, func = function() + local _size = self.ability.extra + + for i = 1, _size do + local card = nil + if self.ability.extra < 1 then self.ability.extra = 1 end + if booster_obj.create_card and type(booster_obj.create_card) == "function" then + local _card_to_spawn = booster_obj:create_card(self, i) + if type((_card_to_spawn or {}).is) == 'function' and _card_to_spawn:is(Card) then + card = _card_to_spawn + else + card = SMODS.create_card(_card_to_spawn) + end + elseif self.ability.name:find('Arcana') then + if G.GAME.used_vouchers.v_omen_globe and pseudorandom('omen_globe') > 0.8 then + card = create_card("Spectral", G.pack_cards, nil, nil, true, true, nil, 'ar2') + else + card = create_card("Tarot", G.pack_cards, nil, nil, true, true, nil, 'ar1') + end + elseif self.ability.name:find('Celestial') then + if G.GAME.used_vouchers.v_telescope and i == 1 then + local _planet, _hand, _tally = nil, nil, 0 + for k, v in ipairs(G.handlist) do + if G.GAME.hands[v].visible and G.GAME.hands[v].played > _tally then + _hand = v + _tally = G.GAME.hands[v].played + end + end + if _hand then + for k, v in pairs(G.P_CENTER_POOLS.Planet) do + if v.config.hand_type == _hand then + _planet = v.key + end + end + end + card = create_card("Planet", G.pack_cards, nil, nil, true, true, _planet, 'pl1') + else + if G.GAME.used_vouchers.v_cry_satellite_uplink and pseudorandom('cry_satellite_uplink') > 0.8 then + card = create_card("Code", G.pack_cards, nil, nil, true, true, nil, 'pl2') + else + card = create_card("Planet", G.pack_cards, nil, nil, true, true, nil, 'pl1') + end + end + elseif self.ability.name:find('Spectral') then + card = create_card("Spectral", G.pack_cards, nil, nil, true, true, nil, 'spe') + elseif self.ability.name:find('Standard') then + card = create_card((pseudorandom(pseudoseed('stdset'..G.GAME.round_resets.ante)) > 0.6) and "Enhanced" or "Base", G.pack_cards, nil, nil, nil, true, nil, 'sta') + local edition_rate = 2 + local edi = self.edition or {} +if edi.type and not (G.GAME.modifiers.cry_force_edition and G.GAME.modifiers.cry_force_edition ~= 'random') then + card:set_edition({[edi.type] = true}) +elseif not G.GAME.modifiers.cry_force_random_edition then + local edition = poll_edition('standard_edition'..G.GAME.round_resets.ante, edition_rate, true) + card:set_edition(edition) +end + + card:set_seal(SMODS.poll_seal({mod = 10})) + elseif self.ability.name:find('Buffoon') then + card = create_card("Joker", G.pack_cards, nil, nil, true, true, nil, 'buf') + + end + local edi = self.edition or {} + if edi.type and not self.ability.name:find('Standard') then + if card.ability.name ~= "cry-meteor" + and card.ability.name ~= "cry-exoplanet" + and card.ability.name ~= "cry-stardust" then + card:set_edition({[edi.type] = true}) + end + end + if self.ability.eternal then + card.ability.eternal = self.ability.eternal + end + if self.ability.perishable and not layer then + card.ability.perishable = self.ability.perishable + end + if self.ability.rental then + card.ability.rental = self.ability.rental + end + if self.pinned then + card.pinned = self.pinned + end + if self.ability.banana then + card.ability.banana = self.ability.banana + end + card.T.x = self.T.x + card.T.y = self.T.y + card:start_materialize({G.C.WHITE, G.C.WHITE}, nil, 1.5*G.SETTINGS.GAMESPEED) + pack_cards[i] = card + end + return true + end})) + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 1.3*math.sqrt(G.SETTINGS.GAMESPEED), blockable = false, blocking = false, func = function() + if G.pack_cards then + if G.pack_cards and G.pack_cards.VT.y < G.ROOM.T.h then + for k, v in ipairs(pack_cards) do + G.pack_cards:emplace(v) + end + return true + end + end + end})) + + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({open_booster = true, card = self}) + end + + if G.GAME.modifiers.inflation then + G.GAME.inflation = G.GAME.inflation + 1 + G.E_MANAGER:add_event(Event({func = function() + for k, v in pairs(G.I.CARD) do + if v.set_cost then v:set_cost() end + end + return true end })) + end + + return true end })) + end +end + +function Card:redeem() + if self.ability.set == "Voucher" then + stop_use() + if not self.config.center.discovered then + discover_card(self.config.center) + end + if self.shop_voucher then G.GAME.current_round.voucher = nil end + G.GAME.cry_voucher_centers[self.config.center_key].config.extra = self.ability.extra + if self.ability.extra_disp then G.GAME.cry_voucher_centers[self.config.center_key].config.extra_disp = self.ability.extra_disp end + + self.states.hover.can = false + G.GAME.cry_owned_vouchers[self.config.center_key] = true + G.GAME.used_vouchers[self.config.center_key] = true + local top_dynatext = nil + local bot_dynatext = nil + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.4, func = function() + top_dynatext = DynaText({string = localize{type = 'name_text', set = self.config.center.set, key = self.config.center.key}, colours = {G.C.WHITE}, rotate = 1,shadow = true, bump = true,float=true, scale = 0.9, pop_in = 0.6/G.SPEEDFACTOR, pop_in_rate = 1.5*G.SPEEDFACTOR}) + bot_dynatext = DynaText({string = localize('k_redeemed_ex'), colours = {G.C.WHITE}, rotate = 2,shadow = true, bump = true,float=true, scale = 0.9, pop_in = 1.4/G.SPEEDFACTOR, pop_in_rate = 1.5*G.SPEEDFACTOR, pitch_shift = 0.25}) + self:juice_up(0.3, 0.5) + play_sound('card1') + play_sound('coin1') + self.children.top_disp = UIBox{ + definition = {n=G.UIT.ROOT, config = {align = 'tm', r = 0.15, colour = G.C.CLEAR, padding = 0.15}, nodes={ + {n=G.UIT.O, config={object = top_dynatext}} + }}, + config = {align="tm", offset = {x=0,y=0},parent = self} + } + self.children.bot_disp = UIBox{ + definition = {n=G.UIT.ROOT, config = {align = 'tm', r = 0.15, colour = G.C.CLEAR, padding = 0.15}, nodes={ + {n=G.UIT.O, config={object = bot_dynatext}} + }}, + config = {align="bm", offset = {x=0,y=0},parent = self} + } + return true end })) + if self.cost ~= 0 then + ease_dollars(-self.cost) + inc_career_stat('c_shop_dollars_spent', self.cost) + end + inc_career_stat('c_vouchers_bought', 1) + set_voucher_usage(self) + check_for_unlock({type = 'run_redeem'}) + G.GAME.current_round.voucher = nil + + G.GAME.current_round.cry_voucher_edition = nil + G.GAME.current_round.cry_voucher_stickers = {eternal = false, perishable = false, rental = false, pinned = false, banana = false} + self:apply_to_run() + + delay(0.6) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({buying_card = true, card = self}) + end + if G.GAME.modifiers.inflation then + G.GAME.inflation = G.GAME.inflation + 1 + G.E_MANAGER:add_event(Event({func = function() + for k, v in pairs(G.I.CARD) do + if v.set_cost then v:set_cost() end + end + return true end })) + end + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 2.6, func = function() + top_dynatext:pop_out(4) + bot_dynatext:pop_out(4) + return true end })) + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.5, func = function() + self.children.top_disp:remove() + self.children.top_disp = nil + self.children.bot_disp:remove() + self.children.bot_disp = nil + return true end })) + end +end + +function Card:apply_to_run(center) + local center_table = { + name = center and center.name or self and self.ability.name, + extra = self and self.config and self.config.center_key and G.GAME and G.GAME.cry_voucher_centers and G.GAME.cry_voucher_centers[self.config.center_key] and G.GAME.cry_voucher_centers[self.config.center_key].config.extra + } + if not G.GAME.voucher_edition_index then G.GAME.voucher_edition_index = {} end + if self and self.edition then + G.GAME.voucher_edition_index[center_table.name] = self.edition.type + end + if not G.GAME.voucher_sticker_index then G.GAME.voucher_sticker_index = {eternal = {}, perishable = {}, rental = {}, pinned = {}, banana = {}} end + if self and self.ability and self.ability.eternal and self.ability.eternal == true then + G.GAME.voucher_sticker_index.eternal[center_table.name] = true + end + if self and self.ability and self.ability.perishable and self.ability.perishable == true then + G.GAME.voucher_sticker_index.perishable[center_table.name] = G.GAME.cry_voucher_perishable_rounds + end + if self and self.ability and self.ability.rental and self.ability.rental == true then + G.GAME.voucher_sticker_index.rental[center_table.name] = true + end + if self and self.pinned and self.pinned == true then + G.GAME.voucher_sticker_index.pinned[center_table.name] = true + end + if self and self.ability and self.ability.banana and self.ability.banana == true then + G.GAME.voucher_sticker_index.banana[center_table.name] = true + end + if not center_table.extra then center_table.extra = center and center.config.extra end -- catch + if self and self.ability and self.ability.extra_disp then + local up = false + if center_table.name == 'Tarot Tycoon' or center_table.name == 'Planet Tycoon' then + up = true + end + local og_extra = 9.6/4 + local og_disp = 2 + if up == true then + og_extra = 32/4 + og_disp = 4 + end + local misprint_diff = self.ability.extra_disp / og_disp + G.GAME.cry_voucher_centers[self.config.center_key].config.extra = og_extra*misprint_diff + center_table.extra = og_extra*misprint_diff + end + local obj = center or self.config.center + if obj.redeem and type(obj.redeem) == 'function' then + obj:redeem(self) + return + end if center_table.name == 'Overstock' or center_table.name == 'Overstock Plus' then + G.E_MANAGER:add_event(Event({func = function() + change_shop_size(center_table.extra) + return true end })) + end + if center_table.name == 'Tarot Merchant' or center_table.name == 'Tarot Tycoon' then + G.E_MANAGER:add_event(Event({func = function() + G.GAME.tarot_rate = 4*center_table.extra + return true end })) + end + if center_table.name == 'Planet Merchant' or center_table.name == 'Planet Tycoon' then + G.E_MANAGER:add_event(Event({func = function() + G.GAME.planet_rate = 4*center_table.extra + return true end })) + end + if center_table.name == 'Hone' or center_table.name == 'Glow Up' then + G.E_MANAGER:add_event(Event({func = function() + G.GAME.edition_rate = center_table.extra + return true end })) + end + if center_table.name == 'Magic Trick' or center_table.name == 'Illusion' then + G.E_MANAGER:add_event(Event({func = function() + G.GAME.playing_card_rate = center_table.extra + return true end })) + end + if center_table.name == 'Telescope' or center_table.name == 'Observatory' then + end + if center_table.name == 'Crystal Ball' then + G.E_MANAGER:add_event(Event({func = function() + G.consumeables.config.card_limit = G.consumeables.config.card_limit + center_table.extra + return true end })) + end + + if center_table.name == 'Clearance Sale' or center_table.name == 'Liquidation' then + G.E_MANAGER:add_event(Event({func = function() + G.GAME.discount_percent = center_table.extra + for k, v in pairs(G.I.CARD) do + if v.set_cost then v:set_cost() end + end + return true end })) + end + if center_table.name == 'Reroll Surplus' or center_table.name == 'Reroll Glut' then + G.E_MANAGER:add_event(Event({func = function() + G.GAME.round_resets.reroll_cost = G.GAME.round_resets.reroll_cost - center_table.extra + G.GAME.current_round.reroll_cost = math.max(0, G.GAME.current_round.reroll_cost - center_table.extra) + return true end })) + end + if center_table.name == 'Seed Money' or center_table.name == 'Money Tree' then + G.E_MANAGER:add_event(Event({func = function() + G.GAME.interest_cap = center_table.extra + return true end })) + end + if center_table.name == 'Grabber' or center_table.name == 'Nacho Tong' then + G.GAME.round_resets.hands = G.GAME.round_resets.hands + center_table.extra + ease_hands_played(center_table.extra) + end + if center_table.name == 'Paint Brush' or center_table.name == 'Palette' then + G.hand:change_size(center_table.extra) + end + if center_table.name == 'Wasteful' or center_table.name == 'Recyclomancy' then + G.GAME.round_resets.discards = G.GAME.round_resets.discards + center_table.extra + ease_discard(center_table.extra) + end + if center_table.name == 'Blank' then + check_for_unlock({type = 'blank_redeems'}) + end + if center_table.name == 'Antimatter' then + G.E_MANAGER:add_event(Event({func = function() + if G.jokers then + G.jokers.config.card_limit = G.jokers.config.card_limit + center_table.extra + end + return true end })) + end + + if center_table.name == 'Hieroglyph' or center_table.name == 'Petroglyph' then + ease_ante(math.floor(-center_table.extra)) + G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante or G.GAME.round_resets.ante + G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante-center_table.extra + + if center_table.name == 'Hieroglyph' then + G.GAME.round_resets.hands = G.GAME.round_resets.hands - center_table.extra + ease_hands_played(-center_table.extra) + end + if center_table.name == 'Petroglyph' then + G.GAME.round_resets.discards = G.GAME.round_resets.discards - center_table.extra + ease_discard(-center_table.extra) + end + end +end + +function Card:explode(dissolve_colours, explode_time_fac) + local explode_time = 1.3*(explode_time_fac or 1)*(math.sqrt(G.SETTINGS.GAMESPEED)) + self.dissolve = 0 + self.dissolve_colours = dissolve_colours + or {G.C.WHITE} + + local start_time = G.TIMERS.TOTAL + local percent = 0 + play_sound('explosion_buildup1') + self.juice = { + scale = 0, + r = 0, + handled_elsewhere = true, + start_time = start_time, + end_time = start_time + explode_time + } + + local childParts1 = Particles(0, 0, 0,0, { + timer_type = 'TOTAL', + timer = 0.01*explode_time, + scale = 0.2, + speed = 2, + lifespan = 0.2*explode_time, + attach = self, + colours = self.dissolve_colours, + fill = true + }) + local childParts2 = nil + + G.E_MANAGER:add_event(Event({ + blockable = false, + func = (function() + if self.juice then + percent = (G.TIMERS.TOTAL - start_time)/explode_time + self.juice.r = 0.05*(math.sin(5*G.TIMERS.TOTAL) + math.cos(0.33 + 41.15332*G.TIMERS.TOTAL) + math.cos(67.12*G.TIMERS.TOTAL))*percent + self.juice.scale = percent*0.15 + end + if G.TIMERS.TOTAL - start_time > 1.5*explode_time then return true end + end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = false, + ref_table = self, + ref_value = 'dissolve', + ease_to = 0.3, + delay = 0.9*explode_time, + func = (function(t) return t end) + })) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.9*explode_time, + func = (function() + childParts2 = Particles(0, 0, 0,0, { + timer_type = 'TOTAL', + pulse_max = 30, + timer = 0.003, + scale = 0.6, + speed = 15, + lifespan = 0.5, + attach = self, + colours = self.dissolve_colours, + }) + childParts2:set_role({r_bond = 'Weak'}) + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = false, + ref_table = self, + ref_value = 'dissolve', + ease_to = 1, + delay = 0.1*explode_time, + func = (function(t) return t end) + })) + self:juice_up() + G.VIBRATION = G.VIBRATION + 1 + play_sound('explosion_release1') + childParts1:fade(0.3*explode_time) return true end) + })) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 1.4*explode_time, + func = (function() + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = false, + blocking = false, + ref_value = 'scale', + ref_table = childParts2, + ease_to = 0, + delay = 0.1*explode_time + })) + return true end) + })) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 1.5*explode_time, + func = (function() self:remove() return true end) + })) +end + +function Card:shatter() + local dissolve_time = 0.7 + self.shattered = true + self.dissolve = 0 + self.dissolve_colours = {{1,1,1,0.8}} + self:juice_up() + local childParts = Particles(0, 0, 0,0, { + timer_type = 'TOTAL', + timer = 0.007*dissolve_time, + scale = 0.3, + speed = 4, + lifespan = 0.5*dissolve_time, + attach = self, + colours = self.dissolve_colours, + fill = true + }) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.5*dissolve_time, + func = (function() childParts:fade(0.15*dissolve_time) return true end) + })) + G.E_MANAGER:add_event(Event({ + blockable = false, + func = (function() + play_sound('glass'..math.random(1, 6), math.random()*0.2 + 0.9,0.5) + play_sound('generic1', math.random()*0.2 + 0.9,0.5) + return true end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = false, + ref_table = self, + ref_value = 'dissolve', + ease_to = 1, + delay = 0.5*dissolve_time, + func = (function(t) return t end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.55*dissolve_time, + func = (function() self:remove() return true end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.51*dissolve_time, + })) +end + +function Card:start_dissolve(dissolve_colours, silent, dissolve_time_fac, no_juice) + local dissolve_time = 0.7*(dissolve_time_fac or 1) + self.dissolve = 0 + self.dissolve_colours = dissolve_colours + or {G.C.BLACK, G.C.ORANGE, G.C.RED, G.C.GOLD, G.C.JOKER_GREY} + if not no_juice then self:juice_up() end + local childParts = Particles(0, 0, 0,0, { + timer_type = 'TOTAL', + timer = 0.01*dissolve_time, + scale = 0.1, + speed = 2, + lifespan = 0.7*dissolve_time, + attach = self, + colours = self.dissolve_colours, + fill = true + }) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.7*dissolve_time, + func = (function() childParts:fade(0.3*dissolve_time) return true end) + })) + if not silent then + G.E_MANAGER:add_event(Event({ + blockable = false, + func = (function() + play_sound('whoosh2', math.random()*0.2 + 0.9,0.5) + play_sound('crumple'..math.random(1, 5), math.random()*0.2 + 0.9,0.5) + return true end) + })) + end + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = false, + ref_table = self, + ref_value = 'dissolve', + ease_to = 1, + delay = 1*dissolve_time, + func = (function(t) return t end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 1.05*dissolve_time, + func = (function() self:remove() return true end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 1.051*dissolve_time, + })) +end + +function Card:start_materialize(dissolve_colours, silent, timefac) + local dissolve_time = 0.6*(timefac or 1) + self.states.visible = true + self.states.hover.can = false + self.dissolve = 1 + self.dissolve_colours = dissolve_colours or + (self.ability.set == 'Joker' and {G.C.RARITY[self.config.center.rarity]}) or + (self.ability.set == 'Planet' and {G.C.SECONDARY_SET.Planet}) or + (self.ability.set == 'Tarot' and {G.C.SECONDARY_SET.Tarot}) or + (self.ability.set == 'Spectral' and {G.C.SECONDARY_SET.Spectral}) or + (self.ability.set == 'Booster' and {G.C.BOOSTER}) or + (self.ability.set == 'Voucher' and {G.C.SECONDARY_SET.Voucher, G.C.CLEAR}) or + {G.C.GREEN} + self:juice_up() + self.children.particles = Particles(0, 0, 0,0, { + timer_type = 'TOTAL', + timer = 0.025*dissolve_time, + scale = 0.25, + speed = 3, + lifespan = 0.7*dissolve_time, + attach = self, + colours = self.dissolve_colours, + fill = true + }) + if not silent then + if not G.last_materialized or G.last_materialized +0.01 < G.TIMERS.REAL or G.last_materialized > G.TIMERS.REAL then + G.last_materialized = G.TIMERS.REAL + G.E_MANAGER:add_event(Event({ + blockable = false, + func = (function() + play_sound('whoosh1', math.random()*0.1 + 0.6,0.3) + play_sound('crumple'..math.random(1,5), math.random()*0.2 + 1.2,0.8) + return true end) + })) + end + end + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 0.5*dissolve_time, + func = (function() if self.children.particles then self.children.particles.max = 0 end return true end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = false, + ref_table = self, + ref_value = 'dissolve', + ease_to = 0, + delay = 1*dissolve_time, + func = (function(t) return t end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 1.05*dissolve_time, + func = (function() self.states.hover.can = true; if self.children.particles then self.children.particles:remove(); self.children.particles = nil end return true end) + })) +end + +function Card:calculate_seal(context) + if self.debuff then return nil end local obj = G.P_SEALS[self.seal] or {} + if obj.calculate and type(obj.calculate) == 'function' then + local o = obj:calculate(self, context) + if o then return o end + end + if context.repetition then + if self.seal == 'Red' then + return { + message = localize('k_again_ex'), + repetitions = 1, + card = self + } + end + end + if context.discard then + if self.seal == 'Purple' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local card = create_card('Tarot',G.consumeables, nil, nil, nil, nil, nil, '8ba') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + return true + end)})) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('k_plus_tarot'), colour = G.C.PURPLE}) + end + end +end + +function Card:calculate_rental() + if self.ability.rental then + ease_dollars(-G.GAME.rental_rate) + card_eval_status_text(self, 'dollars', -G.GAME.rental_rate) + end +end + +function Card:calculate_perishable() + if self.ability.perishable and not self.ability.perish_tally then self.ability.perish_tally = G.GAME.perishable_rounds end + if self.ability.perishable and self.ability.perish_tally > 0 then + if self.ability.perish_tally == 1 then + self.ability.perish_tally = 0 + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('k_disabled_ex'),colour = G.C.FILTER, delay = 0.45}) + self:set_debuff() + else + self.ability.perish_tally = self.ability.perish_tally - 1 + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_remaining',vars={self.ability.perish_tally}},colour = G.C.FILTER, delay = 0.45}) + end + end +end + +function Card:calculate_joker(context) +for k, v in pairs(SMODS.Stickers) do + if self.ability[v.key] then + if v.calculate and type(v.calculate) == 'function' then + local override_card = v:calculate(self, context) + if override_card then return override_card end + end + end +end + if self.debuff then return nil end + local obj = self.config.center + if self.ability.set ~= "Enhanced" and obj.calculate and type(obj.calculate) == 'function' then + local o, t = obj:calculate(self, context) + if o or t then return o, t end + end + if self.ability.set == "Planet" and not self.debuff then + if context.joker_main then + if G.GAME.used_vouchers.v_observatory and self.ability.consumeable.hand_type == context.scoring_name then + return { + message = localize{type = 'variable', key = 'a_xmult', vars = {G.GAME.cry_voucher_centers['v_observatory'].config.extra}}, + Xmult_mod = G.GAME.cry_voucher_centers['v_observatory'].config.extra + + } + end + end + end + if self.ability.set == "Joker" and not self.debuff then + if self.ability.name == "Blueprint" then + local other_joker = nil + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == self then other_joker = G.jokers.cards[i+1] end + end + if other_joker and other_joker ~= self then + context.blueprint = (context.blueprint and (context.blueprint + 1)) or 1 + context.copy_depth = (context.copy_depth and (context.copy_depth + 1)) or 1 + context.blueprint_card = context.blueprint_card or self + if context.blueprint > #G.jokers.cards + 1 then return end + context.no_callback = true + local other_joker_ret, trig = other_joker:calculate_joker(context) + if other_joker_ret then + other_joker_ret.card = context.blueprint_card or self + context.no_callback = not (context.copy_depth <= 1) + context.copy_depth = context.copy_depth - 1; + other_joker_ret.colour = G.C.BLUE + return other_joker_ret + end + end + end + if self.ability.name == "Brainstorm" then + local other_joker = G.jokers.cards[1] + if other_joker and other_joker ~= self then + context.blueprint = (context.blueprint and (context.blueprint + 1)) or 1 + context.copy_depth = (context.copy_depth and (context.copy_depth + 1)) or 1 + context.blueprint_card = context.blueprint_card or self + if context.blueprint > #G.jokers.cards + 1 then return end + context.no_callback = true + local other_joker_ret, trig = other_joker:calculate_joker(context) + if other_joker_ret then + other_joker_ret.card = context.blueprint_card or self + context.no_callback = not (context.copy_depth <= 1) + context.copy_depth = context.copy_depth - 1; + other_joker_ret.colour = G.C.RED + return other_joker_ret + end + end + end + if context.open_booster then + if self.ability.name == 'Hallucination' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + if pseudorandom('halu'..G.GAME.round_resets.ante) < G.GAME.probabilities.normal/self.ability.extra then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local card = create_card('Tarot',G.consumeables, nil, nil, nil, nil, nil, 'hal') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + return true + end)})) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('k_plus_tarot'), colour = G.C.PURPLE}) + end + end + elseif context.buying_card then + + elseif context.selling_self then + if self.ability.name == 'Luchador' then + if G.GAME.blind and ((not G.GAME.blind.disabled) and (G.GAME.blind:get_type() == 'Boss')) then + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('ph_boss_disabled')}) + G.GAME.blind:disable() + return nil, true + end + end + if self.ability.name == 'Diet Cola' then + G.E_MANAGER:add_event(Event({ + func = (function() + add_tag(Tag('tag_double')) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + return true + end) + })) + return nil, true + end + if self.ability.name == 'Invisible Joker' and (self.ability.invis_rounds >= self.ability.extra) and not context.blueprint then + local eval = function(card) return (card.ability.loyalty_remaining == 0) and not G.RESET_JIGGLES end + juice_card_until(self, eval, true) + local jokers = {} + for i=1, #G.jokers.cards do + if G.jokers.cards[i] ~= self then + jokers[#jokers+1] = G.jokers.cards[i] + end + end + if #jokers > 0 then + if #G.jokers.cards <= G.jokers.config.card_limit then + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_duplicated_ex')}) + local chosen_joker = pseudorandom_element(jokers, pseudoseed('invisible')) + local card = copy_card(chosen_joker, nil, nil, nil, chosen_joker.edition and chosen_joker.edition.negative) + if card.ability.invis_rounds then card.ability.invis_rounds = 0 end + card:add_to_deck() + G.jokers:emplace(card) return nil, true + else + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_no_room_ex')}) + end + else + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_no_other_jokers')}) + end + end + elseif context.selling_card then + if self.ability.name == 'Campfire' and not context.blueprint then + self.ability.x_mult = self.ability.x_mult + self.ability.extra + G.E_MANAGER:add_event(Event({ + func = function() card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('k_upgrade_ex')}); return true + end})) + end + if self.ability.name == 'Campfire' and not context.blueprint then return nil, true end + elseif context.reroll_shop then + if self.ability.name == 'Flash Card' and not context.blueprint then + self.ability.mult = self.ability.mult + self.ability.extra + G.E_MANAGER:add_event(Event({ + func = (function() + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_mult', vars = {self.ability.mult}}, colour = G.C.MULT}) + return true + end)})) + end + if self.ability.name == 'Flash Card' and not context.blueprint then return nil, true end + elseif context.ending_shop then + if self.ability.name == 'Perkeo' then + local eligibleJokers = {} + for i = 1, #G.consumeables.cards do + if G.consumeables.cards[i].ability.consumeable then + eligibleJokers[#eligibleJokers + 1] = G.consumeables.cards[i] + end + end + if #eligibleJokers > 0 then + G.E_MANAGER:add_event(Event({ + func = function() + local card = copy_card(pseudorandom_element(eligibleJokers, pseudoseed('perkeo')), nil) + card:set_edition({negative = true}, true) + card:add_to_deck() + G.consumeables:emplace(card) + return true + end})) + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_duplicated_ex')}) + return nil, true + end + return + end + return + elseif context.skip_blind then + if self.ability.name == 'Throwback' and not context.blueprint then + G.E_MANAGER:add_event(Event({ + func = function() + card_eval_status_text(self, 'extra', nil, nil, nil, { + message = localize{type = 'variable', key = 'a_xmult', vars = {self.ability.x_mult}}, + colour = G.C.RED, + card = self + }) + return true + end})) + return nil, true end + return + elseif context.skipping_booster then + if self.ability.name == 'Red Card' and not context.blueprint then + self.ability.mult = self.ability.mult + self.ability.extra + G.E_MANAGER:add_event(Event({ + func = function() + card_eval_status_text(self, 'extra', nil, nil, nil, { + message = localize{type = 'variable', key = 'a_mult', vars = {self.ability.extra}}, + colour = G.C.RED, + delay = 0.45, + card = self + }) + return true + end})) + return nil, true end + return + elseif context.playing_card_added and not self.getting_sliced then + if self.ability.name == 'Hologram' and (not context.blueprint) + and context.cards and context.cards[1] then + self.ability.x_mult = self.ability.x_mult + #context.cards*self.ability.extra + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_xmult', vars = {self.ability.x_mult}}}) + return nil, true end + elseif context.first_hand_drawn then + if self.ability.name == 'Certificate' then + G.E_MANAGER:add_event(Event({ + func = function() + local _card = create_playing_card({ + front = pseudorandom_element(G.P_CARDS, pseudoseed('cert_fr')), + center = G.P_CENTERS.c_base}, G.hand, nil, nil, {G.C.SECONDARY_SET.Enhanced}) + _card:set_seal(SMODS.poll_seal({guaranteed = true, type_key = 'certsl'})) + G.GAME.blind:debuff_card(_card) + G.hand:sort() + if context.blueprint_card then context.blueprint_card:juice_up() else self:juice_up() end + return true + end})) + + playing_card_joker_effects({true}) + return nil, true end + if self.ability.name == 'DNA' and not context.blueprint then + local eval = function() return G.GAME.current_round.hands_played == 0 end + juice_card_until(self, eval, true) + end + if self.ability.name == 'Trading Card' and not context.blueprint then + local eval = function() return G.GAME.current_round.discards_used == 0 and not G.RESET_JIGGLES end + juice_card_until(self, eval, true) + end + elseif context.setting_blind and not self.getting_sliced then + if self.ability.name == 'Chicot' and not context.blueprint + and context.blind.boss and not self.getting_sliced then + G.E_MANAGER:add_event(Event({func = function() + G.E_MANAGER:add_event(Event({func = function() + G.GAME.blind:disable() + play_sound('timpani') + delay(0.4) + return true end })) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize('ph_boss_disabled')}) + return true end })) + return nil, true end + if self.ability.name == 'Madness' and not context.blueprint and not context.blind.boss then + self.ability.x_mult = self.ability.x_mult + self.ability.extra + local destructable_jokers = {} + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] ~= self and not G.jokers.cards[i].ability.eternal and not G.jokers.cards[i].getting_sliced then destructable_jokers[#destructable_jokers+1] = G.jokers.cards[i] end + end + local joker_to_destroy = #destructable_jokers > 0 and pseudorandom_element(destructable_jokers, pseudoseed('madness')) or nil + + if joker_to_destroy and not (context.blueprint_card or self).getting_sliced then + joker_to_destroy.getting_sliced = true + G.E_MANAGER:add_event(Event({func = function() + (context.blueprint_card or self):juice_up(0.8, 0.8) + joker_to_destroy:start_dissolve({G.C.RED}, nil, 1.6) + return true end })) + end + if not (context.blueprint_card or self).getting_sliced then + card_eval_status_text((context.blueprint_card or self), 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_xmult', vars = {self.ability.x_mult}}}) + end + return nil, true end + if self.ability.name == 'Burglar' and not (context.blueprint_card or self).getting_sliced then + G.E_MANAGER:add_event(Event({func = function() + ease_discard(-G.GAME.current_round.discards_left, nil, true) + ease_hands_played(self.ability.extra) + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_hands', vars = {self.ability.extra}}}) + return true end })) + return nil, true end + if self.ability.name == 'Riff-raff' and not (context.blueprint_card or self).getting_sliced and #G.jokers.cards + G.GAME.joker_buffer < G.jokers.config.card_limit then + local jokers_to_create = math.min(2, G.jokers.config.card_limit - (#G.jokers.cards + G.GAME.joker_buffer)) + G.GAME.joker_buffer = G.GAME.joker_buffer + jokers_to_create + G.E_MANAGER:add_event(Event({ + func = function() + for i = 1, jokers_to_create do + local card = create_card('Joker', G.jokers, nil, 0, nil, nil, nil, 'rif') + card:add_to_deck() + G.jokers:emplace(card) + card:start_materialize() + G.GAME.joker_buffer = 0 + end + return true + end})) + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_plus_joker'), colour = G.C.BLUE}) + return nil, true end + if self.ability.name == 'Cartomancer' and not (context.blueprint_card or self).getting_sliced and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + func = (function() + G.E_MANAGER:add_event(Event({ + func = function() + local card = create_card('Tarot',G.consumeables, nil, nil, nil, nil, nil, 'car') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + return true + end})) + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_plus_tarot'), colour = G.C.PURPLE}) + return true + end)})) + return nil, true end + if self.ability.name == 'Ceremonial Dagger' and not context.blueprint then + local my_pos = nil + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == self then my_pos = i; break end + end + if my_pos and G.jokers.cards[my_pos+1] and not self.getting_sliced and not G.jokers.cards[my_pos+1].ability.eternal and not G.jokers.cards[my_pos+1].getting_sliced then + local sliced_card = G.jokers.cards[my_pos+1] + if sliced_card.config.center.rarity == "cry_exotic" then check_for_unlock({type = "what_have_you_done"}) end + sliced_card.getting_sliced = true + G.GAME.joker_buffer = G.GAME.joker_buffer - 1 + G.E_MANAGER:add_event(Event({func = function() + G.GAME.joker_buffer = 0 + self.ability.mult = self.ability.mult + sliced_card.sell_cost*2 + self:juice_up(0.8, 0.8) + sliced_card:start_dissolve({HEX("57ecab")}, nil, 1.6) + play_sound('slice1', 0.96+math.random()*0.08) + return true end })) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_mult', vars = {self.ability.mult+2*sliced_card.sell_cost}}, colour = G.C.RED, no_juice = true}) + return nil, true end + end + if self.ability.name == 'Marble Joker' and not (context.blueprint_card or self).getting_sliced then + G.E_MANAGER:add_event(Event({ + func = function() + local front = pseudorandom_element(G.P_CARDS, pseudoseed('marb_fr')) + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, front, G.P_CENTERS.m_stone, {playing_card = G.playing_card}) + card:start_materialize({G.C.SECONDARY_SET.Enhanced}) + G.play:emplace(card) + table.insert(G.playing_cards, card) + return true + end})) + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_plus_stone'), colour = G.C.SECONDARY_SET.Enhanced}) + + G.E_MANAGER:add_event(Event({ + func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + return true + end})) + draw_card(G.play,G.deck, 90,'up', nil) + + playing_card_joker_effects({true}) + return nil, true end + return + elseif context.destroying_card and not context.blueprint then + if self.ability.name == 'Sixth Sense' and #context.full_hand == 1 and context.full_hand[1]:get_id() == 6 and not context.full_hand[1].ability.eternal and G.GAME.current_round.hands_played == 0 then + if #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local card = create_card('Spectral',G.consumeables, nil, nil, nil, nil, nil, 'sixth') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + return true + end)})) + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_plus_spectral'), colour = G.C.SECONDARY_SET.Spectral}) + end + return true + end + return nil + elseif context.cards_destroyed then + if self.ability.name == 'Caino' and not context.blueprint then + local faces = 0 + for k, v in ipairs(context.glass_shattered) do + if v:is_face() then + faces = faces + 1 + end + end + if faces > 0 then + G.E_MANAGER:add_event(Event({ + func = function() + G.E_MANAGER:add_event(Event({ + func = function() + self.ability.caino_xmult = self.ability.caino_xmult + faces*self.ability.extra + return true + end + })) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_xmult', vars = {self.ability.caino_xmult + faces*self.ability.extra}}}) + return true + end + })) + end + + return + end + if self.ability.name == 'Glass Joker' and not context.blueprint then + local glasses = 0 + for k, v in ipairs(context.glass_shattered) do + if v.shattered then + glasses = glasses + 1 + end + end + if glasses > 0 then + G.E_MANAGER:add_event(Event({ + func = function() + G.E_MANAGER:add_event(Event({ + func = function() + self.ability.x_mult = self.ability.x_mult + self.ability.extra*glasses + return true + end + })) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_xmult', vars = {self.ability.x_mult + self.ability.extra*glasses}}}) + return true + end + })) + end + + return + end + + elseif context.remove_playing_cards then + if self.ability.name == 'Caino' and not context.blueprint then + local face_cards = 0 + for k, val in ipairs(context.removed) do + if val:is_face() then face_cards = face_cards + 1 end + end + if face_cards > 0 then + self.ability.caino_xmult = self.ability.caino_xmult + face_cards*self.ability.extra + G.E_MANAGER:add_event(Event({ + func = function() card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_xmult', vars = {self.ability.caino_xmult}}}); return true + end})) return nil, true + end + return + end + + if self.ability.name == 'Glass Joker' and not context.blueprint then + local glass_cards = 0 + for k, val in ipairs(context.removed) do + if val.shattered then glass_cards = glass_cards + 1 end + end + if glass_cards > 0 then + G.E_MANAGER:add_event(Event({ + func = function() + G.E_MANAGER:add_event(Event({ + func = function() + self.ability.x_mult = self.ability.x_mult + self.ability.extra*glass_cards + return true + end + })) + card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type = 'variable', key = 'a_xmult', vars = {self.ability.x_mult + self.ability.extra*glass_cards}}}) + return true + end + })) return nil, true + end + return + end + elseif context.using_consumeable then + if self.ability.name == 'Glass Joker' and not context.blueprint and context.consumeable.ability.name == 'The Hanged Man' then + local shattered_glass = 0 + for k, val in ipairs(G.hand.highlighted) do + if val.ability.name == 'Glass Card' then shattered_glass = shattered_glass + 1 end + end + if shattered_glass > 0 then + self.ability.x_mult = self.ability.x_mult + self.ability.extra*shattered_glass + G.E_MANAGER:add_event(Event({ + func = function() card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}}}); return true + end})) + return nil, true end + return + end + if self.ability.name == 'Fortune Teller' and not context.blueprint and (context.consumeable.ability.set == "Tarot") then + G.E_MANAGER:add_event(Event({ + func = function() card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_mult',vars={G.GAME.consumeable_usage_total.tarot}}}); return true + end})) + return nil, true end + if self.ability.name == 'Constellation' and not context.blueprint and context.consumeable.ability.set == 'Planet' then + self.ability.x_mult = self.ability.x_mult + self.ability.extra + G.E_MANAGER:add_event(Event({ + func = function() card_eval_status_text(self, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}}}); return true + end})) + return + nil, true end + return + elseif context.debuffed_hand then + if self.ability.name == 'Matador' then + if G.GAME.blind.triggered then + ease_dollars(self.ability.extra) + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + self.ability.extra + if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end + return { + message = localize('$')..self.ability.extra, + dollars = self.ability.extra, + colour = G.C.MONEY + } + end + end + elseif context.pre_discard then + if self.ability.name == 'Burnt Joker' and G.GAME.current_round.discards_used <= 0 and not context.hook then + local text,disp_text = G.FUNCS.get_poker_hand_info(G.hand.highlighted) + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('k_upgrade_ex')}) + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, {handname=localize(text, 'poker_hands'),chips = G.GAME.hands[text].chips, mult = G.GAME.hands[text].mult, level=G.GAME.hands[text].level}) + level_up_hand(context.blueprint_card or self, text, nil, 1) + update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''}) + return nil, true end + elseif context.discard then + if self.ability.name == 'Ramen' and not context.blueprint then + if self.ability.x_mult - self.ability.extra <= 1 then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(self) + self:remove() + self = nil + return true; end})) + return true + end + })) + return { + message = localize('k_eaten_ex'), + colour = G.C.FILTER + } + else + self.ability.x_mult = self.ability.x_mult - self.ability.extra + return { + delay = 0.2, + message = localize{type='variable',key='a_xmult_minus',vars={self.ability.extra}}, + colour = G.C.RED + } + end + end + if self.ability.name == 'Yorick' and not context.blueprint then + if self.ability.yorick_discards <= 1 then + self.ability.yorick_discards = self.ability.extra.discards + self.ability.x_mult = self.ability.x_mult + self.ability.extra.xmult + return { + delay = 0.2, + message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}}, + colour = G.C.RED + } + else + self.ability.yorick_discards = self.ability.yorick_discards - 1 + return nil, true + end + return + end + if self.ability.name == 'Trading Card' and not context.blueprint and + G.GAME.current_round.discards_used <= 0 and #context.full_hand == 1 and not context.other_card.ability.eternal then + ease_dollars(self.ability.extra) + return { + message = localize('$')..self.ability.extra, + colour = G.C.MONEY, + delay = 0.45, + remove = true, + card = self + } + end + + if self.ability.name == 'Castle' and + not context.other_card.debuff and + context.other_card:is_suit(G.GAME.current_round.castle_card.suit) and not context.blueprint then + self.ability.extra.chips = self.ability.extra.chips + self.ability.extra.chip_mod + + return { + message = localize('k_upgrade_ex'), + card = self, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Mail-In Rebate' and + not context.other_card.debuff and + context.other_card:get_id() == G.GAME.current_round.mail_card.id then + ease_dollars(self.ability.extra) + return { + message = localize('$')..self.ability.extra, + colour = G.C.MONEY, + card = self + } + end + if self.ability.name == 'Hit the Road' and + not context.other_card.debuff and + context.other_card:get_id() == 11 and not context.blueprint then + self.ability.x_mult = self.ability.x_mult + self.ability.extra + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}}, + colour = G.C.RED, + delay = 0.45, + card = self + } + end + if self.ability.name == 'Green Joker' and not context.blueprint and context.other_card == context.full_hand[#context.full_hand] then + local prev_mult = self.ability.mult + self.ability.mult = math.max(0, self.ability.mult - self.ability.extra.discard_sub) + if self.ability.mult ~= prev_mult then + return { + message = localize{type='variable',key='a_mult_minus',vars={self.ability.extra.discard_sub}}, + colour = G.C.RED, + card = self + } + end + end + + if self.ability.name == 'Faceless Joker' and context.other_card == context.full_hand[#context.full_hand] then + local face_cards = 0 + for k, v in ipairs(context.full_hand) do + if v:is_face() then face_cards = face_cards + 1 end + end + if face_cards >= self.ability.extra.faces then + G.E_MANAGER:add_event(Event({ + func = function() + ease_dollars(self.ability.extra.dollars) + card_eval_status_text(context.blueprint_card or self, 'extra', nil, nil, nil, {message = localize('$')..self.ability.extra.dollars,colour = G.C.MONEY, delay = 0.45}) + return true + end})) + return + nil, true end + end + return + elseif context.end_of_round then + if context.individual then + + elseif context.repetition then + if context.cardarea == G.hand then + if self.ability.name == 'Mime' and + (next(context.card_effects[1]) or #context.card_effects > 1) then + return { + message = localize('k_again_ex'), + repetitions = self.ability.extra, + card = self + } + end + end + elseif not context.blueprint then + if self.ability.name == 'Campfire' and G.GAME.blind.boss and self.ability.x_mult > 1 then + self.ability.x_mult = 1 + return { + message = localize('k_reset'), + colour = G.C.RED + } + end + if self.ability.name == 'Rocket' and G.GAME.blind.boss then + self.ability.extra.dollars = self.ability.extra.dollars + self.ability.extra.increase + return { + message = localize('k_upgrade_ex'), + colour = G.C.MONEY + } + end + if self.ability.name == 'Turtle Bean' and not context.blueprint then + if self.ability.extra.h_size - self.ability.extra.h_mod <= 0 then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(self) + self:remove() + self = nil + return true; end})) + return true + end + })) + return { + message = localize('k_eaten_ex'), + colour = G.C.FILTER + } + else + self.ability.extra.h_size = self.ability.extra.h_size - self.ability.extra.h_mod + G.hand:change_size(- self.ability.extra.h_mod) + return { + message = localize{type='variable',key='a_handsize_minus',vars={self.ability.extra.h_mod}}, + colour = G.C.FILTER + } + end + end + if self.ability.name == 'Invisible Joker' and not context.blueprint then + self.ability.invis_rounds = self.ability.invis_rounds + 1 + if self.ability.invis_rounds == self.ability.extra then + local eval = function(card) return not card.REMOVED end + juice_card_until(self, eval, true) + end + return { + message = (self.ability.invis_rounds < self.ability.extra) and (self.ability.invis_rounds..'/'..self.ability.extra) or localize('k_active_ex'), + colour = G.C.FILTER + } + end + if self.ability.name == 'Popcorn' and not context.blueprint then + if self.ability.mult - self.ability.extra <= 0 then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(self) + self:remove() + self = nil + return true; end})) + return true + end + })) + return { + message = localize('k_eaten_ex'), + colour = G.C.RED + } + else + self.ability.mult = self.ability.mult - self.ability.extra + return { + message = localize{type='variable',key='a_mult_minus',vars={self.ability.extra}}, + colour = G.C.MULT + } + end + end + if self.ability.name == 'To Do List' and not context.blueprint then + local _poker_hands = {} + for k, v in pairs(G.GAME.hands) do + if v.visible and k ~= self.ability.to_do_poker_hand then _poker_hands[#_poker_hands+1] = k end + end + self.ability.to_do_poker_hand = pseudorandom_element(_poker_hands, pseudoseed('to_do')) + return { + message = localize('k_reset') + } + end + if self.ability.name == 'Egg' then + self.ability.extra_value = self.ability.extra_value + self.ability.extra + self:set_cost() + return { + message = localize('k_val_up'), + colour = G.C.MONEY + } + end + if self.ability.name == 'Gift Card' then + for k, v in ipairs(G.jokers.cards) do + if v.set_cost then + v.ability.extra_value = (v.ability.extra_value or 0) + self.ability.extra + v:set_cost() + end + end + for k, v in ipairs(G.consumeables.cards) do + if v.set_cost then + v.ability.extra_value = (v.ability.extra_value or 0) + self.ability.extra + v:set_cost() + end + end + return { + message = localize('k_val_up'), + colour = G.C.MONEY + } + end + if self.ability.name == 'Hit the Road' and to_big(self.ability.x_mult) > to_big(1) then + self.ability.x_mult = 1 + return { + message = localize('k_reset'), + colour = G.C.RED + } + end + + if self.ability.name == 'Gros Michel' or self.ability.name == 'Cavendish' then + if pseudorandom(self.ability.name == 'Cavendish' and 'cavendish' or 'gros_michel') < G.GAME.probabilities.normal/self.ability.extra.odds then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(self) + self:remove() + self = nil + return true; end})) + return true + end + })) + if self.ability.name == 'Gros Michel' then G.GAME.pool_flags.gros_michel_extinct = true end + return { + message = localize('k_extinct_ex') + } + else + return { + message = localize('k_safe_ex') + } + end + end + if self.ability.name == 'Mr. Bones' and context.game_over and + to_big(G.GAME.chips)/G.GAME.blind.chips >= to_big(0.25) then + G.E_MANAGER:add_event(Event({ + func = function() + G.hand_text_area.blind_chips:juice_up() + G.hand_text_area.game_chips:juice_up() + play_sound('tarot1') + self:start_dissolve() + return true + end + })) + return { + message = localize('k_saved_ex'), + saved = true, + colour = G.C.RED + } + end + end + elseif context.individual then + if context.cardarea == G.play then + if self.ability.name == 'Hiker' then + context.other_card.ability.perma_bonus = context.other_card.ability.perma_bonus or 0 + context.other_card.ability.perma_bonus = context.other_card.ability.perma_bonus + self.ability.extra + return { + extra = {message = localize('k_upgrade_ex'), colour = G.C.CHIPS}, + colour = G.C.CHIPS, + card = self + } + end + if self.ability.name == 'Lucky Cat' and context.other_card.lucky_trigger and not context.blueprint then + self.ability.x_mult = self.ability.x_mult + self.ability.extra + return { + extra = {focus = self, message = localize('k_upgrade_ex'), colour = G.C.MULT}, + card = self + } + end + if self.ability.name == 'Wee Joker' and + context.other_card:get_id() == 2 and not context.blueprint then + self.ability.extra.chips = self.ability.extra.chips + self.ability.extra.chip_mod + + return { + extra = {focus = self, message = localize('k_upgrade_ex')}, + card = self, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Photograph' then + local first_face = nil + for i = 1, #context.scoring_hand do + if context.scoring_hand[i]:is_face() then first_face = context.scoring_hand[i]; break end + end + if context.other_card == first_face then + return { + x_mult = self.ability.extra, + colour = G.C.RED, + card = self + } + end + end + if self.ability.name == '8 Ball' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + if (context.other_card:get_id() == 8) and (pseudorandom('8ball') < G.GAME.probabilities.normal/self.ability.extra) then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + return { + extra = {focus = self, message = localize('k_plus_tarot'), func = function() + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local card = create_card('Tarot',G.consumeables, nil, nil, nil, nil, nil, '8ba') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + return true + end)})) + end}, + colour = G.C.SECONDARY_SET.Tarot, + card = self + } + end + end + if self.ability.name == 'The Idol' and + context.other_card:get_id() == G.GAME.current_round.idol_card.id and + context.other_card:is_suit(G.GAME.current_round.idol_card.suit) then + return { + x_mult = self.ability.extra, + colour = G.C.RED, + card = self + } + end + if self.ability.name == 'Scary Face' and ( + context.other_card:is_face()) then + return { + chips = self.ability.extra, + card = self + } + end + if self.ability.name == 'Smiley Face' and ( + context.other_card:is_face()) then + return { + mult = self.ability.extra, + card = self + } + end + if self.ability.name == 'Golden Ticket' and + context.other_card.ability.name == 'Gold Card' then + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + self.ability.extra + if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end + return { + dollars = self.ability.extra, + card = self + } + end + if self.ability.name == 'Scholar' and + context.other_card:get_id() == 14 then + return { + chips = self.ability.extra.chips, + mult = self.ability.extra.mult, + card = self + } + end + if self.ability.name == 'Walkie Talkie' and + (context.other_card:get_id() == 10 or context.other_card:get_id() == 4) then + return { + chips = self.ability.extra.chips, + mult = self.ability.extra.mult, + card = self + } + end + if self.ability.name == 'Business Card' and + context.other_card:is_face() and + pseudorandom('business') < G.GAME.probabilities.normal/self.ability.extra then + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + 2 + if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end + return { + dollars = 2, + card = self + } + end + if self.ability.name == 'Fibonacci' and ( + context.other_card:get_id() == 2 or + context.other_card:get_id() == 3 or + context.other_card:get_id() == 5 or + context.other_card:get_id() == 8 or + context.other_card:get_id() == 14) then + return { + mult = self.ability.extra, + card = self + } + end + if self.ability.name == 'Even Steven' and + context.other_card:get_id() <= 10 and + context.other_card:get_id() >= 0 and + context.other_card:get_id()%2 == 0 + then + return { + mult = self.ability.extra, + card = self + } + end + if self.ability.name == 'Odd Todd' and + ((context.other_card:get_id() <= 10 and + context.other_card:get_id() >= 0 and + context.other_card:get_id()%2 == 1) or + (context.other_card:get_id() == 14)) + then + return { + chips = self.ability.extra, + card = self + } + end + if self.ability.effect == 'Suit Mult' and + context.other_card:is_suit(self.ability.extra.suit) then + return { + mult = self.ability.extra.s_mult, + card = self + } + end + if self.ability.name == 'Rough Gem' and + context.other_card:is_suit("Diamonds") then + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + self.ability.extra + if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end + return { + dollars = self.ability.extra, + card = self + } + end + if self.ability.name == 'Onyx Agate' and + context.other_card:is_suit("Clubs") then + return { + mult = self.ability.extra, + card = self + } + end + if self.ability.name == 'Arrowhead' and + context.other_card:is_suit("Spades") then + return { + chips = self.ability.extra, + card = self + } + end + if self.ability.name == 'Bloodstone' and + context.other_card:is_suit("Hearts") and + pseudorandom('bloodstone') < G.GAME.probabilities.normal/self.ability.extra.odds then + return { + x_mult = self.ability.extra.Xmult, + card = self + } + end + if self.ability.name == 'Ancient Joker' and + context.other_card:is_suit(G.GAME.current_round.ancient_card.suit) then + return { + x_mult = self.ability.extra, + card = self + } + end + if self.ability.name == 'Triboulet' and + (context.other_card:get_id() == 12 or context.other_card:get_id() == 13) then + return { + x_mult = self.ability.extra, + colour = G.C.RED, + card = self + } + end + end + if context.cardarea == G.hand then + if self.ability.name == 'Shoot the Moon' and + context.other_card:get_id() == 12 then + if context.other_card.debuff then + return { + message = localize('k_debuffed'), + colour = G.C.RED, + card = self, + } + else + return { + h_mult = 13, + card = self + } + end + end + if self.ability.name == 'Baron' and + context.other_card:get_id() == 13 then + if context.other_card.debuff then + return { + message = localize('k_debuffed'), + colour = G.C.RED, + card = self, + } + else + return { + x_mult = self.ability.extra, + card = self + } + end + end + if self.ability.name == 'Reserved Parking' and + context.other_card:is_face() and + pseudorandom('parking') < G.GAME.probabilities.normal/self.ability.extra.odds then + if context.other_card.debuff then + return { + message = localize('k_debuffed'), + colour = G.C.RED, + card = self, + } + else + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + self.ability.extra.dollars + if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end + return { + dollars = self.ability.extra.dollars, + card = self + } + end + end + if self.ability.name == 'Raised Fist' then + local temp_Mult, temp_ID = 15, 15 + local raised_card = nil + for i=1, #G.hand.cards do + if temp_ID >= G.hand.cards[i].base.id and (G.hand.cards[i].ability.effect ~= 'Stone Card' and not G.hand.cards[i].config.center.no_rank) then + temp_Mult = G.hand.cards[i].base.nominal + temp_ID = G.hand.cards[i].base.id + raised_card = G.hand.cards[i] + end + end + if raised_card == context.other_card then + if context.other_card.debuff then + return { + message = localize('k_debuffed'), + colour = G.C.RED, + card = self, + } + else + return { + h_mult = 2*temp_Mult, + card = self, + } + end + end + end + end + elseif context.repetition then + if context.cardarea == G.play then + if self.ability.name == 'Sock and Buskin' and ( + context.other_card:is_face()) then + return { + message = localize('k_again_ex'), + repetitions = self.ability.extra, + card = self + } + end + if self.ability.name == 'Hanging Chad' and ( + context.other_card == context.scoring_hand[1]) then + return { + message = localize('k_again_ex'), + repetitions = self.ability.extra, + card = self + } + end + if self.ability.name == 'Dusk' and G.GAME.current_round.hands_left == 0 then + return { + message = localize('k_again_ex'), + repetitions = self.ability.extra, + card = self + } + end + if self.ability.name == 'Seltzer' then + return { + message = localize('k_again_ex'), + repetitions = 1, + card = self + } + end + if self.ability.name == 'Hack' and ( + context.other_card:get_id() == 2 or + context.other_card:get_id() == 3 or + context.other_card:get_id() == 4 or + context.other_card:get_id() == 5) then + return { + message = localize('k_again_ex'), + repetitions = self.ability.extra, + card = self + } + end + end + if context.cardarea == G.hand then + if self.ability.name == 'Mime' and + (next(context.card_effects[1]) or #context.card_effects > 1) then + return { + message = localize('k_again_ex'), + repetitions = self.ability.extra, + card = self + } + end + end + elseif context.other_joker then + if self.ability.name == 'Baseball Card' and (context.other_joker.config.center.rarity == 2 or context.other_joker.config.center.rarity == "Uncommon") and self ~= context.other_joker then + G.E_MANAGER:add_event(Event({ + func = function() + context.other_joker:juice_up(0.5, 0.5) + return true + end + })) + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra}}, + Xmult_mod = self.ability.extra + } + end + else + if context.cardarea == G.jokers then + if context.before then + if self.ability.name == 'Spare Trousers' and (next(context.poker_hands['Two Pair']) or next(context.poker_hands['Full House'])) and not context.blueprint then + self.ability.mult = self.ability.mult + self.ability.extra + return { + message = localize('k_upgrade_ex'), + colour = G.C.RED, + card = self + } + end + if self.ability.name == 'Space Joker' and pseudorandom('space') < G.GAME.probabilities.normal/self.ability.extra then + return { + card = self, + level_up = true, + message = localize('k_level_up_ex') + } + end + if self.ability.name == 'Square Joker' and #context.full_hand == 4 and not context.blueprint then + self.ability.extra.chips = self.ability.extra.chips + self.ability.extra.chip_mod + return { + message = localize('k_upgrade_ex'), + colour = G.C.CHIPS, + card = self + } + end + if self.ability.name == 'Runner' and next(context.poker_hands['Straight']) and not context.blueprint then + self.ability.extra.chips = self.ability.extra.chips + self.ability.extra.chip_mod + return { + message = localize('k_upgrade_ex'), + colour = G.C.CHIPS, + card = self + } + end + if self.ability.name == 'Midas Mask' and not context.blueprint then + local faces = {} + for k, v in ipairs(context.scoring_hand) do + if v:is_face() then + faces[#faces+1] = v + v:set_ability(G.P_CENTERS.m_gold, nil, true) + G.E_MANAGER:add_event(Event({ + func = function() + v:juice_up() + return true + end + })) + end + end + if #faces > 0 then + return { + message = localize('k_gold'), + colour = G.C.MONEY, + card = self + } + end + end + if self.ability.name == 'Vampire' and not context.blueprint then + local enhanced = {} + for k, v in ipairs(context.scoring_hand) do + if v.config.center ~= G.P_CENTERS.c_base and not v.debuff and not v.vampired then + enhanced[#enhanced+1] = v + v.vampired = true + v:set_ability(G.P_CENTERS.c_base, nil, true) + G.E_MANAGER:add_event(Event({ + func = function() + v:juice_up() + v.vampired = nil + return true + end + })) + end + end + + if #enhanced > 0 then + self.ability.x_mult = self.ability.x_mult + self.ability.extra*#enhanced + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}}, + colour = G.C.MULT, + card = self + } + end + end + if self.ability.name == 'To Do List' and context.scoring_name == self.ability.to_do_poker_hand then + ease_dollars(self.ability.extra.dollars) + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + self.ability.extra.dollars + if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end + return { + message = localize('$')..self.ability.extra.dollars, + dollars = self.ability.extra.dollars, + colour = G.C.MONEY + } + end + if self.ability.name == 'DNA' and G.GAME.current_round.hands_played == 0 then + if #context.full_hand == 1 then + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local _card = copy_card(context.full_hand[1], nil, nil, G.playing_card) + _card:add_to_deck() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + table.insert(G.playing_cards, _card) + G.hand:emplace(_card) + _card.states.visible = nil + + G.E_MANAGER:add_event(Event({ + func = function() + _card:start_materialize() + return true + end + })) + return { + message = localize('k_copied_ex'), + colour = G.C.CHIPS, + card = self, + playing_cards_created = {true} + } + end + end + if self.ability.name == 'Ride the Bus' and not context.blueprint then + local faces = false + for i = 1, #context.scoring_hand do + if context.scoring_hand[i]:is_face() then faces = true end + end + if faces then + local last_mult = self.ability.mult + self.ability.mult = 0 + if last_mult > 0 then + return { + card = self, + message = localize('k_reset') + } + end + else + self.ability.mult = self.ability.mult + self.ability.extra + end + end + if self.ability.name == 'Obelisk' and not context.blueprint then + local reset = true + local play_more_than = (G.GAME.hands[context.scoring_name].played or 0) + for k, v in pairs(G.GAME.hands) do + if k ~= context.scoring_name and v.played >= play_more_than and v.visible then + reset = false + end + end + if reset then + if to_big(self.ability.x_mult) > to_big(1) then + self.ability.x_mult = 1 + return { + card = self, + message = localize('k_reset') + } + end + else + self.ability.x_mult = self.ability.x_mult + self.ability.extra + end + end + if self.ability.name == 'Green Joker' and not context.blueprint then + self.ability.mult = self.ability.mult + self.ability.extra.hand_add + return { + card = self, + message = localize{type='variable',key='a_mult',vars={self.ability.extra.hand_add}} + } + end + elseif context.after then + if self.ability.name == 'Ice Cream' and not context.blueprint then + if self.ability.extra.chips - self.ability.extra.chip_mod <= 0 then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(self) + self:remove() + self = nil + return true; end})) + return true + end + })) + return { + message = localize('k_melted_ex'), + colour = G.C.CHIPS + } + else + self.ability.extra.chips = self.ability.extra.chips - self.ability.extra.chip_mod + return { + message = localize{type='variable',key='a_chips_minus',vars={self.ability.extra.chip_mod}}, + colour = G.C.CHIPS + } + end + end + if self.ability.name == 'Seltzer' and not context.blueprint then + if self.ability.extra - 1 <= 0 then + G.E_MANAGER:add_event(Event({ + func = function() + play_sound('tarot1') + self.T.r = -0.2 + self:juice_up(0.3, 0.4) + self.states.drag.is = true + self.children.center.pinch.x = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, blockable = false, + func = function() + G.jokers:remove_card(self) + self:remove() + self = nil + return true; end})) + return true + end + })) + return { + message = localize('k_drank_ex'), + colour = G.C.FILTER + } + else + self.ability.extra = self.ability.extra - 1 + return { + message = self.ability.extra..'', + colour = G.C.FILTER + } + end + end + elseif context.joker_main then + if self.ability.name == 'Loyalty Card' then + self.ability.loyalty_remaining = (self.ability.extra.every-1-(G.GAME.hands_played - self.ability.hands_played_at_create))%(self.ability.extra.every+1) + if context.blueprint then + if self.ability.loyalty_remaining == self.ability.extra.every then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra.Xmult}}, + Xmult_mod = self.ability.extra.Xmult + } + end + else + if self.ability.loyalty_remaining == 0 then + local eval = function(card) return (card.ability.loyalty_remaining == 0) end + juice_card_until(self, eval, true) + elseif self.ability.loyalty_remaining == self.ability.extra.every then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra.Xmult}}, + Xmult_mod = self.ability.extra.Xmult + } + end + end + end + if self.ability.name ~= 'Seeing Double' and to_big(self.ability.x_mult) > to_big(1) and (self.ability.type == '' or next(context.poker_hands[self.ability.type])) then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}}, + colour = G.C.RED, + Xmult_mod = self.ability.x_mult + } + end + if to_big(self.ability.t_mult) > to_big(0) and next(context.poker_hands[self.ability.type]) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.t_mult}}, + mult_mod = self.ability.t_mult + } + end + if to_big(self.ability.t_chips) > to_big(0) and next(context.poker_hands[self.ability.type]) then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.t_chips}}, + chip_mod = self.ability.t_chips + } + end + if self.ability.name == 'Half Joker' and #context.full_hand <= self.ability.extra.size then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.extra.mult}}, + mult_mod = self.ability.extra.mult + } + end + if self.ability.name == 'Abstract Joker' then + local x = 0 + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.set == 'Joker' then x = x + 1 end + end + return { + message = localize{type='variable',key='a_mult',vars={x*self.ability.extra}}, + mult_mod = x*self.ability.extra + } + end + if self.ability.name == 'Acrobat' and G.GAME.current_round.hands_left == 0 then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra}}, + Xmult_mod = self.ability.extra + } + end + if self.ability.name == 'Mystic Summit' and G.GAME.current_round.discards_left == self.ability.extra.d_remaining then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.extra.mult}}, + mult_mod = self.ability.extra.mult + } + end + if self.ability.name == 'Misprint' then + local temp_Mult = pseudorandom('misprint', self.ability.extra.min, self.ability.extra.max) + return { + message = localize{type='variable',key='a_mult',vars={temp_Mult}}, + mult_mod = temp_Mult + } + end + if self.ability.name == 'Banner' and G.GAME.current_round.discards_left > 0 then + return { + message = localize{type='variable',key='a_chips',vars={G.GAME.current_round.discards_left*self.ability.extra}}, + chip_mod = G.GAME.current_round.discards_left*self.ability.extra + } + end + if self.ability.name == 'Stuntman' then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra.chip_mod}}, + chip_mod = self.ability.extra.chip_mod, + } + end + if self.ability.name == 'Matador' then + if G.GAME.blind.triggered then + ease_dollars(self.ability.extra) + G.GAME.dollar_buffer = (G.GAME.dollar_buffer or 0) + self.ability.extra + if not Talisman.config_file.disable_anims then G.E_MANAGER:add_event(Event({func = (function() G.GAME.dollar_buffer = 0; return true end)})) else G.GAME.dollar_buffer = 0 end + return { + message = localize('$')..self.ability.extra, + dollars = self.ability.extra, + colour = G.C.MONEY + } + end + end + if self.ability.name == 'Supernova' then + return { + message = localize{type='variable',key='a_mult',vars={G.GAME.hands[context.scoring_name].played}}, + mult_mod = G.GAME.hands[context.scoring_name].played + } + end + if self.ability.name == 'Ceremonial Dagger' and to_big(self.ability.mult) > to_big(0) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Vagabond' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + if G.GAME.dollars <= self.ability.extra then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local card = create_card('Tarot',G.consumeables, nil, nil, nil, nil, nil, 'vag') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + return true + end)})) + return { + message = localize('k_plus_tarot'), + card = self + } + end + end + if self.ability.name == 'Superposition' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + local aces = 0 + for i = 1, #context.scoring_hand do + if context.scoring_hand[i]:get_id() == 14 then aces = aces + 1 end + end + if aces >= 1 and next(context.poker_hands["Straight"]) then + local card_type = 'Tarot' + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local card = create_card(card_type,G.consumeables, nil, nil, nil, nil, nil, 'sup') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + return true + end)})) + return { + message = localize('k_plus_tarot'), + colour = G.C.SECONDARY_SET.Tarot, + card = self + } + end + end + if self.ability.name == 'Seance' and #G.consumeables.cards + G.GAME.consumeable_buffer < G.consumeables.config.card_limit then + if next(context.poker_hands[self.ability.extra.poker_hand]) then + G.GAME.consumeable_buffer = G.GAME.consumeable_buffer + 1 + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.0, + func = (function() + local card = create_card('Spectral',G.consumeables, nil, nil, nil, nil, nil, 'sea') + card:add_to_deck() + G.consumeables:emplace(card) + G.GAME.consumeable_buffer = 0 + return true + end)})) + return { + message = localize('k_plus_spectral'), + colour = G.C.SECONDARY_SET.Spectral, + card = self + } + end + end + if self.ability.name == 'Flower Pot' then + local suits = { + ['Hearts'] = 0, + ['Diamonds'] = 0, + ['Spades'] = 0, + ['Clubs'] = 0 + } + for i = 1, #context.scoring_hand do + if context.scoring_hand[i].ability.name ~= 'Wild Card' and not context.scoring_hand[i].config.center.any_suit then + if context.scoring_hand[i]:is_suit('Hearts', true) and suits["Hearts"] == 0 then suits["Hearts"] = suits["Hearts"] + 1 + elseif context.scoring_hand[i]:is_suit('Diamonds', true) and suits["Diamonds"] == 0 then suits["Diamonds"] = suits["Diamonds"] + 1 + elseif context.scoring_hand[i]:is_suit('Spades', true) and suits["Spades"] == 0 then suits["Spades"] = suits["Spades"] + 1 + elseif context.scoring_hand[i]:is_suit('Clubs', true) and suits["Clubs"] == 0 then suits["Clubs"] = suits["Clubs"] + 1 end + end + end + for i = 1, #context.scoring_hand do + if context.scoring_hand[i].ability.name == 'Wild Card' or context.scoring_hand[i].config.center.any_suit then + if context.scoring_hand[i]:is_suit('Hearts') and suits["Hearts"] == 0 then suits["Hearts"] = suits["Hearts"] + 1 + elseif context.scoring_hand[i]:is_suit('Diamonds') and suits["Diamonds"] == 0 then suits["Diamonds"] = suits["Diamonds"] + 1 + elseif context.scoring_hand[i]:is_suit('Spades') and suits["Spades"] == 0 then suits["Spades"] = suits["Spades"] + 1 + elseif context.scoring_hand[i]:is_suit('Clubs') and suits["Clubs"] == 0 then suits["Clubs"] = suits["Clubs"] + 1 end + end + end + if suits["Hearts"] > 0 and + suits["Diamonds"] > 0 and + suits["Spades"] > 0 and + suits["Clubs"] > 0 then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra}}, + Xmult_mod = self.ability.extra + } + end + end + if self.ability.name == 'Seeing Double' then + local suits = { + ['Hearts'] = 0, + ['Diamonds'] = 0, + ['Spades'] = 0, + ['Clubs'] = 0 + } + for i = 1, #context.scoring_hand do + if context.scoring_hand[i].ability.name ~= 'Wild Card' and not context.scoring_hand[i].config.center.any_suit then + if context.scoring_hand[i]:is_suit('Hearts') then suits["Hearts"] = suits["Hearts"] + 1 end + if context.scoring_hand[i]:is_suit('Diamonds') then suits["Diamonds"] = suits["Diamonds"] + 1 end + if context.scoring_hand[i]:is_suit('Spades') then suits["Spades"] = suits["Spades"] + 1 end + if context.scoring_hand[i]:is_suit('Clubs') then suits["Clubs"] = suits["Clubs"] + 1 end + end + end + for i = 1, #context.scoring_hand do + if context.scoring_hand[i].ability.name == 'Wild Card' or context.scoring_hand[i].config.center.any_suit then + if context.scoring_hand[i]:is_suit('Clubs') and suits["Clubs"] == 0 then suits["Clubs"] = suits["Clubs"] + 1 + elseif context.scoring_hand[i]:is_suit('Diamonds') and suits["Diamonds"] == 0 then suits["Diamonds"] = suits["Diamonds"] + 1 + elseif context.scoring_hand[i]:is_suit('Spades') and suits["Spades"] == 0 then suits["Spades"] = suits["Spades"] + 1 + elseif context.scoring_hand[i]:is_suit('Hearts') and suits["Hearts"] == 0 then suits["Hearts"] = suits["Hearts"] + 1 end + end + end + if (suits["Hearts"] > 0 or + suits["Diamonds"] > 0 or + suits["Spades"] > 0) and + suits["Clubs"] > 0 then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra}}, + Xmult_mod = self.ability.extra + } + end + end + if self.ability.name == 'Wee Joker' then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra.chips}}, + chip_mod = self.ability.extra.chips, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Castle' and (to_big(self.ability.extra.chips) > to_big(0)) then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra.chips}}, + chip_mod = self.ability.extra.chips, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Blue Joker' and #G.deck.cards > 0 then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra*#G.deck.cards}}, + chip_mod = self.ability.extra*#G.deck.cards, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Erosion' and (G.GAME.starting_deck_size - #G.playing_cards) > 0 then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.extra*(G.GAME.starting_deck_size - #G.playing_cards)}}, + mult_mod = self.ability.extra*(G.GAME.starting_deck_size - #G.playing_cards), + colour = G.C.MULT + } + end + if self.ability.name == 'Square Joker' then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra.chips}}, + chip_mod = self.ability.extra.chips, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Runner' then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra.chips}}, + chip_mod = self.ability.extra.chips, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Ice Cream' then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra.chips}}, + chip_mod = self.ability.extra.chips, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Stone Joker' and self.ability.stone_tally > 0 then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra*self.ability.stone_tally}}, + chip_mod = self.ability.extra*self.ability.stone_tally, + colour = G.C.CHIPS + } + end + if self.ability.name == 'Steel Joker' and self.ability.steel_tally > 0 then + return { + message = localize{type='variable',key='a_xmult',vars={1 + self.ability.extra*self.ability.steel_tally}}, + Xmult_mod = 1 + self.ability.extra*self.ability.steel_tally, + colour = G.C.MULT + } + end + if self.ability.name == 'Bull' and (G.GAME.dollars + (G.GAME.dollar_buffer or 0)) > 0 then + return { + message = localize{type='variable',key='a_chips',vars={self.ability.extra*math.max(0,(G.GAME.dollars + (G.GAME.dollar_buffer or 0))) }}, + chip_mod = self.ability.extra*math.max(0,(G.GAME.dollars + (G.GAME.dollar_buffer or 0))), + colour = G.C.CHIPS + } + end + if self.ability.name == "Driver's License" then + if (self.ability.driver_tally or 0) >= 16 then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra}}, + Xmult_mod = self.ability.extra + } + end + end + if self.ability.name == "Blackboard" then + local black_suits, all_cards = 0, 0 + for k, v in ipairs(G.hand.cards) do + all_cards = all_cards + 1 + if v:is_suit('Clubs', nil, true) or v:is_suit('Spades', nil, true) then + black_suits = black_suits + 1 + end + end + if black_suits == all_cards then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra}}, + Xmult_mod = self.ability.extra + } + end + end + if self.ability.name == "Joker Stencil" then + if (G.jokers.config.card_limit - #G.jokers.cards) > 0 then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.x_mult}}, + Xmult_mod = self.ability.x_mult + } + end + end + if self.ability.name == 'Swashbuckler' and to_big(self.ability.mult) > to_big(0) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Joker' then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Spare Trousers' and to_big(self.ability.mult) > to_big(0) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Ride the Bus' and to_big(self.ability.mult) > to_big(0) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Flash Card' and to_big(self.ability.mult) > to_big(0) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Popcorn' and to_big(self.ability.mult) > to_big(0) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Green Joker' and to_big(self.ability.mult) > to_big(0) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Fortune Teller' and G.GAME.consumeable_usage_total and G.GAME.consumeable_usage_total.tarot > 0 then + return { + message = localize{type='variable',key='a_mult',vars={G.GAME.consumeable_usage_total.tarot}}, + mult_mod = G.GAME.consumeable_usage_total.tarot + } + end + if self.ability.name == 'Gros Michel' then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.extra.mult}}, + mult_mod = self.ability.extra.mult, + } + end + if self.ability.name == 'Cavendish' then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra.Xmult}}, + Xmult_mod = self.ability.extra.Xmult, + } + end + if self.ability.name == 'Red Card' and to_big(self.ability.mult) > to_big(0) then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.mult}}, + mult_mod = self.ability.mult + } + end + if self.ability.name == 'Card Sharp' and G.GAME.hands[context.scoring_name] and G.GAME.hands[context.scoring_name].played_this_round > 1 then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.extra.Xmult}}, + Xmult_mod = self.ability.extra.Xmult, + } + end + if self.ability.name == 'Bootstraps' and math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0))/self.ability.extra.dollars) >= 1 then + return { + message = localize{type='variable',key='a_mult',vars={self.ability.extra.mult*math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0))/self.ability.extra.dollars)}}, + mult_mod = self.ability.extra.mult*math.floor((G.GAME.dollars + (G.GAME.dollar_buffer or 0))/self.ability.extra.dollars) + } + end + if self.ability.name == 'Caino' and to_big(self.ability.caino_xmult) > to_big(1) then + return { + message = localize{type='variable',key='a_xmult',vars={self.ability.caino_xmult}}, + Xmult_mod = self.ability.caino_xmult + } + end + end + end + end + end + end + +function Card:is_suit(suit, bypass_debuff, flush_calc) + if flush_calc then + if self.ability.effect == 'Stone Card' or self.config.center.no_suit then + return false + end + if (self.ability.name == 'Wild Card' or self.config.center.any_suit) and not self.debuff then + return true + end + if next(find_joker('Smeared Joker')) and (self.base.suit == 'Hearts' or self.base.suit == 'Diamonds') == (suit == 'Hearts' or suit == 'Diamonds') then + return true + end + return self.base.suit == suit + else + if self.debuff and not bypass_debuff then return end + if self.ability.effect == 'Stone Card' or self.config.center.no_suit then + return false + end + if self.ability.name == 'Wild Card' or self.config.center.any_suit then + return true + end + if next(find_joker('Smeared Joker')) and (self.base.suit == 'Hearts' or self.base.suit == 'Diamonds') == (suit == 'Hearts' or suit == 'Diamonds') then + return true + end + return self.base.suit == suit + end +end + +function Card:set_card_area(area) + self.area = area + self.parent = area + self.layered_parallax = area.layered_parallax +end + +function Card:remove_from_area() + self.area = nil + self.parent = nil + self.layered_parallax = {x = 0, y = 0} +end + +function Card:align() + if self.children.floating_sprite then + self.children.floating_sprite.T.y = self.T.y + self.children.floating_sprite.T.x = self.T.x + self.children.floating_sprite.T.r = self.T.r + end + + if self.children.focused_ui then self.children.focused_ui:set_alignment() end +end + +function Card:flip() + if self.facing == 'front' then + self.flipping = 'f2b' + self.facing='back' + self.pinch.x = true + elseif self.facing == 'back' then + self.ability.wheel_flipped = nil + self.flipping = 'b2f' + self.facing='front' + self.pinch.x = true + end +end + +function Card:update(dt) + if self.flipping == 'f2b' then + if self.sprite_facing == 'front' or true then + if self.VT.w <= 0 then + self.sprite_facing = 'back' + self.pinch.x =false + end + end + end + if self.flipping == 'b2f' then + if self.sprite_facing == 'back' or true then + if self.VT.w <= 0 then + self.sprite_facing = 'front' + self.pinch.x =false + end + end + end + + if not self.states.focus.is and self.children.focused_ui then + self.children.focused_ui:remove() + self.children.focused_ui = nil + end + + self:update_alert() + if self.ability.set == 'Joker' and not self.sticker_run then + self.sticker_run = get_joker_win_sticker(self.config.center) or 'NONE' + end + + if self.ability.consumeable and self.ability.consumeable.max_highlighted then + self.ability.consumeable.mod_num = self.ability.consumeable.max_highlighted + end + local obj = self.config.center + if obj.update and type(obj.update) == 'function' then + obj:update(self, dt) + end + local obj = G.P_SEALS[self.seal] or {} + if obj.update and type(obj.update) == 'function' then + obj:update(self, dt) + end + if G.STAGE == G.STAGES.RUN then + if self.ability and self.ability.perma_debuff then self.debuff = true end + if self.cry_debuff_immune then + self.debuff = false + end + + if self.area and ((self.area == G.jokers) or (self.area == G.consumeables)) then + self.bypass_lock = true + self.bypass_discovery_center = true + self.bypass_discovery_ui = true + end + self.sell_cost_label = (self.facing == 'back' and '?') or (G.GAME.modifiers.cry_no_sell_value and 0) or self.sell_cost + + if self.ability.name == 'Temperance' then + self.ability.money = 0 + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.set == 'Joker' then + self.ability.money = self.ability.money + G.jokers.cards[i].sell_cost + end + end + self.ability.money = math.min(self.ability.money, self.ability.extra) + end + if self.ability.name == 'Throwback' then + self.ability.x_mult = 1 + G.GAME.skips*self.ability.extra + end + if self.ability.name == "Driver's License" then + self.ability.driver_tally = 0 + for k, v in pairs(G.playing_cards) do + if v.config.center ~= G.P_CENTERS.c_base then self.ability.driver_tally = self.ability.driver_tally+1 end + end + end + if self.ability.name == "Steel Joker" then + self.ability.steel_tally = 0 + for k, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_steel then self.ability.steel_tally = self.ability.steel_tally+1 end + end + end + if self.ability.name == "Cloud 9" then + self.ability.nine_tally = 0 + for k, v in pairs(G.playing_cards) do + if v:get_id() == 9 then self.ability.nine_tally = self.ability.nine_tally+1 end + end + end + if self.ability.name == "Stone Joker" then + self.ability.stone_tally = 0 + for k, v in pairs(G.playing_cards) do + if v.config.center == G.P_CENTERS.m_stone then self.ability.stone_tally = self.ability.stone_tally+1 end + end + end + if self.ability.name == "Joker Stencil" then + self.ability.x_mult = (G.jokers.config.card_limit - #G.jokers.cards) + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.name == 'Joker Stencil' then self.ability.x_mult = self.ability.x_mult + 1 end + end + end + if self.ability.name == 'The Wheel of Fortune' then + self.eligible_strength_jokers = EMPTY(self.eligible_strength_jokers) + for k, v in pairs(G.jokers.cards) do + if v.ability.set == 'Joker' and (not v.edition) then + table.insert(self.eligible_strength_jokers, v) + end + end + end + if self.ability.name == 'Ectoplasm' or self.ability.name == 'Hex' then + self.eligible_editionless_jokers = EMPTY(self.eligible_editionless_jokers) + for k, v in pairs(G.jokers.cards) do + if v.ability.set == 'Joker' and (not v.edition) then + table.insert(self.eligible_editionless_jokers, v) + end + end + end + if self.config.center.key == 'j_cry_gemino' then + other_joker = G.jokers.cards[1] + if other_joker and other_joker ~= self and not (Card.no(other_joker, "immutable", true)) then + self.ability.blueprint_compat = 'compatible' + else + self.ability.blueprint_compat = 'incompatible' + end end + if self.ability.name == 'Blueprint' or self.ability.name == 'cry-oldblueprint' or self.ability.name == 'Brainstorm' then + local other_joker = nil + if self.ability.name == 'Brainstorm' then + other_joker = G.jokers.cards[1] + elseif self.ability.name == 'Blueprint' or self.ability.name == 'cry-oldblueprint' then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] == self then other_joker = G.jokers.cards[i+1] end + end + end + if other_joker and other_joker ~= self and other_joker.config.center.blueprint_compat then + self.ability.blueprint_compat = 'compatible' + else + self.ability.blueprint_compat = 'incompatible' + end + end + if self.ability.name == 'Swashbuckler' then + local sell_cost = 0 + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] ~= self and (G.jokers.cards[i].area and G.jokers.cards[i].area == G.jokers) then + sell_cost = sell_cost + G.jokers.cards[i].sell_cost + end + end + self.ability.mult = sell_cost + end + else + if self.ability.name == 'Temperance' then + self.ability.money = 0 + end + end +end + +function Card:hard_set_T(X, Y, W, H) + local x = (X or self.T.x) + local y = (Y or self.T.y) + local w = (W or self.T.w) + local h = (H or self.T.h) + Moveable.hard_set_T(self,x, y, w, h) + if self.children.front then self.children.front:hard_set_T(x, y, w, h) end + self.children.back:hard_set_T(x, y, w, h) + self.children.center:hard_set_T(x, y, w, h) +end + +function Card:move(dt) + Moveable.move(self, dt) + --self:align() + if self.children.h_popup then + self.children.h_popup:set_alignment(self:align_h_popup()) + end +end + +function Card:align_h_popup() + local focused_ui = self.children.focused_ui and true or false + local popup_direction = (self.children.buy_button or (self.area and self.area.config.view_deck) or (self.area and self.area.config.type == 'shop')) and 'cl' or + (self.T.y < G.CARD_H*0.8) and 'bm' or + 'tm' + return { + major = self.children.focused_ui or self, + parent = self, + xy_bond = 'Strong', + r_bond = 'Weak', + wh_bond = 'Weak', + offset = { + x = popup_direction ~= 'cl' and 0 or + focused_ui and -0.05 or + (self.ability.consumeable and 0.0) or + (self.ability.set == 'Voucher' and 0.0) or + -0.05, + y = focused_ui and ( + popup_direction == 'tm' and (self.area and self.area == G.hand and -0.08 or-0.15) or + popup_direction == 'bm' and 0.12 or + 0 + ) or + popup_direction == 'tm' and -0.13 or + popup_direction == 'bm' and 0.1 or + 0 + }, + type = popup_direction, + --lr_clamp = true + } +end + +function Card:hover() +if Handy.controller.process_card_hover(self) then return end + self:juice_up(0.05, 0.03) + play_sound('paper1', math.random()*0.2 + 0.9, 0.35) + + --if this is the focused card + if self.states.focus.is and not self.children.focused_ui then + self.children.focused_ui = G.UIDEF.card_focus_ui(self) + end + + if self.facing == 'front' and (not self.states.drag.is or G.CONTROLLER.HID.touch) and not self.no_ui and not G.debug_tooltip_toggle then + if self.children.alert and not self.config.center.alerted then + self.config.center.alerted = true + G:save_progress() + elseif self.children.alert and self.seal and not G.P_SEALS[self.seal].alerted then + G.P_SEALS[self.seal].alerted = true + G:save_progress() + end + + self.ability_UIBox_table = self:generate_UIBox_ability_table() + self.config.h_popup = G.UIDEF.card_h_popup(self) + self.config.h_popup_config = self:align_h_popup() + + Node.hover(self) + end +end + +function Card:stop_hover() + Node.stop_hover(self) +end + +function Card:juice_up(scale, rot_amount) + --G.VIBRATION = G.VIBRATION + 0.4 + local rot_amt = rot_amount and 0.4*(math.random()>0.5 and 1 or -1)*rot_amount or (math.random()>0.5 and 1 or -1)*0.16 + scale = scale and scale*0.4 or 0.11 + Moveable.juice_up(self, scale, rot_amt) +end + +function Card:draw(layer) + layer = layer or 'both' + + self.hover_tilt = 1 + + if not self.states.visible then return end + if self.VT.x < -3 or self.VT.x > G.TILE_W + 2.5 then return end + + if (layer == 'shadow' or layer == 'both') then + self.ARGS.send_to_shader = self.ARGS.send_to_shader or {} + self.ARGS.send_to_shader[1] = math.min(self.VT.r*3, 1) + math.sin(G.TIMERS.REAL/28) + 1 + (self.juice and self.juice.r*20 or 0) + self.tilt_var.amt + self.ARGS.send_to_shader[2] = G.TIMERS.REAL + + for k, v in pairs(self.children) do + v.VT.scale = self.VT.scale + end + end + + G.shared_shadow = self.sprite_facing == 'front' and self.children.center or self.children.back + + --Draw the shadow + if not self.no_shadow and G.SETTINGS.GRAPHICS.shadows == 'On' and((layer == 'shadow' or layer == 'both') and (self.ability.effect ~= 'Glass Card' and not self.greyed and self:should_draw_shadow() ) and ((self.area and self.area ~= G.discard and self.area.config.type ~= 'deck') or not self.area or self.states.drag.is)) then + self.shadow_height = 0*(0.08 + 0.4*math.sqrt(self.velocity.x^2)) + ((((self.highlighted and self.area == G.play) or self.states.drag.is) and 0.35) or (self.area and self.area.config.type == 'title_2') and 0.04 or 0.1) + G.shared_shadow:draw_shader('dissolve', self.shadow_height) + end + + if (layer == 'card' or layer == 'both') and self.area ~= G.hand then + if self.children.focused_ui then self.children.focused_ui:draw() end + end + + if (layer == 'card' or layer == 'both') then + -- for all hover/tilting: + self.tilt_var = self.tilt_var or {mx = 0, my = 0, dx = self.tilt_var.dx or 0, dy = self.tilt_var.dy or 0, amt = 0} + local tilt_factor = 0.3 + if self.states.focus.is then + self.tilt_var.mx, self.tilt_var.my = G.CONTROLLER.cursor_position.x + self.tilt_var.dx*self.T.w*G.TILESCALE*G.TILESIZE, G.CONTROLLER.cursor_position.y + self.tilt_var.dy*self.T.h*G.TILESCALE*G.TILESIZE + self.tilt_var.amt = math.abs(self.hover_offset.y + self.hover_offset.x - 1 + self.tilt_var.dx + self.tilt_var.dy - 1)*tilt_factor + elseif self.states.hover.is then + self.tilt_var.mx, self.tilt_var.my = G.CONTROLLER.cursor_position.x, G.CONTROLLER.cursor_position.y + self.tilt_var.amt = math.abs(self.hover_offset.y + self.hover_offset.x - 1)*tilt_factor + elseif self.ambient_tilt then + local tilt_angle = G.TIMERS.REAL*(1.56 + (self.ID/1.14212)%1) + self.ID/1.35122 + self.tilt_var.mx = ((0.5 + 0.5*self.ambient_tilt*math.cos(tilt_angle))*self.VT.w+self.VT.x+G.ROOM.T.x)*G.TILESIZE*G.TILESCALE + self.tilt_var.my = ((0.5 + 0.5*self.ambient_tilt*math.sin(tilt_angle))*self.VT.h+self.VT.y+G.ROOM.T.y)*G.TILESIZE*G.TILESCALE + self.tilt_var.amt = self.ambient_tilt*(0.5+math.cos(tilt_angle))*tilt_factor + end + --Any particles + if self.children.particles then self.children.particles:draw() end + + --Draw any tags/buttons + if self.children.price then self.children.price:draw() end + if self.children.buy_button then + if self.highlighted then + self.children.buy_button.states.visible = true + self.children.buy_button:draw() + if self.children.buy_and_use_button then + self.children.buy_and_use_button:draw() + end + else + self.children.buy_button.states.visible = false + end + end + if self.children.use_button and self.highlighted then self.children.use_button:draw() end + + if self.vortex then + if self.facing == 'back' then + self.children.back:draw_shader('vortex') + else + self.children.center:draw_shader('vortex') + if self.children.front then + self.children.front:draw_shader('vortex') + end + end + + love.graphics.setShader() + elseif self.sprite_facing == 'front' then + --Draw the main part of the card + if (self.edition and (self.edition.negative or self.edition.cry_oversat)) or (self.ability.name == 'Antimatter' and (self.config.center.discovered or self.bypass_discovery_center)) then + self.children.center:draw_shader('negative', nil, self.ARGS.send_to_shader) + if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then + self.children.front:draw_shader('negative', nil, self.ARGS.send_to_shader) + end + elseif not self:should_draw_base_shader() then + -- Don't render base dissolve shader. + elseif not self.greyed then + self.children.center:draw_shader('dissolve') + --If the card has a front, draw that next + if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then + self.children.front:draw_shader('dissolve') + end + end + + --If the card is not yet discovered + if self.ability.set == 'Enhanced' then self.ability.consumeable = nil end + if not self.config.center.discovered and (self.ability.consumeable or self.config.center.unlocked) and not self.config.center.demo and not self.bypass_discovery_center then + local shared_sprite = (self.ability.set == 'Edition' or self.ability.set == 'Joker') and G.shared_undiscovered_joker or G.shared_undiscovered_tarot + local scale_mod = -0.05 + 0.05*math.sin(1.8*G.TIMERS.REAL) + local rotate_mod = 0.03*math.sin(1.219*G.TIMERS.REAL) + + shared_sprite.role.draw_major = self + if (self.config.center.undiscovered and not self.config.center.undiscovered.no_overlay) or not( SMODS.UndiscoveredSprites[self.ability.set] and SMODS.UndiscoveredSprites[self.ability.set].no_overlay) then + shared_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod) + else + end + end + + if self.ability.name == 'Invisible Joker' and (self.config.center.discovered or self.bypass_discovery_center) then + if self:should_draw_base_shader() then + self.children.center:draw_shader('voucher', nil, self.ARGS.send_to_shader) + end + end + + local center = self.config.center + if center.draw and type(center.draw) == 'function' then + center:draw(self, layer) + end + if center.set == 'Default' or center.set == 'Enhanced' and not center.replace_base_card then + if not center.no_suit then + local suit = SMODS.Suits[self.base.suit] or {} + if suit.draw and type(suit.draw) == 'function' then + suit:draw(self, layer) + end + end + if not center.no_rank then + local rank = SMODS.Ranks[self.base.value] or {} + if rank.draw and type(rank.draw) == 'function' then + rank:draw(self, layer) + end + end + end + --If the card has any edition/seal, add that here + if true then + + if (self.ability.set == 'Voucher' or self.config.center.demo) and (self.ability.name ~= 'Antimatter' or not (self.config.center.discovered or self.bypass_discovery_center)) then + if self:should_draw_base_shader() then + self.children.center:draw_shader('voucher', nil, self.ARGS.send_to_shader) + end + end + if (self.ability.set == 'Booster' or self.ability.set == 'Spectral') and self:should_draw_base_shader() then + self.children.center:draw_shader('booster', nil, self.ARGS.send_to_shader) + end + if self.edition then + for k, v in pairs(G.P_CENTER_POOLS.Edition) do + if self.edition[v.key:sub(3)] and v.shader then + if type(v.draw) == 'function' then + v:draw(self, layer) + else + self.children.center:draw_shader(v.shader, nil, self.ARGS.send_to_shader) + if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then + self.children.front:draw_shader(v.shader, nil, self.ARGS.send_to_shader) + end + end + end + end + end + if (self.edition and (self.edition.negative or self.edition.cry_oversat)) or (self.ability.name == 'Antimatter' and (self.config.center.discovered or self.bypass_discovery_center)) then + self.children.center:draw_shader('negative_shine', nil, self.ARGS.send_to_shader) + end + local seal = G.P_SEALS[self.seal or {}] or {} + if type(seal.draw) == 'function' then + seal:draw(self, layer) + elseif self.seal then + G.shared_seals[self.seal].role.draw_major = self + G.shared_seals[self.seal]:draw_shader('dissolve', nil, nil, nil, self.children.center) + if self.seal == 'Gold' then G.shared_seals[self.seal]:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) end + end + if self.ability.eternal then + G.shared_sticker_eternal.role.draw_major = self + G.shared_sticker_eternal:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_sticker_eternal:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) + end + if self.ability.perishable and not layer then + G.shared_sticker_perishable.role.draw_major = self + G.shared_sticker_perishable:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_sticker_perishable:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) + end + if self.ability.rental then + G.shared_sticker_rental.role.draw_major = self + G.shared_sticker_rental:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_sticker_rental:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) + end + if self.sticker and G.shared_stickers[self.sticker] then + G.shared_stickers[self.sticker].role.draw_major = self + G.shared_stickers[self.sticker]:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_stickers[self.sticker]:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) + elseif (self.sticker_run and G.shared_stickers[self.sticker_run]) and G.SETTINGS.run_stake_stickers then + G.shared_stickers[self.sticker_run].role.draw_major = self + G.shared_stickers[self.sticker_run]:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_stickers[self.sticker_run]:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) + end + + if self.ability.name == 'The Soul' and (self.config.center.discovered or self.bypass_discovery_center) then + local scale_mod = 0.05 + 0.05*math.sin(1.8*G.TIMERS.REAL) + 0.07*math.sin((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3 + local rotate_mod = 0.1*math.sin(1.219*G.TIMERS.REAL) + 0.07*math.sin((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2 + + G.shared_soul.role.draw_major = self + G.shared_soul:draw_shader('dissolve',0, nil, nil, self.children.center,scale_mod, rotate_mod,nil, 0.1 + 0.03*math.sin(1.8*G.TIMERS.REAL),nil, 0.6) + G.shared_soul:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod) + end + + if self.ability.name == 'cry-Gateway' and (self.config.center.discovered or self.bypass_discovery_center) then + local scale_mod2 = 0.07-- + 0.02*math.cos(1.8*G.TIMERS.REAL) + 0.00*math.cos((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3 + local rotate_mod2 = 0--0.05*math.cos(1.219*G.TIMERS.REAL) + 0.00*math.cos((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2 + self.children.floating_sprite2:draw_shader('dissolve',0, nil, nil, self.children.center,scale_mod2, rotate_mod2,nil, 0.1--[[ + 0.03*math.cos(1.8*G.TIMERS.REAL)--]],nil, 0.6) + self.children.floating_sprite2:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod2, rotate_mod2) + + local scale_mod = 0.05 + 0.05*math.sin(1.8*G.TIMERS.REAL) + 0.07*math.sin((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3 + local rotate_mod = 0.1*math.sin(1.219*G.TIMERS.REAL) + 0.07*math.sin((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2 + + self.children.floating_sprite.role.draw_major = self + self.children.floating_sprite:draw_shader('dissolve',0, nil, nil, self.children.center,scale_mod, rotate_mod,nil, 0.1 + 0.03*math.sin(1.8*G.TIMERS.REAL),nil, 0.6) + self.children.floating_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod) + if self.edition then + for k, v in pairs(G.P_CENTER_POOLS.Edition) do + if v.apply_to_float then + if self.edition[v.key:sub(3)] then + self.children.floating_sprite:draw_shader(v.shader, nil, nil, nil, self.children.center, scale_mod, rotate_mod) + end + end + end + end + + end + if self.config.center.soul_pos and (self.config.center.discovered or self.bypass_discovery_center) then + if self.config.center.soul_pos and self.config.center.soul_pos.extra and (self.config.center.discovered or self.bypass_discovery_center) then + local scale_mod = 0.07-- + 0.02*math.cos(1.8*G.TIMERS.REAL) + 0.00*math.cos((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3 + local rotate_mod = 0--0.05*math.cos(1.219*G.TIMERS.REAL) + 0.00*math.cos((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2 + self.children.floating_sprite2:draw_shader('dissolve',0, nil, nil, self.children.center,scale_mod, rotate_mod,nil, 0.1--[[ + 0.03*math.cos(1.8*G.TIMERS.REAL)--]],nil, 0.6) + self.children.floating_sprite2:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod) + end + local scale_mod = 0.07 + 0.02*math.sin(1.8*G.TIMERS.REAL) + 0.00*math.sin((G.TIMERS.REAL - math.floor(G.TIMERS.REAL))*math.pi*14)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^3 + local rotate_mod = 0.05*math.sin(1.219*G.TIMERS.REAL) + 0.00*math.sin((G.TIMERS.REAL)*math.pi*5)*(1 - (G.TIMERS.REAL - math.floor(G.TIMERS.REAL)))^2 + + if self.ability.name == 'Hologram' then + self.hover_tilt = self.hover_tilt*1.5 + self.children.floating_sprite:draw_shader('hologram', nil, self.ARGS.send_to_shader, nil, self.children.center, 2*scale_mod, 2*rotate_mod) + self.hover_tilt = self.hover_tilt/1.5 + else + self.children.floating_sprite:draw_shader('dissolve',0, nil, nil, self.children.center,scale_mod, rotate_mod,nil, 0.1 + 0.03*math.sin(1.8*G.TIMERS.REAL),nil, 0.6) + self.children.floating_sprite:draw_shader('dissolve', nil, nil, nil, self.children.center, scale_mod, rotate_mod) + if self.edition then + for k, v in pairs(G.P_CENTER_POOLS.Edition) do + if v.apply_to_float then + if self.edition[v.key:sub(3)] then + self.children.floating_sprite:draw_shader(v.shader, nil, nil, nil, self.children.center, scale_mod, rotate_mod) + end + end + end + end + end + + end + if self.debuff then + self.children.center:draw_shader('debuff', nil, self.ARGS.send_to_shader) + if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then + self.children.front:draw_shader('debuff', nil, self.ARGS.send_to_shader) + end + end + if self.greyed then + self.children.center:draw_shader('played', nil, self.ARGS.send_to_shader) + if self.children.front and self.ability.effect ~= 'Stone Card' and not self.config.center.replace_base_card then + self.children.front:draw_shader('played', nil, self.ARGS.send_to_shader) + end + end + end + if self.pinned then + G.shared_stickers['pinned'].role.draw_major = self + G.shared_stickers['pinned']:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_stickers['pinned']:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) + end + for k, v in pairs(SMODS.Stickers) do + if self.ability[v.key] then + if v and v.draw and type(v.draw) == 'function' then + v:draw(self, layer) + else + G.shared_stickers[v.key].role.draw_major = self + G.shared_stickers[v.key]:draw_shader('dissolve', nil, nil, nil, self.children.center) + G.shared_stickers[v.key]:draw_shader('voucher', nil, self.ARGS.send_to_shader, nil, self.children.center) + end + end + end + elseif self.sprite_facing == 'back' then + local overlay = G.C.WHITE + if self.area and self.area.config.type == 'deck' and self.rank > 3 then + self.back_overlay = self.back_overlay or {} + self.back_overlay[1] = 0.5 + ((#self.area.cards - self.rank)%7)/50 + self.back_overlay[2] = 0.5 + ((#self.area.cards - self.rank)%7)/50 + self.back_overlay[3] = 0.5 + ((#self.area.cards - self.rank)%7)/50 + self.back_overlay[4] = 1 + overlay = self.back_overlay + end + + if self.area and self.area.config.type == 'deck' then + self.children.back:draw(overlay) + local currentBack = self.params.viewed_back and G.GAME.viewed_back or G.GAME.selected_back + if currentBack.effect.config.cry_force_edition and not currentBack.effect.config.cry_antimatter then + if currentBack.effect.config.cry_force_edition_shader then + self.children.back:draw_shader(currentBack.effect.config.cry_force_edition_shader , nil, self.ARGS.send_to_shader, true) + else + self.children.back:draw_shader(currentBack.effect.config.cry_force_edition , nil, self.ARGS.send_to_shader, true) + end + end + if currentBack.effect.config.cry_force_seal and not currentBack.effect.config.hide_seal and not currentBack.effect.config.cry_antimatter then + G.shared_seals[currentBack.effect.config.cry_force_seal]:draw_shader('dissolve', nil, nil, true, self.children.center) + if currentBack.effect.config.cry_force_seal == 'Gold' then G.shared_seals[currentBack.effect.config.cry_force_seal]:draw_shader('voucher', nil, self.ARGS.send_to_shader, true, self.children.center) end + end + if currentBack.effect.config.cry_force_sticker and not currentBack.effect.config.cry_antimatter then + for k, v in pairs(SMODS.Stickers) do + if currentBack.effect.config.cry_force_sticker == v.key then + if v and v.draw and type(v.draw) == 'function' then + v:draw(self) + else + G.shared_stickers[v.key].role.draw_major = self + G.shared_stickers[v.key]:draw_shader('dissolve', nil, nil, true, self.children.center) + G.shared_stickers[v.key]:draw_shader('voucher', nil, self.ARGS.send_to_shader, true, self.children.center) + end + end + end + end + if currentBack.effect.config.cry_antimatter or currentBack.effect.config.cry_force_edition == 'negative' then + self.children.back:draw_shader('negative', nil, self.ARGS.send_to_shader, true) + self.children.center:draw_shader('negative_shine', nil, self.ARGS.send_to_shader, true) + end + else + local currentBack = self.params.viewed_back and G.GAME.viewed_back or G.GAME.selected_back + if currentBack and currentBack.effect.config.cry_antimatter or currentBack.effect.config.cry_force_edition == 'negative' then + self.children.back:draw_shader('negative', nil, self.ARGS.send_to_shader) + self.children.center:draw_shader('negative_shine', nil, self.ARGS.send_to_shader) + else + self.children.back:draw_shader('dissolve') + end + end + + if self.sticker and G.shared_stickers[self.sticker] then + G.shared_stickers[self.sticker].role.draw_major = self + G.shared_stickers[self.sticker]:draw_shader('dissolve', nil, nil, true, self.children.center) + if self.sticker == 'Gold' then G.shared_stickers[self.sticker]:draw_shader('voucher', nil, self.ARGS.send_to_shader, true, self.children.center) end + end + end + + for k, v in pairs(self.children) do + if k ~= 'focused_ui' and k ~= "front" and k ~= "back" and k ~= "soul_parts" and k ~= "center" and k ~= 'floating_sprite' and k ~= 'floating_sprite2' and k~= "shadow" and k~= "use_button" and k ~= 'buy_button' and k ~= 'buy_and_use_button' and k~= "debuff" and k ~= 'price' and k~= 'particles' and k ~= 'h_popup' then v:draw() end + end + + if (layer == 'card' or layer == 'both') and self.area == G.hand then + if self.children.focused_ui then self.children.focused_ui:draw() end + end + + add_to_drawhash(self) + self:draw_boundingrect() + end +end + +function Card:release(dragged) + if dragged:is(Card) and self.area then + self.area:release(dragged) + end +end + +function Card:highlight(is_higlighted) + self.highlighted = is_higlighted + if true then + if self.highlighted and self.area and self.area.config.type ~= 'shop' then + local x_off = (self.ability.consumeable and -0.1 or 0) + self.children.use_button = UIBox{ + definition = G.UIDEF.use_and_sell_buttons(self), + config = {align= + ((self.area == G.jokers) or (self.area == G.consumeables)) and "cr" or + "bmi" + , offset = + ((self.area == G.jokers) or (self.area == G.consumeables)) and {x=x_off - 0.4,y=0} or + {x=0,y=0.65}, + parent =self} + } + elseif self.children.use_button then + self.children.use_button:remove() + self.children.use_button = nil + end + end + if self.ability.consumeable or self.ability.set == 'Joker' then + if not self.highlighted and self.area and self.area.config.type == 'joker' and + (#G.jokers.cards >= G.jokers.config.card_limit or (self.edition and self.edition.negative)) then + if G.shop_jokers then G.shop_jokers:unhighlight_all() end + end + end +end + +function Card:click() +if Handy.controller.process_card_click(self) then return end + if self.area and self.area:can_highlight(self) then + if (self.area == G.hand) and (G.STATE == G.STATES.HAND_PLAYED) then return end + if self.highlighted ~= true then + self.area:add_to_highlighted(self) + else + self.area:remove_from_highlighted(self) + play_sound('cardSlide2', nil, 0.3) + end + end + if self.area and self.area == G.deck and self.area.cards[1] == self then + G.FUNCS.deck_info() + end +end + +function Card:save() + cardTable = { + sort_id = self.sort_id, + save_fields = { + center = self.config.center_key, + card = self.config.card_key, + }, + params = self.params, + no_ui = self.no_ui, + base_cost = self.base_cost, + extra_cost = self.extra_cost, + cost = self.cost, + sell_cost = self.sell_cost, + facing = self.facing, + sprite_facing = self.facing, + flipping = nil, + highlighted = self.highligted, + debuff = self.debuff, + rank = self.rank, + added_to_deck = self.added_to_deck, + joker_added_to_deck_but_debuffed = self.joker_added_to_deck_but_debuffed, + label = self.label, + playing_card = self.playing_card, + base = self.base, + ability = self.ability, + pinned = self.pinned, + edition = self.edition, + seal = self.seal, + bypass_discovery_center = self.bypass_discovery_center, + bypass_discovery_ui = self.bypass_discovery_ui, + bypass_lock = self.bypass_lock, + unique_val = self.unique_val, + unique_val__saved_ID = self.ID, + ignore_base_shader = self.ignore_base_shader, + ignore_shadow = self.ignore_shadow, + } + return cardTable +end + +function Card:load(cardTable, other_card) + local scale = 1 + self.config = {} + self.config.center_key = cardTable.save_fields.center + self.config.center = G.P_CENTERS[self.config.center_key] + self.params = cardTable.params + self.sticker_run = nil + + local H = G.CARD_H + local W = G.CARD_W + local obj = self.config.center + if obj.load and type(obj.load) == 'function' then + obj:load(self, cardTable, other_card) + elseif self.config.center.name == "Half Joker" then + self.T.h = H*scale/1.7*scale + self.T.w = W*scale + elseif self.config.center.name == "cry-Wee Fibonacci" or self.config.center.name == "cry-reverse" then + self.T.h = H*scale*0.7*scale + self.T.w = W*scale*0.7*scale + elseif self.config.center.name == "cry-biggestm" then + self.T.h = H*scale*1.7*scale + self.T.w = W*scale*1.7*scale + elseif self.config.center.name == "cry-Booster Joker" then + self.T.h = H*scale*1.17*scale + self.T.w = W*scale*1.17*scale + elseif self.config.center.name == "cry-Cube" then + self.T.h = H*scale*0.1*scale + self.T.w = W*scale*0.1*scale + elseif self.config.center.name == "cry-Jimball" then + H = W + self.T.h = H*scale*57/69*scale + self.T.w = W*scale*57/69*scale + elseif self.config.center.name == "cry-Potion" then + H = W + self.T.h = H*scale*35/69*scale + self.T.w = W*scale*35/69*scale + elseif self.config.center.name == "cry-magnet" then + H = W + self.T.h = H*scale*35/71*scale + self.T.w = W*scale*35/71*scale + elseif self.config.center.name == "Wee Joker" then + self.T.h = H*scale*0.7*scale + self.T.w = W*scale*0.7*scale + elseif self.config.center.name == "Photograph" then + self.T.h = H*scale/1.2*scale + self.T.w = W*scale + elseif self.config.center.name == "Square Joker" then + H = W + self.T.h = H*scale + self.T.w = W*scale + elseif self.config.center.set == 'Booster' then + self.T.h = H*1.27 + self.T.w = W*1.27 + else + self.T.h = H*scale + self.T.w = W*scale + end + self.VT.h = self.T.H + self.VT.w = self.T.w + + self.config.card_key = cardTable.save_fields.card + self.config.card = G.P_CARDS[self.config.card_key] + + self.no_ui = cardTable.no_ui + self.base_cost = cardTable.base_cost + self.extra_cost = cardTable.extra_cost + self.cost = cardTable.cost + self.sell_cost = cardTable.sell_cost + self.facing = cardTable.facing + self.sprite_facing = cardTable.sprite_facing + self.flipping = cardTable.flipping + self.highlighted = cardTable.highlighted + self.debuff = cardTable.debuff + self.rank = cardTable.rank + self.added_to_deck = cardTable.added_to_deck + self.label = cardTable.label + self.playing_card = cardTable.playing_card + self.base = cardTable.base + self.sort_id = cardTable.sort_id + self.bypass_discovery_center = cardTable.bypass_discovery_center + self.bypass_discovery_ui = cardTable.bypass_discovery_ui + self.bypass_lock = cardTable.bypass_lock + self.unique_val = cardTable.unique_val or self.unique_val + if cardTable.unique_val__saved_ID and G.ID <= cardTable.unique_val__saved_ID then + G.ID = cardTable.unique_val__saved_ID + 1 + end + + self.ignore_base_shader = cardTable.ignore_base_shader or {} + self.ignore_shadow = cardTable.ignore_shadow or {} + + self.ability = cardTable.ability + self.pinned = cardTable.pinned + self.edition = cardTable.edition + self.seal = cardTable.seal + + remove_all(self.children) + self.children = {} + self.children.shadow = Moveable(0, 0, 0, 0) + + self:set_sprites(self.config.center, self.config.card) +end + +function Card:remove() + self.removed = true + + if self.area then self.area:remove_card(self) end + + self:remove_from_deck() + if self.ability.consumeable and self.pinned and (G.GAME.cry_pinned_consumeables > 0) then + G.GAME.cry_pinned_consumeables = G.GAME.cry_pinned_consumeables - 1 + end + if self.ability.joker_added_to_deck_but_debuffed then + if self.edition and self.edition.card_limit then + if self.ability.consumeable then + G.consumeables.config.card_limit = G.consumeables.config.card_limit - self.edition.card_limit + elseif self.ability.set == 'Joker' then + G.jokers.config.card_limit = G.jokers.config.card_limit - self.edition.card_limit + end + end + end + + if not G.OVERLAY_MENU then + if not next(SMODS.find_card(self.config.center.key, true)) then + G.GAME.used_jokers[self.config.center.key] = nil + end + end + + if G.playing_cards then + for k, v in ipairs(G.playing_cards) do + if v == self then + table.remove(G.playing_cards, k) + break + end + end + for k, v in ipairs(G.playing_cards) do + v.playing_card = k + end + end + + remove_all(self.children) + + for k, v in pairs(G.I.CARD) do + if v == self then + table.remove(G.I.CARD, k) + end + end + Moveable.remove(self) +end diff --git a/lovely/dump/cardarea.lua b/lovely/dump/cardarea.lua new file mode 100644 index 0000000..a72a292 --- /dev/null +++ b/lovely/dump/cardarea.lua @@ -0,0 +1,727 @@ +LOVELY_INTEGRITY = 'a7d11253f7999a0561fd7f99f774f750e8b3702d5f496f16eaf5274a4813bd49' + +--Class +CardArea = Moveable:extend() + +--Class Methods +function CardArea:init(X, Y, W, H, config) + Moveable.init(self, X, Y, W, H) + + self.states.drag.can = false + self.states.hover.can = false + self.states.click.can = false + + + self.config = config or {} + self.card_w = config.card_w or G.CARD_W + self.cards = {} + self.children = {} + self.highlighted = {} + self.config.highlighted_limit = config.highlight_limit or G.GAME.modifiers.cry_highlight_limit or 5 + self.config.card_limit = config.card_limit or 52 + self.config.temp_limit = self.config.card_limit + self.config.card_count = 0 + self.config.type = config.type or 'deck' + self.config.sort = config.sort or 'desc' + self.config.lr_padding = config.lr_padding or 0.1 + self.shuffle_amt = 0 + + if getmetatable(self) == CardArea then + table.insert(G.I.CARDAREA, self) + end +end + +function CardArea:emplace(card, location, stay_flipped) +if self == G.jokers then + Cartomancer.handle_joker_added(card) +end + if card.edition and card.edition.card_limit and (self == G.hand) then + self.config.real_card_limit = (self.config.real_card_limit or self.config.card_limit) + card.edition.card_limit + self.config.card_limit = math.max(0, self.config.real_card_limit) + end + if location == 'front' or self.config.type == 'deck' then + table.insert(self.cards, 1, card) + else + self.cards[#self.cards+1] = card + end + if card.cry_flipped then card.facing = 'back'; card.sprite_facing = 'back' end + if not (card.cry_flipped and (self == G.shop_jokers or self == G.shop_vouchers or self == G.shop_booster)) and card.facing == 'back' and self.config.type ~= 'discard' and self.config.type ~= 'deck' and not stay_flipped then + card:flip() + end + if self == G.hand and stay_flipped then + card.ability.wheel_flipped = true + end + + if #self.cards > self.config.card_limit then + if self == G.deck then + self.config.card_limit = #self.cards + end + end + + card:set_card_area(self) + self:set_ranks() + self:align_cards() + + if self == G.jokers then + local joker_tally = 0 + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.set == 'Joker' then joker_tally = joker_tally + 1 end + end + if joker_tally > G.GAME.max_jokers then G.GAME.max_jokers = joker_tally end + check_for_unlock({type = 'modify_jokers'}) + end + if self == G.deck then check_for_unlock({type = 'modify_deck', deck = self}) end +end + +function CardArea:remove_card(card, discarded_only) + if not self.cards then return end + local _cards = discarded_only and {} or self.cards + if discarded_only then + for k, v in ipairs(self.cards) do + if v.ability and v.ability.discarded then + _cards[#_cards+1] = v + end + end + end + if self.config.type == 'discard' or self.config.type == 'deck' then + card = card or _cards[#_cards] + else + card = card or _cards[1] + end + for i = #self.cards,1,-1 do + if self.cards[i] == card then + if card.edition and card.edition.card_limit and (self == G.hand) then + self.config.real_card_limit = (self.config.real_card_limit or self.config.card_limit) - card.edition.card_limit + self.config.card_limit = math.max(0, self.config.real_card_limit) + end + card:remove_from_area() + table.remove(self.cards, i) + self:remove_from_highlighted(card, true) + break + end + end + self:set_ranks() + if self == G.deck then check_for_unlock({type = 'modify_deck', deck = self}) end + return card +end + +function CardArea:change_size(delta) + if delta ~= 0 then + G.E_MANAGER:add_event(Event({ + func = function() + self.config.real_card_limit = (self.config.real_card_limit or self.config.card_limit) + delta + self.config.card_limit = math.max(0, self.config.real_card_limit) + if delta > 0 and self.config.real_card_limit > 1 and self == G.hand and self.cards[1] and (G.STATE == G.STATES.DRAW_TO_HAND or G.STATE == G.STATES.SELECTING_HAND) then + local card_count = math.abs(delta) + for i=1, card_count do + draw_card(G.deck,G.hand, i*100/card_count,nil, nil , nil, 0.07) + G.E_MANAGER:add_event(Event({func = function() self:sort() return true end})) + end + end + if self == G.hand then check_for_unlock({type = 'min_hand_size'}) end + return true + end})) + end +end + +function CardArea:can_highlight(card) + if G.CONTROLLER.HID.controller then + if self.config.type == 'hand' + then + return true + end + else + if self.config.type == 'hand' or + self.config.type == 'joker' or + self.config.type == 'consumeable' or + (self.config.type == 'shop' and self.config.highlighted_limit > 0) + then + return true + end + end + return false +end + +function CardArea:add_to_highlighted(card, silent) + --if self.config.highlighted_limit <= #self.highlighted then return end + if self.config.type == 'shop' then + if self.highlighted[1] then + self:remove_from_highlighted(self.highlighted[1]) + end + --if not G.FUNCS.check_for_buy_space(card) then return false end + self.highlighted[#self.highlighted+1] = card + card:highlight(true) + if not silent then play_sound('cardSlide1') end + elseif self.config.type == 'joker' or self.config.type == 'consumeable' then + if #self.highlighted >= self.config.highlighted_limit then + self:remove_from_highlighted(self.highlighted[1]) + end + self.highlighted[#self.highlighted+1] = card + card:highlight(true) + if not silent then play_sound('cardSlide1') end + else + if #self.highlighted >= self.config.highlighted_limit then + card:highlight(false) + else + self.highlighted[#self.highlighted+1] = card + card:highlight(true) + if not silent then play_sound('cardSlide1') end + end + if self == G.hand and G.STATE == G.STATES.SELECTING_HAND then + self:parse_highlighted() + end + end +end + +function CardArea:parse_highlighted() + G.boss_throw_hand = nil + local text,disp_text,poker_hands = G.FUNCS.get_poker_hand_info(self.highlighted) + if text == 'NULL' then + update_hand_text({immediate = true, nopulse = true, delay = 0}, {mult = 0, chips = 0, level = '', handname = ''}) + else + if G.GAME.blind and G.GAME.blind:debuff_hand(self.highlighted, poker_hands, text, true) then + G.boss_throw_hand = true + else + + end + local backwards = nil + for k, v in pairs(self.highlighted) do + if v.facing == 'back' then + backwards = true + end + end + if backwards then + update_hand_text({immediate = true, nopulse = nil, delay = 0}, {handname='????', level='?', mult = '?', chips = '?'}) + else + update_hand_text({immediate = true, nopulse = nil, delay = 0}, {handname=disp_text, level=G.GAME.hands[text].level, mult = cry_ascend(G.GAME.hands[text].mult), chips = cry_ascend(G.GAME.hands[text].chips)}) + end + end +end + +function CardArea:remove_from_highlighted(card, force) + if (not force) and card and card.ability.forced_selection and self == G.hand then return end + for i = #self.highlighted,1,-1 do + if self.highlighted[i] == card then + table.remove(self.highlighted, i) + break + end + end + card:highlight(false) + if self == G.hand and G.STATE == G.STATES.SELECTING_HAND then + self:parse_highlighted() + end +end + +function CardArea:unhighlight_all() + for i = #self.highlighted,1,-1 do + if self.highlighted[i].ability.forced_selection and self == G.hand then + else + self.highlighted[i]:highlight(false) + table.remove(self.highlighted, i) + end + end + if self == G.hand and G.STATE == G.STATES.SELECTING_HAND then + self:parse_highlighted() + end +end + +function CardArea:set_ranks() + for k, card in ipairs(self.cards) do + card.rank = k + card.states.collide.can = true + if k > 1 and self.config.type == 'deck' then + card.states.drag.can = false + card.states.collide.can = false + elseif self.config.type == 'play' or self.config.type == 'shop' or self.config.type == 'consumeable' then + card.states.drag.can = false + else + card.states.drag.can = true + end + end +end + +function CardArea:move(dt) + --Set sliding up/down for the hand area + if self == G.hand then + local desired_y = G.TILE_H - G.hand.T.h - 1.9*((not G.deck_preview and (G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.DRAW_TO_HAND)) and 1 or 0) + G.hand.T.y = 15*G.real_dt*desired_y + (1-15*G.real_dt)*G.hand.T.y + if math.abs(desired_y - G.hand.T.y) < 0.01 then G.hand.T.y = desired_y end + if G.STATE == G.STATES.TUTORIAL then + G.play.T.y = G.hand.T.y - (3 + 0.6) + end + end + Moveable.move(self, dt) + self:align_cards() +end + +function CardArea:update(dt) + if self == G.hand then + if G.GAME.modifiers.minus_hand_size_per_X_dollar then + self.config.last_poll_size = self.config.last_poll_size or 0 + if math.floor(G.GAME.dollars/G.GAME.modifiers.minus_hand_size_per_X_dollar) ~= self.config.last_poll_size then + self:change_size(self.config.last_poll_size - math.floor(G.GAME.dollars/G.GAME.modifiers.minus_hand_size_per_X_dollar)) + self.config.last_poll_size = math.floor(G.GAME.dollars/G.GAME.modifiers.minus_hand_size_per_X_dollar) + end + end + for k, v in pairs(self.cards) do + if v.ability.forced_selection and not self.highlighted[1] then + self:add_to_highlighted(v) + end + end + end + if self == G.deck then + self.states.collide.can = true + self.states.hover.can = true + self.states.click.can = true + end + --Check and see if controller is being used + if G.CONTROLLER.HID.controller and self ~= G.hand then self:unhighlight_all() end + if self == G.deck and self.config.card_limit > #G.playing_cards then self.config.card_limit = #G.playing_cards end + self.config.temp_limit = math.max(#self.cards, self.config.card_limit) + self.config.card_count = #self.cards +end + +function CardArea:draw() + if not self.states.visible then return end + if G.VIEWING_DECK and (self==G.deck or self==G.hand or self==G.play) then return end + + local state = G.TAROT_INTERRUPT or G.STATE + + self.ARGS.invisible_area_types = self.ARGS.invisible_area_types or {discard=1, voucher=1, play=1, consumeable=1, title = 1, title_2 = 1} + if self.ARGS.invisible_area_types[self.config.type] or + (self.config.type == 'hand' and ({[G.STATES.SHOP]=1, [G.STATES.TAROT_PACK]=1, [G.STATES.SPECTRAL_PACK]=1, [G.STATES.STANDARD_PACK]=1,[G.STATES.BUFFOON_PACK]=1,[G.STATES.PLANET_PACK]=1, [G.STATES.ROUND_EVAL]=1, [G.STATES.BLIND_SELECT]=1})[state]) or + (self.config.type == 'hand' and state == G.STATES.SMODS_BOOSTER_OPENED) or + (self.config.type == 'deck' and self ~= G.deck) or + (self.config.type == 'shop' and self ~= G.shop_vouchers) then + else + if not self.children.area_uibox then + local card_count = self ~= G.shop_vouchers and {n=G.UIT.R, config={align = self == G.jokers and 'cl' or self == G.hand and 'cm' or 'cr', padding = 0.03, no_fill = true}, nodes={ + {n=G.UIT.B, config={w = 0.1,h=0.1}}, + {n=G.UIT.T, config={ref_table = self.config, ref_value = 'card_count', scale = 0.3, colour = G.C.WHITE}}, + {n=G.UIT.T, config={text = '/', scale = 0.3, colour = G.C.WHITE}}, + {n=G.UIT.T, config={ref_table = self.config, ref_value = 'card_limit', scale = 0.3, colour = G.C.WHITE}}, + {n=G.UIT.B, config={w = 0.1,h=0.1}} + }} or nil + + self.children.area_uibox = UIBox{ + definition = + {n=G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={minw = self.T.w,minh = self.T.h,align = "cm", padding = 0.1, mid = true, r = 0.1, colour = self ~= G.shop_vouchers and {0,0,0,0.1} or nil, ref_table = self}, nodes={ + self == G.shop_vouchers and + {n=G.UIT.C, config={align = "cm", paddin = 0.1, func = 'shop_voucher_empty', visible = false}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = G.GAME.modifiers.cry_no_vouchers and (very_fair_quip[1] or '') or 'DEFEAT', scale = 0.6, colour = G.C.WHITE}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = G.GAME.modifiers.cry_no_vouchers and (very_fair_quip[2] or '') or G.GAME.modifiers.cry_voucher_restock_antes and G.GAME.round_resets.ante % G.GAME.modifiers.cry_voucher_restock_antes == 0 and 'TWO BOSS BLINDS' or 'BOSS BLIND', scale = 0.4, colour = G.C.WHITE}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = G.GAME.modifiers.cry_no_vouchers and (very_fair_quip[3] or '') or 'TO RESTOCK', scale = 0.4, colour = G.C.WHITE}} + }}, + }} or nil, + }}, + card_count + }}, + config = { align = 'cm', offset = {x=0,y=0}, major = self, parent = self} + } + end + self.children.area_uibox:draw() + if self == G.jokers then + Cartomancer.add_visibility_controls() + end + end + + self:draw_boundingrect() + add_to_drawhash(self) + + self.ARGS.draw_layers = self.ARGS.draw_layers or self.config.draw_layers or {'shadow', 'card'} + for k, v in ipairs(self.ARGS.draw_layers) do + if self.config.type == 'deck' then + for i = #self.cards, 1, -1 do + if self.cards[i] ~= G.CONTROLLER.focused.target then + if i == 1 or i%(self.config.thin_draw or 9) == 0 or i == #self.cards or math.abs(self.cards[i].VT.x - self.T.x) > 1 or math.abs(self.cards[i].VT.y - self.T.y) > 1 then + if G.CONTROLLER.dragging.target ~= self.cards[i] then self.cards[i]:draw(v) end + end + end + end + end + + if self.config.type == 'joker' or self.config.type == 'consumeable' or self.config.type == 'shop' or self.config.type == 'title_2' then + for i = 1, #self.cards do + if self.cards[i] ~= G.CONTROLLER.focused.target then + if not self.cards[i].highlighted then + if G.CONTROLLER.dragging.target ~= self.cards[i] then self.cards[i]:draw(v) end + end + end + end + for i = 1, #self.cards do + if self.cards[i] ~= G.CONTROLLER.focused.target then + if self.cards[i].highlighted then + if G.CONTROLLER.dragging.target ~= self.cards[i] then self.cards[i]:draw(v) end + end + end + end + end + + if self.config.type == 'discard' then + for i = 1, #self.cards do + if self.cards[i] ~= G.CONTROLLER.focused.target then + if math.abs(self.cards[i].VT.x - self.T.x) > 1 then + if G.CONTROLLER.dragging.target ~= self.cards[i] then self.cards[i]:draw(v) end + end + end + end + end + + if self.config.type == 'hand' or self.config.type == 'play' or self.config.type == 'title' or self.config.type == 'voucher' then + for i = 1, #self.cards do + if self.cards[i] ~= G.CONTROLLER.focused.target or self == G.hand then + if G.CONTROLLER.dragging.target ~= self.cards[i] then self.cards[i]:draw(v) end + end + end + end + end + + if self == G.deck then + if G.CONTROLLER.HID.controller and G.STATE == G.STATES.SELECTING_HAND and not self.children.peek_deck then + self.children.peek_deck = UIBox{ + definition = + {n=G.UIT.ROOT, config = {align = 'cm', padding = 0.1, r =0.1, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", r =0.1, colour = adjust_alpha(G.C.L_BLACK, 0.5),func = 'set_button_pip', focus_args = {button = 'triggerleft', orientation = 'bm', scale = 0.6, type = 'none'}}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = 'PEEK', scale = 0.48, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = 'DECK', scale = 0.38, colour = G.C.WHITE, shadow = true}} + }}, + }}, + }}, + config = { align = 'cl', offset = {x=-0.5,y=0.1}, major = self, parent = self} + } + self.children.peek_deck.states.collide.can = false + elseif (not G.CONTROLLER.HID.controller or G.STATE ~= G.STATES.SELECTING_HAND) and self.children.peek_deck then + self.children.peek_deck:remove() + self.children.peek_deck = nil + end + if not self.children.view_deck then + self.children.view_deck = UIBox{ + definition = + {n=G.UIT.ROOT, config = {align = 'cm', padding = 0.1, r =0.1, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05, r =0.1, colour = adjust_alpha(G.C.BLACK, 0.5),func = 'set_button_pip', focus_args = {button = 'triggerright', orientation = 'bm', scale = 0.6}, button = 'deck_info'}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 2}, nodes={ + {n=G.UIT.T, config={text = localize('k_view'), scale = 0.48, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", maxw = 2}, nodes={ + {n=G.UIT.T, config={text = localize('k_deck'), scale = 0.38, colour = G.C.WHITE, shadow = true}} + }}, + }}, + }}, + config = { align = 'cm', offset = {x=0,y=0}, major = self.cards[1] or self, parent = self} + } + self.children.view_deck.states.collide.can = false + end + if G.deck_preview or self.states.collide.is or (G.buttons and G.buttons.states.collide.is and G.CONTROLLER.HID.controller) then self.children.view_deck:draw() end + if self.children.peek_deck then self.children.peek_deck:draw() end + end +end + +function CardArea:align_cards() + if self.config.type == 'hand' then + self.config.temp_limit = math.min(self.config.card_limit, #G.playing_cards) + end + if (self == G.hand or self == G.deck or self == G.discard or self == G.play) and G.view_deck and G.view_deck[1] and G.view_deck[1].cards then return end + if self.config.type == 'deck' then + local display_limit + if not Cartomancer.SETTINGS.compact_deck_enabled then + display_limit = 999999 + else + display_limit = Cartomancer.SETTINGS.compact_deck_visible_cards + end + + local deck_height = (self.config.deck_height or 0.15)/52 + local total_cards = #self.cards <= display_limit and #self.cards or display_limit -- limit height + local fixedX, fixedY, fixedR = nil, nil, nil + + for k, card in ipairs(self.cards) do + if card.facing == 'front' then card:flip() end + + if not card.states.drag.is then + if fixedX then + card.T.x = fixedX + card.T.y = fixedY + card.T.r = fixedR -- rotation + card.states.visible = false + else + card.T.x = self.T.x + 0.5*(self.T.w - card.T.w) + self.shadow_parrallax.x*deck_height*(total_cards/(self == G.deck and 1 or 2) - k) + 0.9*self.shuffle_amt*(1 - k*0.01)*(k%2 == 1 and 1 or -0) + card.T.y = self.T.y + 0.5*(self.T.h - card.T.h) + self.shadow_parrallax.y*deck_height*(total_cards/(self == G.deck and 1 or 2) - k) + card.T.r = 0 + 0.3*self.shuffle_amt*(1 + k*0.05)*(k%2 == 1 and 1 or -0) + card.T.x = card.T.x + card.shadow_parrallax.x/30 + card.states.visible = true + + if k >= display_limit then + fixedX = card.T.x + fixedY = card.T.y + fixedR = card.T.r + end + end + end + end + end + if self.config.type == 'discard' then + for k, card in ipairs(self.cards) do + if card.facing == 'front' then card:flip() end + + if not card.states.drag.is then + card.T.x = self.T.x + (self.T.w - card.T.w)*card.discard_pos.x + card.T.y = self.T.y + (self.T.h - card.T.h)* card.discard_pos.y + card.T.r = card.discard_pos.r + end + end + end + if self.config.type == 'hand' and (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then + for k, card in ipairs(self.cards) do + if not card.states.drag.is then + card.T.r = 0.4*(-#self.cards/2 - 0.5 + k)/(#self.cards)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+card.T.x) + local max_cards = math.max(#self.cards, self.config.temp_limit) + card.T.x = self.T.x + (self.T.w-self.card_w)*((k-1)/math.max(max_cards-1, 1) - 0.5*(#self.cards-max_cards)/math.max(max_cards-1, 1)) + 0.5*(self.card_w - card.T.w) + local highlight_height = G.HIGHLIGHT_H + if not card.highlighted then highlight_height = 0 end + card.T.y = G.hand.T.y - 1.8*card.T.h - highlight_height + (G.SETTINGS.reduced_motion and 0 or 1)*0.1*math.sin(0.666*G.TIMERS.REAL+card.T.x) + math.abs(1.3*(-#self.cards/2 + k-0.5)/(#self.cards))^2-0.3 + card.T.x = card.T.x + card.shadow_parrallax.x/30 + end + end + table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*((a.pinned and not a.ignore_pinned) and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*((b.pinned and not b.ignore_pinned) and b.sort_id or 0) end) + end + if self.config.type == 'hand' and not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) then + + for k, card in ipairs(self.cards) do + if not card.states.drag.is then + card.T.r = 0.2*(-#self.cards/2 - 0.5 + k)/(#self.cards)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+card.T.x) + local max_cards = math.max(#self.cards, self.config.temp_limit) + card.T.x = self.T.x + (self.T.w-self.card_w)*((k-1)/math.max(max_cards-1, 1) - 0.5*(#self.cards-max_cards)/math.max(max_cards-1, 1)) + 0.5*(self.card_w - card.T.w) + + local highlight_height = G.HIGHLIGHT_H + if not card.highlighted then highlight_height = 0 end + card.T.y = self.T.y + self.T.h/2 - card.T.h/2 - highlight_height + (G.SETTINGS.reduced_motion and 0 or 1)*0.03*math.sin(0.666*G.TIMERS.REAL+card.T.x) + math.abs(0.5*(-#self.cards/2 + k-0.5)/(#self.cards))-0.2 + card.T.x = card.T.x + card.shadow_parrallax.x/30 + end + end + table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*((a.pinned and not a.ignore_pinned) and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*((b.pinned and not b.ignore_pinned) and b.sort_id or 0) end) + end + if self.config.type == 'title' or (self.config.type == 'voucher' and #self.cards == 1) then + for k, card in ipairs(self.cards) do + if not card.states.drag.is then + card.T.r = 0.2*(-#self.cards/2 - 0.5 + k)/(#self.cards)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+card.T.x) + local max_cards = math.max(#self.cards, self.config.temp_limit) + card.T.x = self.T.x + (self.T.w-self.card_w)*((k-1)/math.max(max_cards-1, 1) - 0.5*(#self.cards-max_cards)/math.max(max_cards-1, 1)) + 0.5*(self.card_w - card.T.w) + local highlight_height = G.HIGHLIGHT_H + if not card.highlighted then highlight_height = 0 end + card.T.y = self.T.y + self.T.h/2 - card.T.h/2 - highlight_height + (G.SETTINGS.reduced_motion and 0 or 1)*0.03*math.sin(0.666*G.TIMERS.REAL+card.T.x) + math.abs(0.5*(-#self.cards/2 + k-0.5)/(#self.cards))-(#self.cards>1 and 0.2 or 0) + card.T.x = card.T.x + card.shadow_parrallax.x/30 + end + end + table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*((a.pinned and not a.ignore_pinned) and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*((b.pinned and not b.ignore_pinned) and b.sort_id or 0) end) + end + if self.config.type == 'voucher' and #self.cards > 1 then + local self_w = math.max(self.T.w, 3.2) + for k, card in ipairs(self.cards) do + if not card.states.drag.is then + card.T.r = 0.2*(-#self.cards/2 - 0.5 + k)/(#self.cards)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+card.T.x+card.T.y) + (k%2 == 0 and 1 or -1)*0.08 + local max_cards = math.max(#self.cards, self.config.temp_limit) + card.T.x = self.T.x + (self_w-self.card_w)*((k-1)/math.max(max_cards-1, 1) - 0.5*(#self.cards-max_cards)/math.max(max_cards-1, 1)) + 0.5*(self.card_w - card.T.w) + (k%2 == 1 and 1 or -1)*0.27 + (self.T.w-self_w)/2 + local highlight_height = G.HIGHLIGHT_H + if not card.highlighted then highlight_height = 0 end + card.T.y = self.T.y + self.T.h/2 - card.T.h/2 - highlight_height + (G.SETTINGS.reduced_motion and 0 or 1)*0.03*math.sin(0.666*G.TIMERS.REAL+card.T.x) + math.abs(0.5*(-#self.cards/2 + k-0.5)/(#self.cards))-(#self.cards>1 and 0.2 or 0) + card.T.x = card.T.x + card.shadow_parrallax.x/30 + end + end + table.sort(self.cards, function (a, b) return a.ability.order < b.ability.order end) + end + if self.config.type == 'play' or self.config.type == 'shop' then + for k, card in ipairs(self.cards) do + if not card.states.drag.is then + card.T.r = 0 + local max_cards = math.max(#self.cards, self.config.temp_limit) + card.T.x = self.T.x + (self.T.w-self.card_w)*((k-1)/math.max(max_cards-1, 1) - 0.5*(#self.cards-max_cards)/math.max(max_cards-1, 1)) + 0.5*(self.card_w - card.T.w) + (self.config.card_limit == 1 and 0.5*(self.T.w - card.T.w) or 0) + local highlight_height = G.HIGHLIGHT_H + if not card.highlighted then highlight_height = 0 end + card.T.y = self.T.y + self.T.h/2 - card.T.h/2 - highlight_height + card.T.x = card.T.x + card.shadow_parrallax.x/30 + end + end + table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*((a.pinned and not a.ignore_pinned) and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*((b.pinned and not b.ignore_pinned) and b.sort_id or 0) end) + end + if self == G.jokers and G.jokers.cart_jokers_expanded then + local align_cards = Cartomancer.expand_G_jokers() + + -- This should work fine without cryptid. But because cryptid's patch is priority=0, it has to be this way + if not G.GAME.modifiers.cry_conveyor then + table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end) + end + + if align_cards then + G.jokers:hard_set_cards() + end + elseif self.config.type == 'joker' or self.config.type == 'title_2' then + for k, card in ipairs(self.cards) do + if not card.states.drag.is then + card.T.r = 0.1*(-#self.cards/2 - 0.5 + k)/(#self.cards)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+card.T.x) + local max_cards = math.max(#self.cards, self.config.temp_limit) + card.T.x = self.T.x + (self.T.w-self.card_w)*((k-1)/math.max(max_cards-1, 1) - 0.5*(#self.cards-max_cards)/math.max(max_cards-1, 1)) + 0.5*(self.card_w - card.T.w) + if #self.cards > 2 or (#self.cards > 1 and self == G.consumeables) or (#self.cards > 1 and self.config.spread) then + card.T.x = self.T.x + (self.T.w-self.card_w)*((k-1)/(#self.cards-1)) + 0.5*(self.card_w - card.T.w) + elseif #self.cards > 1 and self ~= G.consumeables then + card.T.x = self.T.x + (self.T.w-self.card_w)*((k - 0.5)/(#self.cards)) + 0.5*(self.card_w - card.T.w) + else + card.T.x = self.T.x + self.T.w/2 - self.card_w/2 + 0.5*(self.card_w - card.T.w) + end + local highlight_height = G.HIGHLIGHT_H/2 + if not card.highlighted then highlight_height = 0 end + card.T.y = self.T.y + self.T.h/2 - card.T.h/2 - highlight_height+ (G.SETTINGS.reduced_motion and 0 or 1)*0.03*math.sin(0.666*G.TIMERS.REAL+card.T.x) + card.T.x = card.T.x + card.shadow_parrallax.x/30 + end + end + if not G.GAME.modifiers.cry_conveyor then table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*(a.pinned and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*(b.pinned and b.sort_id or 0) end) end + end + if self.config.type == 'consumeable'then + for k, card in ipairs(self.cards) do + if not card.states.drag.is then + if #self.cards > 1 then + card.T.x = self.T.x + (self.T.w-self.card_w)*((k-1)/(#self.cards-1)) + 0.5*(self.card_w - card.T.w) + else + card.T.x = self.T.x + self.T.w/2 - self.card_w/2 + 0.5*(self.card_w - card.T.w) + end + local highlight_height = G.HIGHLIGHT_H + if not card.highlighted then highlight_height = 0 end + card.T.y = self.T.y + self.T.h/2 - card.T.h/2 - highlight_height + (not card.highlighted and (G.SETTINGS.reduced_motion and 0 or 1)*0.05*math.sin(2*1.666*G.TIMERS.REAL+card.T.x) or 0) + card.T.x = card.T.x + card.shadow_parrallax.x/30 + end + end + table.sort(self.cards, function (a, b) return a.T.x + a.T.w/2 - 100*((a.pinned and not a.ignore_pinned) and a.sort_id or 0) < b.T.x + b.T.w/2 - 100*((b.pinned and not b.ignore_pinned) and b.sort_id or 0) end) + end + for k, card in ipairs(self.cards) do + card.rank = k + end + if self.children.view_deck then + self.children.view_deck:set_role{major = self.cards[1] or self} + end +end + +function CardArea:hard_set_T(X, Y, W, H) + local x = (X or self.T.x) + local y = (Y or self.T.y) + local w = (W or self.T.w) + local h = (H or self.T.h) + Moveable.hard_set_T(self,x, y, w, h) + self:calculate_parrallax() + self:align_cards() + self:hard_set_cards() +end + +function CardArea:hard_set_cards() + for k, card in pairs(self.cards) do + card:hard_set_T() + card:calculate_parrallax() + end +end + +function CardArea:shuffle(_seed) + pseudoshuffle(self.cards, pseudoseed(_seed or 'shuffle')) + self:set_ranks() +end + +function CardArea:sort(method) + self.config.sort = method or self.config.sort + if self.config.sort == 'desc' then + table.sort(self.cards, function (a, b) return a:get_nominal() > b:get_nominal() end ) + elseif self.config.sort == 'asc' then + table.sort(self.cards, function (a, b) return a:get_nominal() < b:get_nominal() end ) + elseif self.config.sort == 'suit desc' then + table.sort(self.cards, function (a, b) return a:get_nominal('suit') > b:get_nominal('suit') end ) + elseif self.config.sort == 'suit asc' then + table.sort(self.cards, function (a, b) return a:get_nominal('suit') < b:get_nominal('suit') end ) + elseif self.config.sort == 'order' then + table.sort(self.cards, function (a, b) return (a.config.card.order or a.config.center.order) < (b.config.card.order or b.config.center.order) end ) + end +end + +function CardArea:draw_card_from(area, stay_flipped, discarded_only) + if area:is(CardArea) then + if #self.cards < self.config.card_limit or self == G.deck or self == G.hand then + local card = area:remove_card(nil, discarded_only) + if card then + if area == G.discard then + card.T.r = 0 + end + if self == G.hand and not card.states.visible then + card.states.visible = true + end + local stay_flipped = G.GAME and G.GAME.blind and G.GAME.blind:stay_flipped(self, card) + if (self == G.hand) and G.GAME.modifiers.flipped_cards then + if pseudorandom(pseudoseed('flipped_card')) < 1/G.GAME.modifiers.flipped_cards then + stay_flipped = true + end + end + self:emplace(card, nil, stay_flipped) + return true + end + end + end +end + +function CardArea:click() + if self == G.deck then + G.FUNCS.deck_info() + end +end + +function CardArea:save() + if not self.cards then return end + local cardAreaTable = { + cards = {}, + config = self.config, + } + for i = 1, #self.cards do + cardAreaTable.cards[#cardAreaTable.cards + 1] = self.cards[i]:save() + end + + return cardAreaTable +end + +function CardArea:load(cardAreaTable) + + if self.cards then remove_all(self.cards) end + self.cards = {} + if self.children then remove_all(self.children) end + self.children = {} + + self.config = cardAreaTable.config + + for i = 1, #cardAreaTable.cards do + loading = true + local card = Card(0, 0, G.CARD_W, G.CARD_H, G.P_CENTERS.j_joker, G.P_CENTERS.c_base) + loading = nil + card:load(cardAreaTable.cards[i]) + self.cards[#self.cards + 1] = card + if card.highlighted then + self.highlighted[#self.highlighted + 1] = card + end + card:set_card_area(self) + end + self:set_ranks() + self:align_cards() + self:hard_set_cards() +end + +function CardArea:remove() + if self.cards then remove_all(self.cards) end + self.cards = nil + if self.children then remove_all(self.children) end + self.children = nil + for k, v in pairs(G.I.CARDAREA) do + if v == self then + table.remove(G.I.CARDAREA, k) + end + end + Moveable.remove(self) +end diff --git a/lovely/dump/conf.lua b/lovely/dump/conf.lua new file mode 100644 index 0000000..b3c8854 --- /dev/null +++ b/lovely/dump/conf.lua @@ -0,0 +1,13 @@ +LOVELY_INTEGRITY = 'cd11f803e89c48e476527cbfeec15ed491f002735c8b21fdbb0b2939cfd96518' + +_RELEASE_MODE = false +_DEMO = false + +function love.conf(t) + t.console = not _RELEASE_MODE + t.title = 'Balatro' + t.window.width = 0 + t.window.height = 0 + t.window.minwidth = 100 + t.window.minheight = 100 +end diff --git a/lovely/dump/engine/animatedsprite.lua b/lovely/dump/engine/animatedsprite.lua new file mode 100644 index 0000000..f39766a --- /dev/null +++ b/lovely/dump/engine/animatedsprite.lua @@ -0,0 +1,109 @@ +LOVELY_INTEGRITY = '8f16206d263b8b20d4f441ae0cdc8450eef3f2bd3b801abb44802f6b1578b809' + +--Class +AnimatedSprite = Sprite:extend() + +--Class Methods +function AnimatedSprite:init(X, Y, W, H, new_sprite_atlas, sprite_pos) + Sprite.init(self,X, Y, W, H, new_sprite_atlas, sprite_pos) + self.offset = {x = 0, y = 0} + + table.insert(G.ANIMATIONS, self) + if getmetatable(self) == AnimatedSprite then + table.insert(G.I.SPRITE, self) + end +end + +function AnimatedSprite:rescale() + self.scale_mag = math.min(self.scale.x/self.T.w,self.scale.y/self.T.h) +end + +function AnimatedSprite:reset() + self.atlas = G.ANIMATION_ATLAS[self.atlas.name] + self:set_sprite_pos({x = self.animation.x, y = self.animation.y}) +end + +function AnimatedSprite:set_sprite_pos(sprite_pos) + self.animation = { + x= sprite_pos and sprite_pos.x or 0, + y=sprite_pos and sprite_pos.y or 0, + frames=self.atlas.frames,current=0, + w=self.scale.x, h=self.scale.y} + + self.frame_offset = 0 + + self.current_animation = { + current = 0, + frames = self.animation.frames, + w = self.animation.w, + h = self.animation.h} + + self.image_dims = self.image_dims or {} + self.image_dims[1], self.image_dims[2] = self.atlas.image:getDimensions() + + self.sprite = love.graphics.newQuad( + 0, + self.animation.h*self.animation.y, + self.animation.w, + self.animation.h, + self.image_dims[1], self.image_dims[2]) + self.offset_seconds = G.TIMERS.REAL +end + +function AnimatedSprite:get_pos_pixel() + self.RETS.get_pos_pixel = self.RETS.get_pos_pixel or {} + self.RETS.get_pos_pixel[1] = self.current_animation.current + self.RETS.get_pos_pixel[2] = self.animation.y + self.RETS.get_pos_pixel[3] = self.animation.w + self.RETS.get_pos_pixel[4] = self.animation.h + return self.RETS.get_pos_pixel +end + +function AnimatedSprite:draw_self() + if not self.states.visible then return end + + prep_draw(self, 1) + love.graphics.scale(1/self.scale_mag) + love.graphics.setColor(G.C.WHITE) + love.graphics.draw( + self.atlas.image, + self.sprite, + 0 ,0, + 0, + self.VT.w/(self.T.w), + self.VT.h/(self.T.h) + ) + love.graphics.pop() +end + +function AnimatedSprite:animate() + local new_frame = math.floor(G.ANIMATION_FPS*(G.TIMERS.REAL - self.offset_seconds))%self.current_animation.frames + if new_frame ~= self.current_animation.current then + self.current_animation.current = new_frame + self.frame_offset = math.floor(self.animation.w*(self.current_animation.current)) + self.sprite:setViewport( + self.frame_offset, + self.animation.h*self.animation.y, + self.animation.w, + self.animation.h) + end + if self.float then + self.T.r = 0.02*math.sin(2*G.TIMERS.REAL+self.T.x) + self.offset.y = -(1+0.3*math.sin(0.666*G.TIMERS.REAL+self.T.y))*self.shadow_parrallax.y + self.offset.x = -(0.7+0.2*math.sin(0.666*G.TIMERS.REAL+self.T.x))*self.shadow_parrallax.x + end +end + +function AnimatedSprite:remove() + for k, v in pairs(G.ANIMATIONS) do + if v == self then + table.remove(G.ANIMATIONS, k) + end + end + for k, v in pairs(G.I.SPRITE) do + if v == self then + table.remove(G.I.SPRITE, k) + end + end + Sprite.remove(self) +end diff --git a/lovely/dump/engine/controller.lua b/lovely/dump/engine/controller.lua new file mode 100644 index 0000000..dd203fa --- /dev/null +++ b/lovely/dump/engine/controller.lua @@ -0,0 +1,1426 @@ +LOVELY_INTEGRITY = '2550ee40d7ac8df7273f766f9ca41eae9c54302c74f10249ada15b3fe2d6121c' + +---@class Controller +Controller = Object:extend() + +--The controller contains all engine logic for how human input interacts with any game objects. +function Controller:init() + +--Each of these are calculated per frame to pass along to the corresponding nodes for input handling +self.clicked = {target = nil, handled = true, prev_target = nil} --The node that was clicked this frame +self.focused = {target = nil, handled = true, prev_target = nil } --The node that is being focused on this frame, only applies when using controller +self.dragging = {target = nil, handled = true, prev_target = nil} --The node being dragged this frame +self.hovering = {target = nil, handled = true, prev_target = nil} --The node being hovered this frame +self.released_on = {target = nil, handled = true, prev_target = nil} --The node that the cursor 'Released' on, like letting go of LMB + +self.collision_list = {} --A list of all node that the cursor currently collides with + +--Input values to be determined by this controller - the actual game objects should not have to see any of this +self.cursor_down = {T = {x=0, y=0}, target = nil, time = 0, handled = true} +self.cursor_up = {T = {x=0, y=0}, target = nil, time = 0.1, handled = true} +self.cursor_hover = {T = {x=0, y=0}, target = nil, time = 0, handled = true} +self.cursor_collider = nil --The node that collides with the cursor this frame +self.cursor_position = {x=0,y=0} --NOT IN GAME UNITS + +--For key presses, hold times, and if they are released directly from LOVE +self.pressed_keys = {} +self.held_keys = {} +self.held_key_times = {} +self.released_keys = {} + +--For button presses, hold times, and if they are released directly from LOVE +self.pressed_buttons = {} +self.held_buttons = {} +self.held_button_times = {} +self.released_buttons = {} + +--For all controller interrupts +self.interrupt = { + focus = false, +} + +--For all controller locks +self.locks = {} +self.locked = nil + +--Buttons pressed and released during axis updates +self.axis_buttons = { + l_stick = {current = '', previous = ''}, + r_stick = {current = '', previous = ''}, + l_trig = {current = '', previous = ''}, + r_trig = {current = '', previous = ''} +} + +--The speed that the controller thumbstick moves the cursor +self.axis_cursor_speed = 20 + +--A registry of buttons, each a valid button input name corresponding to a node (likely a button). This is modified with the registry functions +self.button_registry = {} + +--A node representing where the cursor should 'snap' to. When this is set, then next frame should have the cursor to that position or on that node. Use :snap_to +self.snap_cursor_to = nil + +--A stack of cursor positions, this stack changes depending on the depth of menus on screen so the game can remember where you last had your cursor +--This needs to keep track of both positions and nodes if possible, as well as the depth +self.cursor_context = { + layer = 1, + stack = {} +} + +self.cardarea_context = {} + +--Human Interface device flags, these are set per frame to ensure that correct controller updates are taking place +self.HID = { + last_type = '', + dpad = false, + pointer = true, + touch = false, + controller = false, + mouse = true, + axis_cursor = false, +} + +--The gamepad most recently used, if any +self.GAMEPAD = {object = nil, mapping = nil, name = nil} +self.GAMEPAD_CONSOLE = '' --Valid button icons for Xbox, Playstation and Nintendo controllers + +--If we need an emulated gamepad for keyboard controls +self.keyboard_controller = { + getGamepadMappingString = function() return 'balatro_kbm' end, + getGamepadAxis = function() return 0 end +} + +self.is_cursor_down = false +end + +--Sets the gamepad to be the updated gamepad, searches for the console type and sets the art button pips accordingly +--Some code here is from github.com/idbrii/love-gamepadguesser (MIT License) +function Controller:set_gamepad(_gamepad) + if self.GAMEPAD.object ~= _gamepad then + self.GAMEPAD.object = _gamepad + self.GAMEPAD.mapping = _gamepad:getGamepadMappingString() or '' + self.GAMEPAD.name = self.GAMEPAD.mapping:match("^%x*,(.-),") or '' + self.GAMEPAD.temp_console = self:get_console_from_gamepad(self.GAMEPAD.name) + if self.GAMEPAD_CONSOLE ~= self.GAMEPAD.temp_console then + self.GAMEPAD_CONSOLE = self.GAMEPAD.temp_console + for k, v in pairs(G.I.SPRITE) do + if v.atlas == G.ASSET_ATLAS["gamepad_ui"] then + v.sprite_pos.y = G.CONTROLLER.GAMEPAD_CONSOLE == 'Nintendo' and 2 or G.CONTROLLER.GAMEPAD_CONSOLE == 'Playstation' and (G.F_PS4_PLAYSTATION_GLYPHS and 3 or 1) or 0 + v:set_sprite_pos(v.sprite_pos) + end + end + end + self.GAMEPAD.temp_console = nil + end +end + +--Some code here is from github.com/idbrii/love-gamepadguesser (MIT License) +function Controller:get_console_from_gamepad(_gamepad) + G.ARGS.gamepad_patterns = G.ARGS.gamepad_patterns or + { + Playstation = {"%f[%w]PS%d%f[%D]", "Sony%f[%W]", "Play[Ss]tation"}, + Nintendo = {"Wii%f[%L]", "%f[%u]S?NES%f[%U]", "%f[%l]s?nes%f[%L]", "%f[%u]Switch%f[%L]", "Joy[- ]Cons?%f[%L]",}, + --Keyboard = {'balatro_kbm'} + } + + for k, v in pairs(G.ARGS.gamepad_patterns) do + for kk, vv in ipairs(v) do + if _gamepad:match(vv) then + return k + end + end + end + return 'Xbox' +end + +--The universal controller for what type of HID Device the player is using to interact with the game. The Game should be able to handle switching +--to any viable HID at any time +function Controller:set_HID_flags(HID_type, button) + --we need to determine it the axis input will be handled like a button or pointer + --if button and self.HID.controller and not string.find(button, 'dp') then return end + if HID_type == 'axis' then + self.HID.controller = true + self.HID.last_type = 'axis' + elseif HID_type and HID_type ~= self.HID.last_type then + self.HID.dpad = HID_type == 'button' + self.HID.pointer = HID_type == 'mouse' or HID_type == 'axis_cursor' or HID_type == 'touch' + self.HID.controller = HID_type == 'button' or HID_type == 'axis_cursor' + self.HID.mouse = HID_type == 'mouse' + self.HID.touch = HID_type == 'touch' + self.HID.axis_cursor = HID_type == 'axis_cursor' + self.HID.last_type = HID_type + + if self.HID.mouse then + love.mouse.setVisible(true) + else + love.mouse.setVisible(false) + end + end + if not self.HID.controller then + self.GAMEPAD_CONSOLE = '' + self.GAMEPAD.object = nil + self.GAMEPAD.mapping = nil + self.GAMEPAD.name = nil + end +end + +--Sets the current position of the cursor +function Controller:set_cursor_position() + --If using a mouse for the cursor + if self.HID.mouse or self.HID.touch then + self.interrupt.focus = false + --Never focus if using the mouse + if self.focused.target then + self.focused.target.states.focus.is = false + self.focused.target= nil + end + + --Set the position of the cursor to the love position of the mouse, derive cursor transform from that + self.cursor_position.x, self.cursor_position.y = love.mouse.getPosition() + G.CURSOR.T.x = self.cursor_position.x/(G.TILESCALE*G.TILESIZE) + G.CURSOR.T.y = self.cursor_position.y/(G.TILESCALE*G.TILESIZE) + G.CURSOR.VT.x = G.CURSOR.T.x + G.CURSOR.VT.y = G.CURSOR.T.y + end +end + +--Called every game logic update frame +function Controller:update(dt) + + --parse all locks and set + self.locked = false + if G.screenwipe then self.locks.wipe = true else self.locks.wipe = false end + + for k, v in pairs(self.locks) do + if v then self.locked = true end + end + + if self.locks.frame_set then + self.locks.frame_set = nil + self.overlay_timer = 0 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + timer = 'UPTIME', + blocking = false, + blockable = false, + no_delete = true, + func = (function() + self.locks.frame = nil + return true + end) + })) + end + + self.overlay_timer = self.overlay_timer or 0 + if G.OVERLAY_MENU then + self.overlay_timer = self.overlay_timer + dt + else + self.overlay_timer = 0 + end + + if self.overlay_timer > 1.5 then self.locks.frame = nil end + + --Remove anything from the registry that is no longer in game + self:cull_registry() + + --Calculate the axis update and set the HID flags if there is any axis input + self:set_HID_flags(self:update_axis(dt)) + + --Set the cursor to be visible only if we are using a mouse or an axis to control the cursor position + if self.HID.pointer and not (self.HID.mouse or self.HID.touch) and not self.interrupt.focus then + G.CURSOR.states.visible = true + else + G.CURSOR.states.visible = false + end + + --For mouse input, reset any controller things and set the cursor to be where the mouse is + self:set_cursor_position() + + --Handle all the button updates and key updates, call the required functions + if not G.screenwipe then + --Key press and release handling + for k, v in pairs(self.pressed_keys) do + if v then self:key_press_update(k, dt) end + end + for k, v in pairs(self.held_keys) do + if v then self:key_hold_update(k, dt) end + end + for k, v in pairs(self.released_keys) do + if v then self:key_release_update(k, dt) end + end + + --Button press and release handling + for k, v in pairs(self.pressed_buttons) do + if v then self:button_press_update(k, dt) end + end + for k, v in pairs(self.held_buttons) do + if v then self:button_hold_update(k, dt) end + end + for k, v in pairs(self.released_buttons) do + if v then self:button_release_update(k, dt) end + end + end + + self.frame_buttonpress = false + + --reset press and release lists + self.pressed_keys = EMPTY(self.pressed_keys) + self.released_keys = EMPTY(self.released_keys) + self.pressed_buttons= EMPTY(self.pressed_buttons) + self.released_buttons = EMPTY(self.released_buttons) + + + --If using controller, update context and snap tos + if self.HID.controller then + --If there is a node/position to snap to from the cursor context layer + if self.cursor_context.stack[self.cursor_context.layer] then + local _context = self.cursor_context.stack[self.cursor_context.layer] + self:snap_to{node = (_context.node and not _context.node.REMOVED and _context.node), T = _context.cursor_pos} + self.interrupt.stack = _context.interrupt + self.cursor_context.stack[self.cursor_context.layer] = nil + end + --If there is a card the was being dragged but no longer is, snap to it + if self.dragging.prev_target and not self.dragging.target and getmetatable(self.dragging.prev_target) == Card and not self.dragging.prev_target.REMOVED then + --Overly complicated coyote time focus, so the user can quickly select cards without things going wonky + if not self.COYOTE_FOCUS then + self:snap_to{node = self.dragging.prev_target} + else + self.COYOTE_FOCUS = nil + end + end + --If the cursor should snap to a location + if self.snap_cursor_to then + self.interrupt.focus = self.interrupt.stack + self.interrupt.stack = false + if self.snap_cursor_to.type == 'node' and not self.snap_cursor_to.node.REMOVED then + self.focused.prev_target = self.focused.target + self.focused.target = self.snap_cursor_to.node + self:update_cursor() + elseif self.snap_cursor_to.type == 'transform' then + self:update_cursor(self.snap_cursor_to.T) + end + if self.focused.prev_target ~= self.focused.target and self.focused.prev_target then self.focused.prev_target.states.focus.is = false end + self.snap_cursor_to = nil + end + end + + --Reset all collision states, get every node that collides with the cursor, then update the focus and hover targets + self:get_cursor_collision(G.CURSOR.T) + self:update_focus() + self:set_cursor_hover() + if self.L_cursor_queue then + self:L_cursor_press(self.L_cursor_queue.x, self.L_cursor_queue.y) + self.L_cursor_queue = nil + end + + self.dragging.prev_target = self.dragging.target + self.released_on.prev_target = self.released_on.target + self.clicked.prev_target = self.clicked.target + self.hovering.prev_target = self.hovering.target + + --Cursor is currently down + if not self.cursor_down.handled then + if self.cursor_down.target.states.drag.can then + self.cursor_down.target.states.drag.is = true + self.cursor_down.target:set_offset(self.cursor_down.T, 'Click') + self.dragging.target = self.cursor_down.target + self.dragging.handled = false + end + self.cursor_down.handled = true + end + + if not self.cursor_up.handled then + --First, stop dragging + if self.dragging.target then + self.dragging.target:stop_drag() + self.dragging.target.states.drag.is = false + self.dragging.target = nil + end + --Now, handle the Cursor release + --Was the Cursor release in the same location as the Cursor press and within Cursor timeout? + if self.cursor_down.target then + if (not self.cursor_down.target.click_timeout or self.cursor_down.target.click_timeout*G.SPEEDFACTOR > self.cursor_up.time - self.cursor_down.time) then + if Vector_Dist(self.cursor_down.T, self.cursor_up.T) < G.MIN_CLICK_DIST then + if self.cursor_down.target.states.click.can then + self.clicked.target = self.cursor_down.target + self.clicked.handled = false + end + --if not, was the Cursor dragging some other thing? + elseif self.dragging.prev_target and self.cursor_up.target and self.cursor_up.target.states.release_on.can then + self.released_on.target = self.cursor_up.target + self.released_on.handled = false + end + end + end + self.cursor_up.handled = true + end + + --Cursor is currently hovering over something + if self.cursor_hover.target and self.cursor_hover.target.states.hover.can and (not self.HID.touch or self.is_cursor_down) then + self.hovering.target = self.cursor_hover.target + if self.hovering.prev_target and self.hovering.prev_target ~= self.hovering.target then self.hovering.prev_target.states.hover.is = false end + self.hovering.target.states.hover.is = true + self.hovering.target:set_offset(self.cursor_hover.T, 'Hover') + elseif (self.cursor_hover.target == nil or (self.HID.touch and not self.is_cursor_down)) and self.hovering.target then + self.hovering.target.states.hover.is = false + self.hovering.target = nil + end + + -------------------------------------------------------------------- + -- Sending all input updates to the game objects + -------------------------------------------------------------------- + --The clicked object + if not self.clicked.handled then + self.clicked.target:click() + self.clicked.handled = true + end + + --Process registry clicks + self:process_registry() + + --The object being dragged + if self.dragging.target then + self.dragging.target:drag() + end + + --The object released on + if not self.released_on.handled and self.dragging.prev_target then + if self.dragging.prev_target == self.hovering.target then self.hovering.target:stop_hover();self.hovering.target = nil end + self.released_on.target:release(self.dragging.prev_target) + self.released_on.handled = true + end + + --The object being hovered over + if self.hovering.target then + self.hovering.target:set_offset(self.cursor_hover.T, 'Hover') + if self.hovering.prev_target ~= self.hovering.target then + if self.hovering.target ~= self.dragging.target and not self.HID.touch then + self.hovering.target:hover() + elseif self.HID.touch then + local _ID = self.hovering.target.ID + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + blocking = false, + delay = G.MIN_HOVER_TIME, + func = function() + if self.hovering.target and _ID == self.hovering.target.ID then + self.hovering.target:hover() + end + return true + end + })) + if self.hovering.prev_target then + self.hovering.prev_target:stop_hover() + end + end + if self.hovering.prev_target then + self.hovering.prev_target:stop_hover() + end + end + elseif self.hovering.prev_target then + self.hovering.prev_target:stop_hover() + end + if self.hovering.target and self.hovering.target == self.dragging.target and not self.HID.touch then + self.hovering.target:stop_hover() + end +end + +--Brute force remove all registries that no longer have valid nodes +function Controller:cull_registry() + for k, registry in pairs(self.button_registry) do + for i=#registry, 1, -1 do + if registry[i].node.REMOVED then + table.remove(registry, i) + end + end + end +end + +--Adds a node to the controller registry. Supply the button that will be pressed in order to click this node +-- +---@param node Node The node that will be clicked when the registry is pressed +---@param registry string The button to register, must be a valid gamepad input +function Controller:add_to_registry(node, registry) + --If the button doesn't have a registry list yet, add it + self.button_registry[registry] = self.button_registry[registry] or {} + + --There really should only ever be one entry per registered button, but that is hard sometimes with all the stuff on screen. + --If that does happen, the most recently registered one will be used and the old one will be kept in case we remove the new button and want to keep the old binding + table.insert(self.button_registry[registry], 1, {node = node, menu = (not not G.OVERLAY_MENU) or (not not G.SETTINGS.paused)}) +end + +--Process any click function of any nodes that have been clicked in the button registry +function Controller:process_registry() + for _, registry in pairs(self.button_registry) do + for i=1, #registry do + if registry[i].click and registry[i].node.click then + if registry[i].menu == not not G.OVERLAY_MENU and + registry[i].node.T.x > -2 and registry[i].node.T.x < G.ROOM.T.w + 2 and + registry[i].node.T.y > -2 and registry[i].node.T.y < G.ROOM.T.h + 2 then + registry[i].node:click() + end + registry[i].click = nil + end + end + end +end + +--Add or remove layers from the context for the cursor. This allows the cursor to 'snap' back to the previous layer when the current layer is removed\ +--in such cases where a menu on screen is removed or nested menus are being navigated +-- +---@param delta number The direction to modify the cursor context, 1 to add a layer, -1 to remove a layer, -1000 to remove all layers except for the base +function Controller:mod_cursor_context_layer(delta) + --Add a layer to the context, reference the node but if that node has been removed also save the cursor position too + if delta == 1 then + local prev_cursor_context = {node = self.focused.target, cursor_pos = {x = G.CURSOR.T.x, y = G.CURSOR.T.y}, interrupt = self.interrupt.focus} + self.cursor_context.stack[self.cursor_context.layer] = prev_cursor_context + self.cursor_context.layer = self.cursor_context.layer + 1 + + --remove the top layer from the stack + elseif delta == -1 then + self.cursor_context.stack[self.cursor_context.layer] = nil + self.cursor_context.layer = self.cursor_context.layer - 1 + + --remove all but the base layer from the stack + elseif delta == -1000 then + self.cursor_context.layer = 1 + self.cursor_context.stack = {self.cursor_context.stack[1]} + + --remove all layers + elseif delta == -2000 then + self.cursor_context.layer = 1 + self.cursor_context.stack = {} + end + + --Navigate focus, will default to the top layer on the stack + self:navigate_focus() +end + +--Snap the cursor to a particular node or transform +function Controller:snap_to(args) + self.snap_cursor_to = {node = args.node, T = args.T, type = args.node and 'node' or 'transform'} +end + +--saves the focus context to be loaded in the future, for example if the shop is rerolled while a card is highlighted +function Controller:save_cardarea_focus(_cardarea) + if G[_cardarea] then + if self.focused.target and self.focused.target.area and self.focused.target.area == G[_cardarea] then + self.cardarea_context[_cardarea] = self.focused.target.rank + return true + else + self.cardarea_context[_cardarea] = nil + end + end +end + +--recalls the focus context for a particular cardarea +function Controller:recall_cardarea_focus(_cardarea) + local ca_string = nil + if type(_cardarea) == 'string' then ca_string = _cardarea; _cardarea = G[_cardarea] end + + if _cardarea and (not self.focused.target or + self.interrupt.focus or + (not self.interrupt.focus and self.focused.target.area and self.focused.target.area == _cardarea)) then + if ca_string and self.cardarea_context[ca_string] then + for i = self.cardarea_context[ca_string], 1, -1 do + if _cardarea.cards[i] then + self:snap_to({node = _cardarea.cards[i]}) + self.interrupt.focus = false + break + end + end + elseif _cardarea.cards and _cardarea.cards[1] then + self:snap_to({node = _cardarea.cards[1]}) + self.interrupt.focus = false + end + end + if ca_string then self.cardarea_context[ca_string] = nil end +end + +--Updated the location of the cursor, either with a specific T or if there is a Node target +function Controller:update_cursor(hard_set_T) + if hard_set_T then + G.CURSOR.T.x = hard_set_T.x + G.CURSOR.T.y = hard_set_T.y + self.cursor_position.x = G.CURSOR.T.x*(G.TILESCALE*G.TILESIZE) + self.cursor_position.y = G.CURSOR.T.y*(G.TILESCALE*G.TILESIZE) + G.CURSOR.VT.x = G.CURSOR.T.x + G.CURSOR.VT.y = G.CURSOR.T.y + return + end + if self.focused.target then + self.cursor_position.x, self.cursor_position.y = self.focused.target:put_focused_cursor() + G.CURSOR.T.x = self.cursor_position.x/(G.TILESCALE*G.TILESIZE) + G.CURSOR.T.y = self.cursor_position.y/(G.TILESCALE*G.TILESIZE) + G.CURSOR.VT.x = G.CURSOR.T.x + G.CURSOR.VT.y = G.CURSOR.T.y + end +end + +--Helper function to set the button presses/releases for the values determined in update_axis() +function Controller:handle_axis_buttons() + for _, v in pairs(G.CONTROLLER.axis_buttons) do + --Button is no longer being pressed + if v.previous ~= '' and (v.current == '' or v.previous ~= v.current) then + G.CONTROLLER:button_release(v.previous) + end + --New button is being pressed + if v.current ~= '' and v.previous ~= v.current then + G.CONTROLLER:button_press(v.current) + end + end +end + +--Handles all axis input for left stick, right stick and triggers. Treats them as buttons or cursors depending on context +function Controller:update_axis(dt) + --Keep track of if there is any cursor movement from the axis changes + local axis_interpretation = nil + + --Advance all the axis buttons to determine if there were any changes + self.axis_buttons.l_stick.previous = self.axis_buttons.l_stick.current; self.axis_buttons.l_stick.current = '' + self.axis_buttons.r_stick.previous = self.axis_buttons.r_stick.current; self.axis_buttons.r_stick.current = '' + self.axis_buttons.l_trig.previous = self.axis_buttons.l_trig.current; self.axis_buttons.l_trig.current = '' + self.axis_buttons.r_trig.previous = self.axis_buttons.r_trig.current; self.axis_buttons.r_trig.current = '' + + if self.HID.controller then + --------------------------------------------------------------- + -- Left Thumbstick + --------------------------------------------------------------- + local l_stick_x = self.GAMEPAD.object:getGamepadAxis('leftx') + local l_stick_y = self.GAMEPAD.object:getGamepadAxis('lefty') + --If there is something being dragged, we want to treat the left stick as a cursor input + if self.dragging.target and math.abs(l_stick_x) + math.abs(l_stick_y) > 0.1 then + axis_interpretation = 'axis_cursor' --There is some cursor movement + + --deadzone of 10% for each axis of l_stick + if math.abs(l_stick_x) < 0.1 then l_stick_x = 0 end + if math.abs(l_stick_y) < 0.1 then l_stick_y = 0 end + l_stick_x = l_stick_x + (l_stick_x>0 and -0.1 or 0) + (l_stick_x<0 and 0.1 or 0) + l_stick_y = l_stick_y + (l_stick_y>0 and -0.1 or 0) + (l_stick_y<0 and 0.1 or 0) + + --Modify the cursor position according to the l_stick axis values + G.CURSOR.T.x = G.CURSOR.T.x + dt*l_stick_x*self.axis_cursor_speed + G.CURSOR.T.y = G.CURSOR.T.y + dt*l_stick_y*self.axis_cursor_speed + G.CURSOR.VT.x = G.CURSOR.T.x + G.CURSOR.VT.y = G.CURSOR.T.y + + self.cursor_position.x = G.CURSOR.T.x*(G.TILESCALE*G.TILESIZE) + self.cursor_position.y = G.CURSOR.T.y*(G.TILESCALE*G.TILESIZE) + + --If nothing is being dragged, we want to treat the left stick as a dpad input + else + self.axis_buttons.l_stick.current = self.axis_buttons.l_stick.previous + if math.abs(l_stick_x) + math.abs(l_stick_y) > 0.5 then + axis_interpretation = 'button' --left stick is no longer a cursor, can be set below from the right stick though + + self.axis_buttons.l_stick.current = math.abs(l_stick_x) > math.abs(l_stick_y) and + (l_stick_x > 0 and 'dpright' or 'dpleft') or + (l_stick_y > 0 and 'dpdown' or 'dpup') + elseif math.abs(l_stick_x) + math.abs(l_stick_y) < 0.3 then + self.axis_buttons.l_stick.current = '' + end + end + + --------------------------------------------------------------- + -- Right Thumbstick + --------------------------------------------------------------- + local r_stick_x = self.GAMEPAD.object:getGamepadAxis('rightx') + local r_stick_y = self.GAMEPAD.object:getGamepadAxis('righty') + G.DEADZONE = 0.2 + local mag = math.sqrt(math.abs(r_stick_x)^2 + math.abs(r_stick_y)^2) + if mag > G.DEADZONE then + axis_interpretation = 'axis_cursor' --There is some cursor movement + + --deadzone of 20% for each axis of l_stick + if math.abs(r_stick_x) < G.DEADZONE then r_stick_x = 0 end + if math.abs(r_stick_y) < G.DEADZONE then r_stick_y = 0 end + r_stick_x = r_stick_x + (r_stick_x>0 and -G.DEADZONE or 0) + (r_stick_x<0 and G.DEADZONE or 0) + r_stick_y = r_stick_y + (r_stick_y>0 and -G.DEADZONE or 0) + (r_stick_y<0 and G.DEADZONE or 0) + + --Modify the cursor position according to the l_stick axis values + G.CURSOR.T.x = G.CURSOR.T.x + dt*r_stick_x*self.axis_cursor_speed + G.CURSOR.T.y = G.CURSOR.T.y + dt*r_stick_y*self.axis_cursor_speed + G.CURSOR.VT.x = G.CURSOR.T.x + G.CURSOR.VT.y = G.CURSOR.T.y + + self.cursor_position.x = G.CURSOR.T.x*(G.TILESCALE*G.TILESIZE) + self.cursor_position.y = G.CURSOR.T.y*(G.TILESCALE*G.TILESIZE) + end + + --------------------------------------------------------------- + -- Triggers + --------------------------------------------------------------- + --Triggers are just buttons + local l_trig = self.GAMEPAD.object:getGamepadAxis('triggerleft') + local r_trig = self.GAMEPAD.object:getGamepadAxis('triggerright') + + --Set the current to be the same as previous, only set to '' if the trigger value is low enough + self.axis_buttons.l_trig.current = self.axis_buttons.l_trig.previous + self.axis_buttons.r_trig.current = self.axis_buttons.r_trig.previous + + --Make a tilting effect when you press the triggers while hovering over a node that has tilt_var + if self.focused.target and self.focused.target.tilt_var then + --self.focused.target.tilt_var.dx = 0.1*(r_trig - l_trig) + 0.9*self.focused.target.tilt_var.dx + end + + if l_trig > 0.5 then + self.axis_buttons.l_trig.current = 'triggerleft' + elseif l_trig < 0.3 then + self.axis_buttons.l_trig.current = '' + end + if r_trig > 0.5 then + self.axis_buttons.r_trig.current = 'triggerright' + elseif r_trig < 0.3 then + self.axis_buttons.r_trig.current = '' + end + + --return 'button' if trigger is being used and axis is not + if self.axis_buttons.r_trig.current ~= '' or self.axis_buttons.l_trig.current ~= '' then + axis_interpretation = axis_interpretation or 'button' + end + + self:handle_axis_buttons() + end + + if axis_interpretation then self.interrupt.focus = false end + + return axis_interpretation +end + +function Controller:button_press_update(button, dt) + if self.locks.frame then return end + self.held_button_times[button] = 0 + self.interrupt.focus = false + + if not self:capture_focused_input(button, 'press', dt) then + if button == "dpup" then + self:navigate_focus('U') + end + if button == "dpdown" then + self:navigate_focus('D') + end + if button == "dpleft" then + self:navigate_focus('L') + end + if button == "dpright" then + self:navigate_focus('R') + end + end + + if ((self.locked) and not G.SETTINGS.paused) or (self.locks.frame) or (self.frame_buttonpress) then return end + self.frame_buttonpress = true + + if self.button_registry[button] and self.button_registry[button][1] and not self.button_registry[button][1].node.under_overlay then + self.button_registry[button][1].click = true + else + if button == 'start' then + if G.STATE == G.STATES.SPLASH then + G:delete_run() + G:main_menu() + end + end + if button == "a" then + if self.focused.target and + self.focused.target.config.focus_args and + self.focused.target.config.focus_args.type == 'slider' and + (not G.CONTROLLER.HID.mouse and not G.CONTROLLER.HID.axis_cursor) then + else + self:L_cursor_press() + end + end + if button == 'b' then + if G.hand and self.focused.target and + self.focused.target.area == G.hand then + self:queue_R_cursor_press() + else + self.interrupt.focus = true + end + end + end +end + +function Controller:button_hold_update(button, dt) + if ((self.locked) and not G.SETTINGS.paused) or (self.locks.frame) or (self.frame_buttonpress) then return end + self.frame_buttonpress = true + if self.held_button_times[button] then + self.held_button_times[button] = self.held_button_times[button] + dt + self:capture_focused_input(button, 'hold', dt) + end + if (button == 'dpleft' or button == 'dpright' or button == 'dpup' or button == 'dpdown') and not self.no_holdcap then + self.repress_timer = self.repress_timer or 0.3 + if self.held_button_times[button] and (self.held_button_times[button] > self.repress_timer) then + self.repress_timer = 0.1 + self.held_button_times[button] = 0 + self:button_press_update(button, dt) + end + end +end + +function Controller:button_release_update(button, dt) + if not self.held_button_times[button] then return end + self.repress_timer = 0.3 + self.held_button_times[button] = nil + if button == 'a' then + self:L_cursor_release() + end +end + +function Controller:key_press_update(key, dt) + if key == "escape" and Cartomancer.INTERNAL_in_config then + Cartomancer.INTERNAL_in_config = false + if not Cartomancer.use_smods() then + Cartomancer.save_config() + end + end + if key == "escape" and G.ACTIVE_MOD_UI then + G.FUNCS.exit_mods() + end + if self.locks.frame then return end + if string.sub(key, 1, 2) == 'kp' then key = string.sub(key, 3) end + if key == 'enter' then key = 'return' end + + if self.text_input_hook then + if key == "escape" then + self.text_input_hook = nil + elseif key == "capslock" then + self.capslock = not self.capslock + else + G.FUNCS.text_input_key{ + e=self.text_input_hook, + key = key, + caps = self.held_keys["lshift"] or self.held_keys["rshift"] + } + end + return + end + + if key == "escape" then + if G.STATE == G.STATES.SPLASH then + G:delete_run() + G:main_menu() + else + if not G.OVERLAY_MENU then + G.FUNCS:options() + elseif not G.OVERLAY_MENU.config.no_esc then + G.FUNCS:exit_overlay_menu() + end + end + end + + if ((self.locked) and not G.SETTINGS.paused) or (self.locks.frame) or (self.frame_buttonpress) then return end + self.frame_buttonpress = true + self.held_key_times[key] = 0 + + + for _, keybind in pairs(SMODS.Keybinds) do + if keybind.action and keybind.key_pressed == key then + local execute = true + for _, other_key in pairs(keybind.held_keys) do + if not self.held_keys[other_key] then + execute = false + break + end + end + if execute then + keybind.action(self) + end + end + end + if not _RELEASE_MODE and require("debugplus.core").isOkayToHandleDebugForKey(key) then + if key == 'tab' and not G.debug_tools then + G.debug_tools = UIBox{ + definition = create_UIBox_debug_tools(), + config = {align='cr', offset = {x=G.ROOM.T.x + 11,y=0},major = G.ROOM_ATTACH, bond = 'Weak'} + } + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.debug_tools.alignment.offset.x = -4 + return true + end + })) + end + if self.hovering.target and self.hovering.target:is(Card) then + local _card = self.hovering.target + if G.OVERLAY_MENU then + if key == "1" then + unlock_card(_card.config.center) + _card:set_sprites(_card.config.center) + end + if key == "2" then + unlock_card(_card.config.center) + discover_card(_card.config.center) + _card:set_sprites(_card.config.center) + end + if key == "3" then + if _card.ability.set == 'Joker' and G.jokers and #G.jokers.cards < G.jokers.config.card_limit then + add_joker(_card.config.center.key) + _card:set_sprites(_card.config.center) + end + local debugplus = require("debugplus.core") + debugplus.handleSpawn(self, _card) + if _card.ability.consumeable and G.consumeables and #G.consumeables.cards < G.consumeables.config.card_limit then + add_joker(_card.config.center.key) + _card:set_sprites(_card.config.center) + end + end + end + if key == 'q' then + if (_card.ability.set == 'Joker' or _card.playing_card or _card.area) then + local found_index = 1 + if _card.edition then + for i, v in ipairs(G.P_CENTER_POOLS.Edition) do + if v.key == _card.edition.key then + found_index = i + break + end + end + end + found_index = found_index + 1 + if found_index > #G.P_CENTER_POOLS.Edition then found_index = found_index - #G.P_CENTER_POOLS.Edition end + local _edition = G.P_CENTER_POOLS.Edition[found_index].key + _card:set_edition(_edition, true, true) + end + end + end + if key == 'h' then + G.debug_UI_toggle = not G.debug_UI_toggle + end + if key == 'b' then + G:delete_run() + G:start_run({}) + end + if key == 'l' then + G:delete_run() + G.SAVED_GAME = get_compressed(G.SETTINGS.profile..'/'..'save.jkr') + if G.SAVED_GAME ~= nil then G.SAVED_GAME = STR_UNPACK(G.SAVED_GAME) end + G:start_run({savetext = G.SAVED_GAME}) + end + if key == 'j' then + G.debug_splash_size_toggle = not G.debug_splash_size_toggle + G:delete_run() + G:main_menu('splash') + end + if key == '8' then + love.mouse.setVisible( not love.mouse.isVisible() ) + end + if key == '9' then + G.debug_tooltip_toggle = not G.debug_tooltip_toggle + end + if key == "space" then + live_test() + end + local debugplus = require("debugplus.core") + debugplus.handleKeys(self, key, dt) + if key == 'v' then + if not G.prof then G.prof = require "engine/profile"; G.prof.start() + else G.prof:stop(); + print(G.prof.report()); G.prof = nil end + debugplus.profileMessage() + end + if key == "p" then + G.SETTINGS.perf_mode = not G.SETTINGS.perf_mode + debugplus.togglePerfUI() + end + end +end + +function Controller:key_hold_update(key, dt) + if ((self.locked) and not G.SETTINGS.paused) or (self.locks.frame) or (self.frame_buttonpress) then return end + --self.frame_buttonpress = true + if self.held_key_times[key] then + if key == 'm' then + if self.held_key_times[key] > 1.1 then + SMODS.save_all_config() + SMODS.restart_game() + else + self.held_key_times[key] = self.held_key_times[key] + dt + end +elseif key == "r" and not G.SETTINGS.paused and not (G.GAME and G.GAME.USING_CODE) then + if self.held_key_times[key] > 0.7 then + if not G.GAME.won and not G.GAME.seeded and not G.GAME.challenge then + G.PROFILES[G.SETTINGS.profile].high_scores.current_streak.amt = 0 + end + G:save_settings() + self.held_key_times[key] = nil + G.SETTINGS.current_setup = 'New Run' + G.GAME.viewed_back = nil + G.run_setup_seed = G.GAME.seeded + G.challenge_tab = G.GAME and G.GAME.challenge and G.GAME.challenge_tab or nil + G.forced_seed, G.setup_seed = nil, nil + if G.GAME.seeded then G.forced_seed = G.GAME.pseudorandom.seed end + G.forced_stake = G.GAME.stake + if G.STAGE == G.STAGES.RUN then G.FUNCS.start_setup_run() end + G.forced_stake = nil + G.challenge_tab = nil + G.forced_seed = nil + else + self.held_key_times[key] = self.held_key_times[key] + dt + end + end + end +end + +function Controller:key_release_update(key, dt) + if ((self.locked) and not G.SETTINGS.paused) or (self.locks.frame) or (self.frame_buttonpress) then return end + self.frame_buttonpress = true + if key == "a" and self.held_keys["g"] and not _RELEASE_MODE then + G.DEBUG = not(G.DEBUG) + end + if key == 'tab' and G.debug_tools then + G.debug_tools:remove() + G.debug_tools = nil + end +end + +function Controller:key_press(key) + self.pressed_keys[key] = true + self.held_keys[key] = true +end + +function Controller:key_release(key) + self.held_keys[key] = nil + self.released_keys[key] = true +end + +function Controller:button_press(button) + self.pressed_buttons[button] = true + self.held_buttons[button] = true +end + +function Controller:button_release(button) + self.held_buttons[button] = nil + self.released_buttons[button] = true +end + +function Controller:get_cursor_collision(cursor_trans) + self.collision_list = EMPTY(self.collision_list) + self.nodes_at_cursor = EMPTY(self.nodes_at_cursor) + + if self.COYOTE_FOCUS then return end + if self.dragging.target then + self.dragging.target.states.collide.is = true + self.nodes_at_cursor[#self.nodes_at_cursor+1] = self.dragging.target + self.collision_list[#self.collision_list+1] = self.dragging.target + end + + if not G.DRAW_HASH[1] or + cursor_trans.x-G.ROOM.T.x < -G.DRAW_HASH_BUFF or cursor_trans.x-G.ROOM.T.x > G.TILE_W + G.DRAW_HASH_BUFF or + cursor_trans.y-G.ROOM.T.y < -G.DRAW_HASH_BUFF or cursor_trans.y-G.ROOM.T.y > G.TILE_H + G.DRAW_HASH_BUFF then + return + end + + local DRAW_HASH_SQUARE = G.DRAW_HASH + for i = #DRAW_HASH_SQUARE, 1, -1 do + local v = DRAW_HASH_SQUARE[i] + if v:collides_with_point(cursor_trans) and not v.REMOVED then + self.nodes_at_cursor[#self.nodes_at_cursor+1] = v + if v.states.collide.can then + v.states.collide.is = true + self.collision_list[#self.collision_list+1] = v + end + end + end +end + +function Controller:set_cursor_hover() + self.cursor_hover.T = self.cursor_hover.T or {} + self.cursor_hover.T.x, self.cursor_hover.T.y =G.CURSOR.T.x, G.CURSOR.T.y + self.cursor_hover.time = G.TIMERS.TOTAL + + self.cursor_hover.prev_target = self.cursor_hover.target + self.cursor_hover.target = nil + + if self.interrupt.focus or ((self.locked) and (not G.SETTINGS.paused or G.screenwipe)) or self.locks.frame or self.COYOTE_FOCUS then self.cursor_hover.target = G.ROOM; return end + + if self.HID.controller and self.focused.target and self.focused.target.states.hover.can then + if (self.HID.dpad or self.HID.axis_cursor) and self.focused.target.states.collide.is then + self.cursor_hover.target = self.focused.target + else + for _, v in ipairs(self.collision_list) do + if v.states.hover.can then + self.cursor_hover.target = v + break + end + end + end + else + for _, v in ipairs(self.collision_list) do + if v.states.hover.can and (not v.states.drag.is or self.HID.touch) then + self.cursor_hover.target = v + break + end + end + end + + if not self.cursor_hover.target or (self.dragging.target and not self.HID.touch) then self.cursor_hover.target = G.ROOM end + if self.cursor_hover.target ~= self.cursor_hover.prev_target then self.cursor_hover.handled = false end +end + +function Controller:queue_L_cursor_press(x, y) + if self.locks.frame then return end + if G.STATE == G.STATES.SPLASH then + self:key_press('escape') + end + self.L_cursor_queue = {x = x, y = y} +end + +function Controller:queue_R_cursor_press(x, y) + if self.locks.frame then return end + if not G.SETTINGS.paused and G.hand and G.hand.highlighted[1] then + if (G.play and #G.play.cards > 0) or + (self.locked) or + (self.locks.frame) or + (G.GAME.STOP_USE and G.GAME.STOP_USE > 0) then return end + G.hand:unhighlight_all() + end +end + +function Controller:L_cursor_press(x, y) + x = x or self.cursor_position.x + y = y or self.cursor_position.y + + if ((self.locked) and (not G.SETTINGS.paused or G.screenwipe)) or (self.locks.frame) then return end + + self.cursor_down.T = {x = x/(G.TILESCALE*G.TILESIZE), y = y/(G.TILESCALE*G.TILESIZE)} + self.cursor_down.time = G.TIMERS.TOTAL + self.cursor_down.handled = false + self.cursor_down.target = nil + self.is_cursor_down = true + + local press_node = (self.HID.touch and self.cursor_hover.target) or self.hovering.target or self.focused.target + + if press_node then + self.cursor_down.target = press_node.states.click.can and press_node or press_node:can_drag() or nil + end + + if self.cursor_down.target == nil then + self.cursor_down.target = G.ROOM + end +end + +function Controller:L_cursor_release(x, y) + x = x or self.cursor_position.x + y = y or self.cursor_position.y + + if ((self.locked) and (not G.SETTINGS.paused or G.screenwipe)) or (self.locks.frame) then return end + + self.cursor_up.T = {x = x/(G.TILESCALE*G.TILESIZE), y = y/(G.TILESCALE*G.TILESIZE)} + self.cursor_up.time = G.TIMERS.TOTAL + self.cursor_up.handled = false + self.cursor_up.target = nil + self.is_cursor_down = false + + self.cursor_up.target = self.hovering.target or self.focused.target + + if self.cursor_up.target == nil then + self.cursor_up.target = G.ROOM + end +end + +function Controller:is_node_focusable(node) + local ret_val = false + if node.T.y > G.ROOM.T.h + 3 then return false end + if not node.REMOVED and not node.under_overlay and (node.states.hover.can and not self.dragging.target or self.dragging.target == node) and + (not not node.created_on_pause) == (not not G.SETTINGS.paused) and + (node.states.visible) and (not node.UIBox or node.UIBox.states.visible) then + if self.screen_keyboard then + if node.UIBox and node.UIBox == self.screen_keyboard and node.config.button then + ret_val = true + end + else + if node:is(Card) and (node.facing == 'front' or node.area == G.hand or node.area == G.jokers or (node == G.deck)) and + node.states.hover.can and not node.jimbo then + ret_val = true + end + if node.config and node.config.force_focus then ret_val = true end + if node.config and node.config.button then ret_val = true end + if node.config and node.config.focus_args then + if node.config.focus_args.type == 'none' or node.config.focus_args.funnel_from then + ret_val = false + else + ret_val = true + end + end + end + end + return ret_val +end + +--essentially works like 'hovering' with any nodes that are focusable, but +--the nodes can also be navigated to via controller key inputs. If no direction is supplied, +--this function focuses on any nodes that collide with this cursor. If a direction is +--supplied, focuses on the nearest node in that direction. +function Controller:update_focus(dir) + self.focused.prev_target = self.focused.target + + --Only needed when using a controller, hovering covers all KBM scenarios + if not self.HID.controller or self.interrupt.focus or (self.locked) and (not G.SETTINGS.paused or G.screenwipe) then + if self.focused.target then self.focused.target.states.focus.is = false end + self.focused.target = nil + return + end + + G.ARGS.focus_list = EMPTY(G.ARGS.focus_list) + G.ARGS.focusables = EMPTY(G.ARGS.focusables) + + ------------------------------------------------- + -- Find the focusable starting point + ------------------------------------------------- + --First, is there currently a focusable target that is still valid? + if self.focused.target then + self.focused.target.states.focus.is = false + + --If that node is no longer focusable or if the cursor no longer collides with the node, remove the target + if not self:is_node_focusable(self.focused.target) or not self.focused.target:collides_with_point(G.CURSOR.T) or self.HID.axis_cursor then + self.focused.target = nil + end + end + + --Now we check for 3 criteria: + --1: is there a current focused target and no dpad direction? if so, we simply add the currsnt focused target to the focusable list and set the state to true + --2: if not, and there is no dpad direction, iterate through the node list that the cursor intersects and check if any are focusable, only add the first one + --3: if there is a direction, add all focusable moveables to the focusable list to check later + if not dir and self.focused.target then + self.focused.target.states.focus.can = true + G.ARGS.focusables[#G.ARGS.focusables+1] = self.focused.target + else + if not dir then + for k, v in ipairs(self.nodes_at_cursor) do + v.states.focus.can = false + v.states.focus.is = false + if #G.ARGS.focusables == 0 and self:is_node_focusable(v) then + v.states.focus.can = true + G.ARGS.focusables[#G.ARGS.focusables+1] = v + end + end + else + for k, v in pairs(G.MOVEABLES) do + v.states.focus.can = false + v.states.focus.is = false + if self:is_node_focusable(v) then + v.states.focus.can = true + G.ARGS.focusables[#G.ARGS.focusables+1] = v + end + end + end + end + + --If there are any valid focusables + if #G.ARGS.focusables > 0 then + --If a direction control is supplied, set the target to be the closest node in that direction + if dir then + if (dir == 'L' or dir == 'R') and self.focused.target and self.focused.target:is(Card) and self.focused.target.area == G.hand and G.hand then + local nu_rank = self.focused.target.rank + (dir == 'L' and -1 or 1) + if nu_rank > #G.hand.cards then nu_rank = 1 end + if nu_rank == 0 then nu_rank = #G.hand.cards end + if nu_rank ~= self.focused.target.rank then G.ARGS.focus_list[1] = {node = G.hand.cards[nu_rank]} end + else + --set the cursor position to where it currently is on screen + G.ARGS.focus_cursor_pos = G.ARGS.focus_cursor_pos or {} + G.ARGS.focus_cursor_pos.x, G.ARGS.focus_cursor_pos.y = G.CURSOR.T.x - G.ROOM.T.x, G.CURSOR.T.y - G.ROOM.T.y + + --if there is a focused target, set the cursor to the midpoint + if self.focused.target then + _ft = self.focused.target + if self.focused.target.config.focus_args and self.focused.target.config.focus_args.funnel_to then + _ft = self.focused.target.config.focus_args.funnel_to + end + G.ARGS.focus_cursor_pos.x, G.ARGS.focus_cursor_pos.y = _ft.T.x + 0.5*_ft.T.w,_ft.T.y + 0.5*_ft.T.h + --if not but there is a focusable hovering target, put the cursor on it instead + elseif self.hovering.target and self.hovering.target.states.focus.can then + G.ARGS.focus_cursor_pos.x, G.ARGS.focus_cursor_pos.y = self.hovering.target:put_focused_cursor() + G.ARGS.focus_cursor_pos.x = G.ARGS.focus_cursor_pos.x / (G.TILESCALE*G.TILESIZE) - G.ROOM.T.x + G.ARGS.focus_cursor_pos.y = G.ARGS.focus_cursor_pos.y / (G.TILESCALE*G.TILESIZE) - G.ROOM.T.y + end + + --set the list to be all the nodes in that direction sorted by the closest node + for _, v in pairs(G.ARGS.focusables) do + if v ~= self.hovering.target and v ~= self.focused.target then + local eligible = false + + if v.config.focus_args and v.config.focus_args.funnel_to then + v = v.config.focus_args.funnel_to + end + + G.ARGS.focus_vec = G.ARGS.focus_vec or {} + G.ARGS.focus_vec.x = v.T.x + 0.5*v.T.w - (G.ARGS.focus_cursor_pos.x) + G.ARGS.focus_vec.y = v.T.y + 0.5*v.T.h - (G.ARGS.focus_cursor_pos.y) + + if v.config.focus_args and v.config.focus_args.nav then + if v.config.focus_args.nav == 'wide' then + if G.ARGS.focus_vec.y > 0.1 and dir == 'D' then eligible = true + elseif G.ARGS.focus_vec.y < -0.1 and dir == 'U' then eligible = true + elseif math.abs(G.ARGS.focus_vec.y) < v.T.h/2 then eligible = true end + elseif v.config.focus_args.nav == 'tall' then + if G.ARGS.focus_vec.x > 0.1 and dir == 'R' then eligible = true + elseif G.ARGS.focus_vec.x < -0.1 and dir == 'L' then eligible = true + elseif math.abs(G.ARGS.focus_vec.x) < v.T.w/2 then eligible = true end + end + elseif math.abs(G.ARGS.focus_vec.x) > math.abs(G.ARGS.focus_vec.y) then + if G.ARGS.focus_vec.x > 0 and dir == 'R' then eligible = true + elseif G.ARGS.focus_vec.x < 0 and dir == 'L' then eligible = true end + else + if G.ARGS.focus_vec.y > 0 and dir == 'D' then eligible = true + elseif G.ARGS.focus_vec.y < 0 and dir == 'U' then eligible = true end + end + + if eligible then + G.ARGS.focus_list[#G.ARGS.focus_list+1] = {node = v, dist = math.abs(G.ARGS.focus_vec.x) + math.abs(G.ARGS.focus_vec.y)} + end + end + end + if #G.ARGS.focus_list < 1 then + if self.focused.target then self.focused.target.states.focus.is = true end + return + end + table.sort(G.ARGS.focus_list, function (a, b) return a.dist < b.dist end) + end + else + if self.focused.target then + G.ARGS.focus_list[#G.ARGS.focus_list+1] = {node = self.focused.target, dist = 0} + else + --otherwise, get the focusable that collides + G.ARGS.focus_list[#G.ARGS.focus_list+1] = {node = G.ARGS.focusables[1], dist = 0} + end + end + end + + --now with the lists created, set the focused target to be the first node in the list + if G.ARGS.focus_list[1] then + if G.ARGS.focus_list[1].node.config and G.ARGS.focus_list[1].node.config.focus_args and G.ARGS.focus_list[1].node.config.focus_args.funnel_from then + self.focused.target = G.ARGS.focus_list[1].node.config.focus_args.funnel_from + else + self.focused.target = G.ARGS.focus_list[1].node + end + if self.focused.target ~= self.focused.prev_target then G.VIBRATION = G.VIBRATION + 0.7 end + else + self.focused.target = nil + end + + if self.focused.target then self.focused.target.states.focus.is = true end +end + +function Controller:capture_focused_input(button, input_type, dt) + local ret = false + local focused = self.focused.target + local extern_button = false + self.no_holdcap = nil + + --Implementing 'coyote time' type selection where a full button press isnt needed to select a card in hand. As long as a button down has been registered + --before a timer is up and the dpad is used to move to the next card it should register + if input_type == 'press' and (button == 'dpleft' or button == 'dpright') and + focused and self.dragging.target and + (self.held_button_times['a'] and self.held_button_times['a'] < 0.12) and + focused.area and focused.area:can_highlight(focused) then + self:L_cursor_release() + self:navigate_focus(button == 'dpleft' and 'L' or 'R') + self.held_button_times['a'] = nil + self.COYOTE_FOCUS = true + ret = true + elseif input_type == 'press' and focused and focused.area and focused == self.dragging.target then + focused.states.drag.is = false + if button == 'dpleft' and focused.rank > 1 then + focused.rank = focused.rank - 1 + focused.area.cards[focused.rank].rank = focused.rank + 1 + table.sort(focused.area.cards, function (a, b) return a.rank < b.rank end) + focused.area:align_cards() + self:update_cursor() + elseif button == 'dpright' and focused.rank < #focused.area.cards then + focused.rank = focused.rank + 1 + focused.area.cards[focused.rank].rank = focused.rank - 1 + table.sort(focused.area.cards, function (a, b) return a.rank < b.rank end) + focused.area:align_cards() + self:update_cursor() + end + focused.states.drag.is = true + ret = true + end + + if G.OVERLAY_MENU and not self.screen_keyboard and input_type == 'press' and G.OVERLAY_MENU:get_UIE_by_ID('tab_shoulders') and (button == 'leftshoulder' or button == 'rightshoulder') then + focused = G.OVERLAY_MENU:get_UIE_by_ID('tab_shoulders') + extern_button = true + end + if G.OVERLAY_MENU and not self.screen_keyboard and input_type == 'press' and G.OVERLAY_MENU:get_UIE_by_ID('cycle_shoulders') and (button == 'leftshoulder' or button == 'rightshoulder') then + focused = G.OVERLAY_MENU:get_UIE_by_ID('cycle_shoulders').children[1] + extern_button = true + end + if focused and focused.config.focus_args then + if focused.config.focus_args.type == 'cycle' and input_type == 'press' then + if ((extern_button and button == 'leftshoulder') or (not extern_button and button == 'dpleft')) then + focused.children[1]:click() + ret = true + end + if ((extern_button and button == 'rightshoulder') or (not extern_button and button == 'dpright')) then + focused.children[3]:click() + ret = true + end + end + if focused.config.focus_args.type == 'tab' and input_type == 'press' then + local proto_choices = focused.UIBox:get_group(nil, focused.children[1].children[1].config.group) + local choices = {} + for k, v in ipairs(proto_choices) do + if v.config.choice and v.config.button then choices[#choices+1] = v end + end + for k, v in ipairs(choices) do + if v.config.chosen then + if ((extern_button and button == 'leftshoulder') or (not extern_button and button == 'dpleft')) then + local next_i = k ~= 1 and (k-1) or (#choices) + if focused.config.focus_args.no_loop and next_i > k then ret = nil + else + choices[next_i]:click() + self:snap_to({node = choices[next_i]}) + self:update_cursor() + ret = true + end + elseif ((extern_button and button == 'rightshoulder') or (not extern_button and button == 'dpright')) then + local next_i = k ~= #choices and (k+1) or (1) + if focused.config.focus_args.no_loop and next_i < k then ret = nil + else + choices[next_i]:click() + self:snap_to({node = choices[next_i]}) + self:update_cursor() + ret = true + end + end + break + end + end + end + if focused.config.focus_args.type == 'slider' then + if button == 'dpleft' then + self.no_holdcap = true + if input_type == 'hold' and self.held_button_times[button] > 0.2 then + G.FUNCS.slider_descreet(focused.children[1], -dt*self.held_button_times[button]*0.6) + end + if input_type == 'press' then + G.FUNCS.slider_descreet(focused.children[1], -0.01) + end + ret = true + end + if button == 'dpright' then + self.no_holdcap = true + if input_type == 'hold' and self.held_button_times[button] > 0.2 then + G.FUNCS.slider_descreet(focused.children[1], dt*self.held_button_times[button]*0.6) + end + if input_type == 'press' then + G.FUNCS.slider_descreet(focused.children[1], 0.01) + end + ret = true + end + end + end + if ret == true then G.VIBRATION = G.VIBRATION +1 end + return ret +end + +function Controller:navigate_focus(dir) + --Get the corresponding focus target first, with or without a direction + self:update_focus(dir) + + --Set the cursor to be in the correct position for that target + self:update_cursor() +end + diff --git a/lovely/dump/engine/moveable.lua b/lovely/dump/engine/moveable.lua new file mode 100644 index 0000000..4ad58fa --- /dev/null +++ b/lovely/dump/engine/moveable.lua @@ -0,0 +1,520 @@ +LOVELY_INTEGRITY = '2774a24114332371bc4208f854d32c6f93786326d6810fc4139b615d177277e3' + +---@class Moveable: Node +Moveable = Node:extend() + +--Moveable represents any game object that has the ability to move about the gamespace.\ +--All Moveables have a T (transform) that describes their desired transform in game units, as\ +--well as a VT (Visible Transform) that eases to T over time. This allows for simplified movement where\ +--we only need to set T.x, T.y, etc. to their final position and the engine will ensure the Moveable\ +--VT eases to that final location, regargless of any events or timing. +-- +---@param args {T: table, container: Node} +--**T** The transform ititializer, with keys of x|1, y|2, w|3, h|4, r|5\ +--**container** optional container for this Node, defaults to G.ROOM +function Moveable:init(X,Y,W,H) + local args = (type(X) == 'table') and X or {T ={X or 0,Y or 0,W or 0,H or 0}} + Node.init(self, args) + + --The Visible transform is initally set to the same values as the transform T. + --Note that the VT has an extra 'scale' factor, this is used to manipulate the center-adjusted + --scale of any objects that need to be drawn larger or smaller + self.VT = { + x = self.T.x, + y = self.T.y, + w = self.T.w, + h = self.T.h, + r = self.T.r, + scale = self.T.scale + } + + --To determine location of VT, we need to keep track of the velocity of VT as it approaches T for the next frame + self.velocity = {x = 0, y = 0, r = 0, scale = 0, mag = 0} + + --For more robust drawing, attaching, movement and fewer redundant movement calculations, Moveables each have a 'role' + --that describes a heirarchy of move() calls. Any Moveables with 'Major' role type behave normally, essentially recalculating their + --VT every frame to ensure smooth movement. Moveables can be set to 'Minor' role and attached to some 'Major' moveable + --to weld the Minor moveable to the Major moveable. This makes the dependent moveable set their T and VT to be equal to + --the corresponding 'Major' T and VT, plus some defined offset. + --For finer control over what parts of T and VT are inherited, xy_bond, wh_bond, and r_bond can be set to one of + --'Strong' or 'Weak'. Strong simply copies the values, Weak allows the 'Minor' moveable to calculate their own. + self.role = { + role_type = 'Major', --Major dictates movement, Minor is welded to some major + offset = {x = 0, y = 0}, --Offset from Minor to Major + major = nil, + draw_major = self, + xy_bond = 'Strong', + wh_bond = 'Strong', + r_bond = 'Strong', + scale_bond = 'Strong' + } + + self.alignment = { + type = 'a', + offset = {x = 0, y = 0}, + prev_type = '', + prev_offset = {x = 0, y = 0}, + } + + --the pinch table is used to modify the VT.w and VT.h compared to T.w and T.h. If either x or y pinch is + --set to true, the VT width and or height will ease to 0. If pinch is false, they ease to T.w or T.h + self.pinch = {x = false, y = false} + + --Keep track of the last time this Moveable was moved via :move(dt). When it is successfully moved, set to equal + --the current G.TIMERS.REAL, and if it is called again this frame, doesn't recalculate move(dt) + self.last_moved = -1 + self.last_aligned = -1 + + self.static_rotation = false + + self.offset = {x=0, y=0} + self.Mid = self + + self.shadow_parrallax = {x = 0, y = -1.5} + self.layered_parallax = {x = 0, y = 0} + self.shadow_height = 0.2 + + self:calculate_parrallax() + + table.insert(G.MOVEABLES, self) + if getmetatable(self) == Moveable then + table.insert(G.I.MOVEABLE, self) + end +end +function Moveable:draw() + Node.draw(self) + self:draw_boundingrect() +end + +--Sets the alignment of moveable using roles +-- +---@param args {major: Moveable, bond: string, offset: table, type: string} +--**major** The moveable this moveable will attach to\ +--**bond** The bond type, either 'Strong' or 'Weak'. Strong instantly adjusts VT, Weak manually calculates VT changes\ +--**offset** {x , y} offset from the alignment\ +--**type** the alignment type. Vertical options: c - center, t - top, b - bottom. Horizontal options: l - left, m - middle, r - right. i for inner +function Moveable:set_alignment(args) + args = args or {} + if args.major then + self:set_role({ + role_type = 'Minor', + major = args.major, + xy_bond = args.bond or args.xy_bond or 'Weak', + wh_bond = args.wh_bond or self.role.wh_bond, + r_bond = args.r_bond or self.role.r_bond, + scale_bond = args.scale_bond or self.role.scale_bond, + }) + end + self.alignment.type = args.type or self.alignment.type + if args.offset and (type(args.offset)=='table' and not (args.offset.y and args.offset.x)) or type(args.offset) ~= 'table' then + args.offset = nil + end + self.alignment.offset = args.offset or self.alignment.offset + self.alignment.lr_clamp = args.lr_clamp +end + +function Moveable:align_to_major() + if self.alignment.type ~= self.alignment.prev_type then + self.alignment.type_list = { + a = self.alignment.type == 'a', + m = string.find(self.alignment.type, "m"), + c = string.find(self.alignment.type, "c"), + b = string.find(self.alignment.type, "b"), + t = string.find(self.alignment.type, "t"), + l = string.find(self.alignment.type, "l"), + r = string.find(self.alignment.type, "r"), + i = string.find(self.alignment.type, "i"), + } + end + + if not self.alignment.type_list then return end + self.NEW_ALIGNMENT = true + + if self.alignment.type ~= self.alignment.prev_type then + self.alignment.prev_type = self.alignment.type + end + + if self.alignment.type_list.a or not self.role.major then return end + + if self.alignment.type_list.m then + self.role.offset.x = 0.5*self.role.major.T.w - (self.Mid.T.w)/2 + self.alignment.offset.x - self.Mid.T.x + self.T.x + end + + if self.alignment.type_list.c then + self.role.offset.y = 0.5*self.role.major.T.h - (self.Mid.T.h)/2 + self.alignment.offset.y - self.Mid.T.y + self.T.y + end + + if self.alignment.type_list.b then + if self.alignment.type_list.i then + self.role.offset.y = self.alignment.offset.y + self.role.major.T.h - self.T.h + else + self.role.offset.y = self.alignment.offset.y + self.role.major.T.h + end + end + + if self.alignment.type_list.r then + if self.alignment.type_list.i then + self.role.offset.x = self.alignment.offset.x + self.role.major.T.w - self.T.w + else + self.role.offset.x = self.alignment.offset.x + self.role.major.T.w + end + end + + if self.alignment.type_list.t then + if self.alignment.type_list.i then + self.role.offset.y = self.alignment.offset.y + else + self.role.offset.y = self.alignment.offset.y - self.T.h + end + end + + if self.alignment.type_list.l then + if self.alignment.type_list.i then + self.role.offset.x = self.alignment.offset.x + else + self.role.offset.x = self.alignment.offset.x - self.T.w + end + end + + self.role.offset.x = self.role.offset.x or 0 + self.role.offset.y = self.role.offset.y or 0 + + self.T.x = self.role.major.T.x + self.role.offset.x + self.T.y = self.role.major.T.y + self.role.offset.y + + self.alignment.prev_offset = self.alignment.prev_offset or {} + self.alignment.prev_offset.x, self.alignment.prev_offset.y = self.alignment.offset.x, self.alignment.offset.y +end + +function Moveable:hard_set_T(X, Y, W, H) + self.T.x = X + self.T.y = Y + self.T.w = W + self.T.h = H + + self.velocity.x = 0 + self.velocity.y = 0 + self.velocity.r = 0 + self.velocity.scale = 0 + + self.VT.x = X + self.VT.y = Y + self.VT.w = W + self.VT.h = H + self.VT.r = self.T.r + self.VT.scale = self.T.scale + self:calculate_parrallax() +end + +function Moveable:hard_set_VT() + self.VT.x = self.T.x + self.VT.y = self.T.y + self.VT.w = self.T.w + self.VT.h = self.T.h +end + +function Moveable:drag(offset) + if self.states.drag.can or offset then + self.ARGS.drag_cursor_trans = self.ARGS.drag_cursor_trans or {} + self.ARGS.drag_translation = self.ARGS.drag_translation or {} + local _p = self.ARGS.drag_cursor_trans + local _t = self.ARGS.drag_translation + _p.x = G.CONTROLLER.cursor_position.x/(G.TILESCALE*G.TILESIZE) + _p.y = G.CONTROLLER.cursor_position.y/(G.TILESCALE*G.TILESIZE) + + _t.x, _t.y = -self.container.T.w/2, -self.container.T.h/2 + point_translate(_p, _t) + + point_rotate(_p, self.container.T.r) + + _t.x, _t.y = self.container.T.w/2-self.container.T.x, self.container.T.h/2-self.container.T.y + point_translate(_p, _t) + + if not offset then + offset = self.click_offset + end + + self.T.x = _p.x - offset.x + self.T.y = _p.y - offset.y + self.NEW_ALIGNMENT = true + for k, v in pairs(self.children) do + v:drag(offset) + end + end + if self.states.drag.can then + Node.drag(self) + end +end + +function Moveable:juice_up(amount, rot_amt) + if G.SETTINGS.reduced_motion then return end + local amount = amount or 0.4 + + local end_time = G.TIMERS.REAL + 0.4 + local start_time = G.TIMERS.REAL + self.juice = { + scale = 0, + scale_amt = amount, + r = 0, + r_amt = ((rot_amt or pseudorandom_element({0.6*amount, -0.6*amount})) or 0), + start_time = start_time, + end_time = end_time + } + self.VT.scale = 1-0.6*amount +end + +function Moveable:move_juice(dt) + if self.juice and not self.juice.handled_elsewhere then + if self.juice.end_time < G.TIMERS.REAL then + self.juice = nil + else + self.juice.scale = self.juice.scale_amt*math.sin(50.8*(G.TIMERS.REAL-self.juice.start_time))*math.max(0, ((self.juice.end_time - G.TIMERS.REAL)/(self.juice.end_time - self.juice.start_time))^3) + self.juice.r = self.juice.r_amt*math.sin(40.8*(G.TIMERS.REAL-self.juice.start_time))*math.max(0, ((self.juice.end_time - G.TIMERS.REAL)/(self.juice.end_time - self.juice.start_time))^2) + end + end +end + +function Moveable:move(dt) + if self.FRAME.MOVE >= G.FRAMES.MOVE then return end + self.FRAME.OLD_MAJOR = self.FRAME.MAJOR + self.FRAME.MAJOR = nil + self.FRAME.MOVE = G.FRAMES.MOVE + if not self.created_on_pause and G.SETTINGS.paused then return end + + --WHY ON EARTH DOES THIS LINE MAKE IT RUN 2X AS FAST??? + ------------------------------------------------------- + --local timestart = love.timer.getTime() + ------------------------------------------------------- + + self:align_to_major() + + self.CALCING = nil + if self.role.role_type == 'Glued' then + if self.role.major then self:glue_to_major(self.role.major) end + elseif self.role.role_type == 'Minor' and self.role.major then + if self.role.major.FRAME.MOVE < G.FRAMES.MOVE then self.role.major:move(dt) end + self.STATIONARY = self.role.major.STATIONARY + if (not self.STATIONARY) or self.NEW_ALIGNMENT or + self.config.refresh_movement or + self.juice or + self.role.xy_bond == 'Weak' or + self.role.r_bond == 'Weak' then + self.CALCING = true + self:move_with_major(dt) + end + elseif self.role.role_type == 'Major' then + self.STATIONARY = true + self:move_juice(dt) + self:move_xy(dt) + self:move_r(dt, self.velocity) + self:move_scale(dt) + self:move_wh(dt) + self:calculate_parrallax() + end + if self.alignment and self.alignment.lr_clamp then + self:lr_clamp() + end + + self.NEW_ALIGNMENT = false +end + +function Moveable:lr_clamp() + if self.T.x < 0 then self.T.x = 0 end + if self.VT.x < 0 then self.VT.x = 0 end + if (self.T.x + self.T.w) > G.ROOM.T.w then self.T.x = G.ROOM.T.w - self.T.w end + if (self.VT.x + self.VT.w) > G.ROOM.T.w then self.VT.x = G.ROOM.T.w - self.VT.w end +end + +function Moveable:glue_to_major(major_tab) + self.T = major_tab.T + + self.VT.x = major_tab.VT.x + (0.5*(1 - major_tab.VT.w/(major_tab.T.w))*self.T.w) + self.VT.y = major_tab.VT.y + self.VT.w = major_tab.VT.w + self.VT.h = major_tab.VT.h + self.VT.r = major_tab.VT.r + self.VT.scale = major_tab.VT.scale + + self.pinch = major_tab.pinch + self.shadow_parrallax = major_tab.shadow_parrallax +end + +MWM = { + rotated_offset = {}, + angles = {}, + WH = {}, + offs = {}, +} + +function Moveable:move_with_major(dt) + if self.role.role_type ~= 'Minor' then return end + local major_tab = self.role.major:get_major() + + self:move_juice(dt) + + if self.role.r_bond == 'Weak' then + MWM.rotated_offset.x, MWM.rotated_offset.y = self.role.offset.x + major_tab.offset.x,self.role.offset.y+major_tab.offset.y + else + if major_tab.major.VT.r < 0.0001 and major_tab.major.VT.r > -0.0001 then + MWM.rotated_offset.x = self.role.offset.x + major_tab.offset.x + MWM.rotated_offset.y = self.role.offset.y + major_tab.offset.y + else + MWM.angles.cos, MWM.angles.sin = math.cos(major_tab.major.VT.r),math.sin(major_tab.major.VT.r) + MWM.WH.w, MWM.WH.h = -self.T.w/2 + major_tab.major.T.w/2,-self.T.h/2 + major_tab.major.T.h/2 + MWM.offs.x, MWM.offs.y = self.role.offset.x + major_tab.offset.x - MWM.WH.w,self.role.offset.y + major_tab.offset.y - MWM.WH.h + + MWM.rotated_offset.x = MWM.offs.x*MWM.angles.cos - MWM.offs.y*MWM.angles.sin + MWM.WH.w + MWM.rotated_offset.y = MWM.offs.x*MWM.angles.sin + MWM.offs.y*MWM.angles.cos + MWM.WH.h + end + end + + self.T.x = major_tab.major.T.x + MWM.rotated_offset.x + self.T.y = major_tab.major.T.y + MWM.rotated_offset.y + + if self.role.xy_bond == 'Strong' then + self.VT.x = major_tab.major.VT.x + MWM.rotated_offset.x + self.VT.y = major_tab.major.VT.y + MWM.rotated_offset.y + elseif self.role.xy_bond == 'Weak' then + self:move_xy(dt) + end + + if self.role.r_bond == 'Strong' then + self.VT.r = self.T.r + major_tab.major.VT.r + (self.juice and self.juice.r or 0) + elseif self.role.r_bond == 'Weak' then + self:move_r(dt, self.velocity) + end + + if self.role.scale_bond == 'Strong' then + self.VT.scale = self.T.scale*(major_tab.major.VT.scale/major_tab.major.T.scale) + (self.juice and self.juice.scale or 0) + elseif self.role.scale_bond == 'Weak' then + self:move_scale(dt) + end + + if self.role.wh_bond == 'Strong' then + self.VT.x = self.VT.x + (0.5*(1 - major_tab.major.VT.w/(major_tab.major.T.w))*self.T.w) + self.VT.w = (self.T.w)*(major_tab.major.VT.w/major_tab.major.T.w) + self.VT.h = (self.T.h)*(major_tab.major.VT.h/major_tab.major.T.h) + elseif self.role.wh_bond == 'Weak' then + self:move_wh(dt) + end + + self:calculate_parrallax() +end + +function Moveable:move_xy(dt) + if (self.T.x ~= self.VT.x or math.abs(self.velocity.x) > 0.01) or + (self.T.y ~= self.VT.y or math.abs(self.velocity.y) > 0.01) then + self.velocity.x = G.exp_times.xy*self.velocity.x + (1-G.exp_times.xy)*(self.T.x - self.VT.x)*35*dt + self.velocity.y = G.exp_times.xy*self.velocity.y + (1-G.exp_times.xy)*(self.T.y - self.VT.y)*35*dt + if self.velocity.x*self.velocity.x + self.velocity.y*self.velocity.y > G.exp_times.max_vel*G.exp_times.max_vel then + local actual_vel = math.sqrt(self.velocity.x*self.velocity.x + self.velocity.y*self.velocity.y) + self.velocity.x = G.exp_times.max_vel*self.velocity.x/actual_vel + self.velocity.y = G.exp_times.max_vel*self.velocity.y/actual_vel + end + self.STATIONARY = false + self.VT.x = self.VT.x + self.velocity.x + self.VT.y = self.VT.y + self.velocity.y + if math.abs(self.VT.x - self.T.x) < 0.01 and math.abs(self.velocity.x) < 0.01 then self.VT.x = self.T.x; self.velocity.x = 0 end + if math.abs(self.VT.y - self.T.y) < 0.01 and math.abs(self.velocity.y) < 0.01 then self.VT.y = self.T.y; self.velocity.y = 0 end + end +end + +function Moveable:move_scale(dt) + local des_scale = self.T.scale + (self.zoom and ((self.states.drag.is and 0.1 or 0) + (self.states.hover.is and 0.05 or 0)) or 0) + (self.juice and self.juice.scale or 0) + + if des_scale ~= self.VT.scale or + math.abs(self.velocity.scale) > 0.001 then + self.STATIONARY = false + self.velocity.scale = G.exp_times.scale*self.velocity.scale + (1-G.exp_times.scale)*(des_scale - self.VT.scale) + self.VT.scale = self.VT.scale + self.velocity.scale + end +end + +function Moveable:move_wh(dt) +if Big and G.STATE == G.STATES.MENU then self.T.w = to_big(self.T.w):to_number() +self.T.h = to_big(self.T.h):to_number() +self.VT.w = to_big(self.VT.w):to_number() +self.VT.h = to_big(self.VT.h):to_number() end + if (self.T.w ~= self.VT.w and not self.pinch.x) or + (self.T.h ~= self.VT.h and not self.pinch.y) or + (self.VT.w > 0 and self.pinch.x) or + (self.VT.h > 0 and self.pinch.y) then + self.STATIONARY = false + self.VT.w = self.VT.w + (8*dt)*(self.pinch.x and -1 or 1)*self.T.w + self.VT.h = self.VT.h + (8*dt)*(self.pinch.y and -1 or 1)*self.T.h + self.VT.w = math.max(math.min(self.VT.w, self.T.w), 0) + self.VT.h = math.max(math.min(self.VT.h, self.T.h), 0) + end +end + +function Moveable:move_r(dt, vel) + local des_r = self.T.r +0.015*vel.x/dt + (self.juice and self.juice.r*2 or 0) + + if des_r ~= self.VT.r or + math.abs(self.velocity.r) > 0.001 then + self.STATIONARY = false + self.velocity.r = G.exp_times.r*self.velocity.r + (1-G.exp_times.r)*(des_r - self.VT.r) + self.VT.r = self.VT.r + self.velocity.r + end + if math.abs(self.VT.r - self.T.r) < 0.001 and math.abs(self.velocity.r) < 0.001 then self.VT.r = self.T.r; self.velocity.r = 0 end +end + +function Moveable:calculate_parrallax() + if not G.ROOM then return end + self.shadow_parrallax.x = (self.T.x + self.T.w/2 - G.ROOM.T.w/2)/(G.ROOM.T.w/2)*1.5 +end + +function Moveable:set_role(args) + if args.major and not args.major.set_role then return end + if args.offset and (type(args.offset)=='table' and not (args.offset.y and args.offset.x)) or type(args.offset) ~= 'table' then + args.offset = nil + end + self.role = { + role_type = args.role_type or self.role.role_type, + offset = args.offset or self.role.offset, + major = args.major or self.role.major, + xy_bond = args.xy_bond or self.role.xy_bond, + wh_bond = args.wh_bond or self.role.wh_bond, + r_bond = args.r_bond or self.role.r_bond, + scale_bond = args.scale_bond or self.role.scale_bond, + draw_major = args.draw_major or self.role.draw_major, + } + if self.role.role_type == 'Major' then self.role.major = nil end +end + +function Moveable:get_major() + if ( self.role.role_type ~= 'Major' and self.role.major ~= self) and (self.role.xy_bond ~= 'Weak' and self.role.r_bond ~= 'Weak') then + --First, does the major already have their offset precalculated for this frame? + if not self.FRAME.MAJOR or (G.REFRESH_FRAME_MAJOR_CACHE) then + self.FRAME.MAJOR = self.FRAME.MAJOR or EMPTY(self.FRAME.OLD_MAJOR) + self.temp_offs = EMPTY(self.temp_offs) + local major = self.role.major:get_major() + self.FRAME.MAJOR.major = major.major + self.FRAME.MAJOR.offset = self.FRAME.MAJOR.offset or self.temp_offs + self.FRAME.MAJOR.offset.x, self.FRAME.MAJOR.offset.y = major.offset.x + self.role.offset.x + self.layered_parallax.x, major.offset.y + self.role.offset.y + self.layered_parallax.y + end + return self.FRAME.MAJOR + else + self.ARGS.get_major = self.ARGS.get_major or {} + self.ARGS.get_major.major = self + self.ARGS.get_major.offset = self.ARGS.get_major.offset or {} + self.ARGS.get_major.offset.x, self.ARGS.get_major.offset.y = 0,0 + return self.ARGS.get_major + end +end + +function Moveable:remove() + for k, v in pairs(G.MOVEABLES) do + if v == self then + table.remove(G.MOVEABLES, k) + break; + end + end + for k, v in pairs(G.I.MOVEABLE) do + if v == self then + table.remove(G.I.MOVEABLE, k) + break; + end + end + Node.remove(self) +end diff --git a/lovely/dump/engine/sound_manager.lua b/lovely/dump/engine/sound_manager.lua new file mode 100644 index 0000000..9f5816b --- /dev/null +++ b/lovely/dump/engine/sound_manager.lua @@ -0,0 +1,222 @@ +LOVELY_INTEGRITY = '72872b3b4770af870f29bca0a9c04f8e62103c150ae3d5e6600706da63ac8e0b' + +require "love.audio" +require "love.sound" +require "love.system" + +if (love.system.getOS() == 'OS X' )and (jit.arch == 'arm64' or jit.arch == 'arm') then jit.off() end + +--vars needed for sound manager thread +CHANNEL = love.thread.getChannel("sound_request") +LOAD_CHANNEL = love.thread.getChannel('load_channel') +LOAD_CHANNEL:push('audio thread start') +DISABLE_SFX = false +SMODS_Sounds = {} + +--create all sounds from resources and play one each to load into mem +SOURCES = {} +local sound_files = love.filesystem.getDirectoryItems("resources/sounds") + +for _, filename in ipairs(sound_files) do + local extension = string.sub(filename, -4) + for i = 1, 1 do + if extension == '.ogg' then + LOAD_CHANNEL:push('audio file - '..filename) + local sound_code = string.sub(filename, 1, -5) + local s = { + sound = love.audio.newSource("resources/sounds/"..filename,string.find(sound_code,'music') and "stream" or 'static'), + filepath = "resources/sounds/"..filename + } + SOURCES[sound_code] = {} + table.insert(SOURCES[sound_code], s) + s.sound_code = sound_code + s.sound:setVolume(0) + love.audio.play(s.sound) + s.sound:stop() + end + end +end + +function PLAY_SOUND(args) + args.per = args.per or 1 + args.vol = args.vol or 1 + SOURCES[args.sound_code] = SOURCES[args.sound_code] or {} + + for _, s in ipairs(SOURCES[args.sound_code]) do + if s.sound and not s.sound:isPlaying() then + s.original_pitch = args.per + s.original_volume = args.vol + s.created_on_pause = args.overlay_menu + s.created_on_state = args.state + s.sfx_handled = 0 + s.transition_timer = 0 + SET_SFX(s, args) + love.audio.play(s.sound) + return s + end + end + + local should_stream = (string.find(args.sound_code,'music') or string.find(args.sound_code,'ambient')) + local c = SMODS_Sounds[args.sound_code] + local s = c and + {sound = love.audio.newSource(love.sound.newDecoder(c.data), c.should_stream and 'stream' or 'static'), per = c.per, vol = c.vol } or + {sound = love.audio.newSource("resources/sounds/"..args.sound_code..'.ogg', should_stream and "stream" or 'static')} + table.insert(SOURCES[args.sound_code], s) + s.sound_code = args.sound_code + s.original_pitch = ((args.type ~= "sound") and s.per) or args.per or 1 + s.original_volume = ((args.type ~= "sound") and s.vol) or args.vol or 1 + s.created_on_pause = (args.overlay_menu and true or false) + s.created_on_state = args.state + s.sfx_handled = 0 + s.transition_timer = 0 + SET_SFX(s, args) + love.audio.play(s.sound) + return s +end + +function STOP_AUDIO() + for _, source in pairs(SOURCES) do + for _, s in pairs(source) do + if s.sound:isPlaying() then + s.sound:stop() + end + end + end +end + +function SET_SFX(s, args) + if string.find(s.sound_code,'music') then + if s.sound_code == args.desired_track then + s.current_volume = s.current_volume or 1 + s.current_volume = 1*(args.dt*3) + (1-(args.dt*3))*s.current_volume + else + s.current_volume = s.current_volume or 0 + s.current_volume = 0*(args.dt*3) + (1-(args.dt*3))*s.current_volume + end + s.sound:setVolume(s.current_volume*s.original_volume*(args.sound_settings.volume/100.0)*(args.sound_settings.music_volume/100.0)) + s.sound:setPitch(s.original_pitch*args.pitch_mod) + else + if s.temp_pitch ~= s.original_pitch then + s.sound:setPitch(s.original_pitch) + s.temp_pitch = s.original_pitch + end + local sound_vol = s.original_volume*(args.sound_settings.volume/100.0)*(args.sound_settings.game_sounds_volume/100.0) + if s.created_on_state == 13 then sound_vol = sound_vol*args.splash_vol end + if sound_vol <= 0 then + s.sound:stop() + else + s.sound:setVolume(sound_vol) + end + end + end + + function MODULATE(args) + if args.desired_track ~= '' then + local sound = ((SOURCES[current_track or {}] or {})[1] or {}).sound + if not sound or not sound:isPlaying() then + RESTART_MUSIC(args) + end + end + + for k, v in pairs(SOURCES) do + local i=1 + while i <= #v do + if not v[i].sound:isPlaying() then + v[i].sound:release() + table.remove(v, i) + else + i = i + 1 + end + end + + current_track = args.desired_track + for _, s in pairs(v) do + if s.sound and s.sound:isPlaying() and s.original_volume then + SET_SFX(s, args) + end + end + end + end + + function RESTART_MUSIC(args) + for k, v in pairs(SOURCES) do + if string.find(k,'music') then + for i, s in ipairs(v) do + s.sound:stop() + end + SOURCES[k] = {} + args.per = 0.7 + args.vol = 0.6 + args.sound_code = k + local s = PLAY_SOUND(args) + s.initialized = true + end + end + end + + function AMBIENT(args) + for k, v in pairs(SOURCES) do + if args.ambient_control[k] then + local start_ambient = args.ambient_control[k].vol*(args.sound_settings.volume/100.0)*(args.sound_settings.game_sounds_volume/100.0) > 0 + + for i, s in ipairs(v) do + if s.sound and s.sound:isPlaying() and s.original_volume then + s.original_volume = args.ambient_control[k].vol + SET_SFX(s, args) + start_ambient = false + end + end + + if start_ambient then + args.sound_code = k + args.vol = args.ambient_control[k].vol + args.per = args.ambient_control[k].per + PLAY_SOUND(args) + end + end + end + end + + function RESET_STATES(state) + for k, v in pairs(SOURCES) do + for i, s in ipairs(v) do + s.created_on_state = state + end + end + end + + LOAD_CHANNEL:push('finished') + + while true do + --Monitor the channel for any new requests + local request = CHANNEL:demand() -- Value from channel + if request then + if request.type == 'kill' then return end + --If the request is for an update to the music track, handle it here + if false then elseif request.type == 'sound' then + PLAY_SOUND(request) + elseif request.type == 'sound_source' then + SMODS_Sounds[request.sound_code] = { + sound_code = request.sound_code, + data = request.data, + sound = sound, + per = request.per, + vol = request.vol, + } + SOURCES[request.sound_code] = {} + elseif request.type == 'stop' then + STOP_AUDIO() + elseif request.type == 'modulate' then + MODULATE(request) + if request.ambient_control then AMBIENT(request) end + elseif request.type == 'restart_music' then + RESTART_MUSIC(request) + elseif request.type == 'reset_states' then + for k, v in pairs(SOURCES) do + for i, s in ipairs(v) do + s.created_on_state = request.state + end + end + end + end +end diff --git a/lovely/dump/engine/sprite.lua b/lovely/dump/engine/sprite.lua new file mode 100644 index 0000000..5d2878c --- /dev/null +++ b/lovely/dump/engine/sprite.lua @@ -0,0 +1,232 @@ +LOVELY_INTEGRITY = '3d0395906e098682391897a81b05df12cd2dc6e39322ebd87a281c6c9d1caebc' + +--Class +Sprite = Moveable:extend() + +--Class Methods +function Sprite:init(X, Y, W, H, new_sprite_atlas, sprite_pos) + Moveable.init(self,X, Y, W, H) + self.CT = self.VT + self.atlas = new_sprite_atlas + self.scale = {x=self.atlas.px, y=self.atlas.py} + self.scale_mag = math.min(self.scale.x/W,self.scale.y/H) + self.zoom = true + + self:set_sprite_pos(sprite_pos) + + if getmetatable(self) == Sprite then + table.insert(G.I.SPRITE, self) + end +end + +function Sprite:reset() + self.atlas = G.ASSET_ATLAS[self.atlas.name] + self:set_sprite_pos(self.sprite_pos) +end + +function Sprite:set_sprite_pos(sprite_pos) + if sprite_pos and sprite_pos.v then + self.sprite_pos = {x = (math.random(sprite_pos.v)-1), y = sprite_pos.y} + else + self.sprite_pos = sprite_pos or {x=0,y=0} + end + self.sprite_pos_copy = {x = self.sprite_pos.x, y = self.sprite_pos.y} + + self.sprite = love.graphics.newQuad( + self.sprite_pos.x*self.atlas.px, + self.sprite_pos.y*self.atlas.py, + self.scale.x, + self.scale.y, self.atlas.image:getDimensions()) + + self.image_dims = {} + self.image_dims[1], self.image_dims[2] = self.atlas.image:getDimensions() +end + +function Sprite:get_pos_pixel() + self.RETS.get_pos_pixel = self.RETS.get_pos_pixel or {} + self.RETS.get_pos_pixel[1] = self.sprite_pos.x + self.RETS.get_pos_pixel[2] = self.sprite_pos.y + self.RETS.get_pos_pixel[3] = self.atlas.px --self.scale.x + self.RETS.get_pos_pixel[4] = self.atlas.py --self.scale.y + return self.RETS.get_pos_pixel +end + +function Sprite:get_image_dims() + return self.image_dims +end + +function Sprite:define_draw_steps(draw_step_definitions) + self.draw_steps = EMPTY(self.draw_steps) + for k, v in ipairs(draw_step_definitions) do + self.draw_steps[#self.draw_steps+1] = { + shader = v.shader or 'dissolve', + shadow_height = v.shadow_height or nil, + send = v.send or nil, + no_tilt = v.no_tilt or nil, + other_obj = v.other_obj or nil, + ms = v.ms or nil, + mr = v.mr or nil, + mx = v.mx or nil, + my = v.my or nil + } + end +end + +function Sprite:draw_shader(_shader, _shadow_height, _send, _no_tilt, other_obj, ms, mr, mx, my, custom_shader, tilt_shadow) + if G.SETTINGS.reduced_motion then _no_tilt = true end + local _draw_major = self.role.draw_major or self + if _shadow_height then + self.VT.y = self.VT.y - _draw_major.shadow_parrallax.y*_shadow_height + self.VT.x = self.VT.x - _draw_major.shadow_parrallax.x*_shadow_height + self.VT.scale = self.VT.scale*(1-0.2*_shadow_height) + end + + if custom_shader then + if _send then + for k, v in ipairs(_send) do + G.SHADERS[_shader]:send(v.name, v.val or (v.func and v.func()) or v.ref_table[v.ref_value]) + end + end + elseif _shader == 'vortex' then + G.SHADERS['vortex']:send('vortex_amt', G.TIMERS.REAL - (G.vortex_time or 0)) + else + self.ARGS.prep_shader = self.ARGS.prep_shader or {} + self.ARGS.prep_shader.cursor_pos = self.ARGS.prep_shader.cursor_pos or {} + self.ARGS.prep_shader.cursor_pos[1] = _draw_major.tilt_var and _draw_major.tilt_var.mx*G.CANV_SCALE or G.CONTROLLER.cursor_position.x*G.CANV_SCALE + self.ARGS.prep_shader.cursor_pos[2] = _draw_major.tilt_var and _draw_major.tilt_var.my*G.CANV_SCALE or G.CONTROLLER.cursor_position.y*G.CANV_SCALE + + G.SHADERS[_shader or 'dissolve']:send('mouse_screen_pos', self.ARGS.prep_shader.cursor_pos) + G.SHADERS[_shader or 'dissolve']:send('screen_scale', G.TILESCALE*G.TILESIZE*(_draw_major.mouse_damping or 1)*G.CANV_SCALE) + G.SHADERS[_shader or 'dissolve']:send('hovering',((_shadow_height and not tilt_shadow) or _no_tilt) and 0 or (_draw_major.hover_tilt or 0)*(tilt_shadow or 1)) + G.SHADERS[_shader or 'dissolve']:send("dissolve",math.abs(_draw_major.dissolve or 0)) + G.SHADERS[_shader or 'dissolve']:send("time",123.33412*(_draw_major.ID/1.14212 or 12.5123152)%3000) + G.SHADERS[_shader or 'dissolve']:send("texture_details",self:get_pos_pixel()) + G.SHADERS[_shader or 'dissolve']:send("image_details",self:get_image_dims()) + G.SHADERS[_shader or 'dissolve']:send("burn_colour_1",_draw_major.dissolve_colours and _draw_major.dissolve_colours[1] or G.C.CLEAR) + G.SHADERS[_shader or 'dissolve']:send("burn_colour_2",_draw_major.dissolve_colours and _draw_major.dissolve_colours[2] or G.C.CLEAR) + G.SHADERS[_shader or 'dissolve']:send("shadow",(not not _shadow_height)) + if _send then + G.SHADERS[_shader or 'dissolve']:send((SMODS.Shaders[_shader or 'dissolve'] and SMODS.Shaders[_shader or 'dissolve'].original_key) or _shader,_send) + end + end + + local p_shader = SMODS.Shader.obj_table[_shader or 'dissolve'] + if p_shader and type(p_shader.send_vars) == "function" then + local sh = G.SHADERS[_shader or 'dissolve'] + local parent_card = self.role.major and self.role.major:is(Card) and self.role.major + local send_vars = p_shader.send_vars(self, parent_card) + + if type(send_vars) == "table" then + for key, value in pairs(send_vars) do + sh:send(key, value) + end + end + end + love.graphics.setShader( G.SHADERS[_shader or 'dissolve'], G.SHADERS[_shader or 'dissolve']) + + if other_obj then + self:draw_from(other_obj, ms, mr, mx, my) + else + self:draw_self() + end + + love.graphics.setShader() + + if _shadow_height then + self.VT.y = self.VT.y + _draw_major.shadow_parrallax.y*_shadow_height + self.VT.x = self.VT.x + _draw_major.shadow_parrallax.x*_shadow_height + self.VT.scale = self.VT.scale/(1-0.2*_shadow_height) + end +end + +function Sprite:draw_self(overlay) + if not self.states.visible then return end + if self.sprite_pos.x ~= self.sprite_pos_copy.x or self.sprite_pos.y ~= self.sprite_pos_copy.y then + self:set_sprite_pos(self.sprite_pos) + end + prep_draw(self, 1) + love.graphics.scale(1/(self.scale.x/self.VT.w), 1/(self.scale.y/self.VT.h)) + love.graphics.setColor(overlay or G.BRUTE_OVERLAY or G.C.WHITE) + if self.video then + self.video_dims = self.video_dims or { + w = self.video:getWidth(), + h = self.video:getHeight(), + } + love.graphics.draw( + self.video, + 0 ,0, + 0, + self.VT.w/(self.T.w)/(self.video_dims.w/self.scale.x), + self.VT.h/(self.T.h)/(self.video_dims.h/self.scale.y) + ) + else + love.graphics.draw( + self.atlas.image, + self.sprite, + 0 ,0, + 0, + self.VT.w/(self.T.w), + self.VT.h/(self.T.h) + ) + end + love.graphics.pop() + add_to_drawhash(self) + self:draw_boundingrect() + if self.shader_tab then love.graphics.setShader() end +end + +function Sprite:draw(overlay) + if not self.states.visible then return end + if self.draw_steps then + for k, v in ipairs(self.draw_steps) do + self:draw_shader(v.shader, v.shadow_height, v.send, v.no_tilt, v.other_obj, v.ms, v.mr, v.mx, v.my, not not v.send) + end + else + self:draw_self(overlay) + end + + add_to_drawhash(self) + for k, v in pairs(self.children) do + if k ~= 'h_popup' then v:draw() end + end + add_to_drawhash(self) + self:draw_boundingrect() +end + +function Sprite:draw_from(other_obj, ms, mr, mx, my) + self.ARGS.draw_from_offset = self.ARGS.draw_from_offset or {} + self.ARGS.draw_from_offset.x = mx or 0 + self.ARGS.draw_from_offset.y = my or 0 + prep_draw(other_obj, (1 + (ms or 0)), (mr or 0), self.ARGS.draw_from_offset, true) + love.graphics.scale(1/(other_obj.scale_mag or other_obj.VT.scale)) + love.graphics.setColor(G.BRUTE_OVERLAY or G.C.WHITE) + love.graphics.draw( + self.atlas.image, + self.sprite, + -(other_obj.T.w/2 -other_obj.VT.w/2)*10, + 0, + 0, + other_obj.VT.w/(other_obj.T.w), + other_obj.VT.h/(other_obj.T.h) + ) + self:draw_boundingrect() + love.graphics.pop() +end + +function Sprite:remove() + if self.video then + self.video:release() + end + for k, v in pairs(G.ANIMATIONS) do + if v == self then + table.remove(G.ANIMATIONS, k) + end + end + for k, v in pairs(G.I.SPRITE) do + if v == self then + table.remove(G.I.SPRITE, k) + end + end + + Moveable.remove(self) +end diff --git a/lovely/dump/engine/string_packer.lua b/lovely/dump/engine/string_packer.lua new file mode 100644 index 0000000..2197a9b --- /dev/null +++ b/lovely/dump/engine/string_packer.lua @@ -0,0 +1,83 @@ +LOVELY_INTEGRITY = '5d0771786fa792673ea7fd08a5f7ef9bfec9d89cc6a2d2f4ee8e7ff3faee05ec' + +--[[ +MIT License +Copyright (c) 2017 Robert Herlihy +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +]] + +--I modified this A LOT. Needed to make it quicker if it is being saved to file every few seconds during a game +function STR_PACK(data, recursive) + local ret_str = (recursive and "" or "return ").."{" + + for i, v in pairs(data) do + local type_i, type_v = type(i), type(v) + assert((type_i ~= "table"), "Data table cannot have an table as a key reference") + if type_i == "string" then + i = '['..string.format("%q",i)..']' + else + i = "["..i.."]" + end + if type_v == "table" then + if v.m and v.e then + v = "to_big("..v.m..","..v.e..")" + elseif v.array and v.sign then + local v0 = "to_big({" + for qi = 1,#v.array do + v0 = v0 .. v.array[qi] .. ", " + end + v0 = v0 .. "},"..v.sign..")" + v = v0 + elseif v.is and v:is(Object) then + v = [["]].."MANUAL_REPLACE"..[["]] + else + v = STR_PACK(v, true) + end + else + if type_v == "string" then v = string.format("%q", v) end + if type_v == "boolean" then v = v and "true" or "false" end + end + ret_str = ret_str..i.."="..v.."," + end + + return ret_str.."}" +end + +function STR_UNPACK(str) + return assert(loadstring(str))() +end + +function get_compressed(_file) + local file_data = love.filesystem.getInfo(_file) + if file_data ~= nil then + local file_string = love.filesystem.read(_file) + if file_string ~= '' then + if string.sub(file_string, 1, 6) ~= 'return' then + local success = nil + success, file_string = pcall(love.data.decompress, 'string', 'deflate', file_string) + if not success then return nil end + end + return file_string + end + end +end + +function compress_and_save(_file, _data) + local save_string = type(_data) == 'table' and STR_PACK(_data) or _data + save_string = love.data.compress('string', 'deflate', save_string, 1) + love.filesystem.write(_file,save_string) +end diff --git a/lovely/dump/engine/text.lua b/lovely/dump/engine/text.lua new file mode 100644 index 0000000..3c372fb --- /dev/null +++ b/lovely/dump/engine/text.lua @@ -0,0 +1,360 @@ +LOVELY_INTEGRITY = '3f32eb39deebe597f05c1629610525603e46d33168f8992920aef2aab6c8d63a' + +--Class +DynaText = Moveable:extend() + +--Class Methods +function DynaText:init(config) + config = config or {} + self.config = config + self.shadow = config.shadow + self.scale = config.scale or 1 + self.pop_in_rate = config.pop_in_rate or 3 + self.bump_rate = config.bump_rate or 2.666 + self.bump_amount = config.bump_amount or 1 + self.font = config.font or G.LANG.font + if config.string and type(config.string) ~= 'table' then config.string = {config.string} end + self.string = (config.string and type(config.string) == 'table' and config.string[1]) or {'HELLO WORLD'} + self.text_offset = { + x = self.font.TEXT_OFFSET.x*self.scale + (self.config.x_offset or 0), + y = self.font.TEXT_OFFSET.y*self.scale + (self.config.y_offset or 0), + } + self.colours = config.colours or {G.C.RED} + self.created_time = G.TIMERS.REAL + self.silent = (config.silent) + + self.start_pop_in = self.config.pop_in + + config.W = 0 + config.H = 0 + + self.strings = {} + self.focused_string = 1 + + self:update_text(true) + if self.config.maxw and self.config.W > self.config.maxw then + self.start_pop_in = self.config.pop_in + self.scale = self.scale*(self.config.maxw/self.config.W) + self:update_text(true) + end + + if #self.strings > 1 then + self.pop_delay = self.config.pop_delay or 1.5 + self:pop_out(4) + end + + Moveable.init(self,config.X or 0, config.Y or 0, config.W, config.H) + + self.T.r = self.config.text_rot or 0 + + self.states.hover.can = false + self.states.click.can = false + self.states.collide.can = false + self.states.drag.can = false + self.states.release_on.can = false + + self:set_role{ + wh_bond = 'Weak', + scale_bond = 'Weak' + } + + if getmetatable(self) == DynaText then + table.insert(G.I.MOVEABLE, self) + end +end + +function DynaText:update(dt) + self:update_text() + self:align_letters() +end + +function DynaText:update_text(first_pass) + self.config.W = 0 + self.config.H = 0 + self.scale = self.config.scale_function and self.config.scale_function() or self.scale + + for k, v in ipairs(self.config.string) do + if (type(v) == 'table' and v.ref_table) or first_pass then + local part_a, part_b = 0,1000000 + local new_string = v + local outer_colour = nil + local inner_colour = nil + local part_scale = 1 + if type(v) == 'table' and (v.ref_table or v.string) then + new_string = (v.prefix or '')..format_ui_value(v.ref_table and v.ref_table[v.ref_value] or v.string)..(v.suffix or '') + part_a = #(v.prefix or '') + part_b = #new_string - #(v.suffix or '') + if v.scale then part_scale = v.scale end + if first_pass then + outer_colour = v.outer_colour or nil + inner_colour = v.colour or nil + end + v = new_string + end + + self.strings[k] = self.strings[k] or {} + local old_string = self.strings[k].string + if old_string ~= new_string or first_pass then + if self.start_pop_in then self.reset_pop_in = true end + self.reset_pop_in = self.reset_pop_in or self.config.reset_pop_in + if not self.reset_pop_in then + self.config.pop_out = nil + self.config.pop_in = nil + else + self.config.pop_in = self.config.pop_in or 0 + self.created_time = G.TIMERS.REAL + end + self.strings[k].string = v + local old_letters = self.strings[k].letters + local tempW = 0 + local tempH = 0 + local current_letter = 1 + self.strings[k].letters = {}--EMPTY(self.strings[k].letters) + + for _, c in utf8.chars(v) do + local old_letter = old_letters and old_letters[current_letter] or nil + local let_tab = {letter = love.graphics.newText(self.font.FONT, c), char = c, scale = old_letter and old_letter.scale or part_scale} + self.strings[k].letters[current_letter] = let_tab + local tx = self.font.FONT:getWidth(c)*self.scale*part_scale*G.TILESCALE*self.font.FONTSCALE + 2.7*(self.config.spacing or 0)*G.TILESCALE*self.font.FONTSCALE + local ty = self.font.FONT:getHeight(c)*self.scale*part_scale*G.TILESCALE*self.font.FONTSCALE*self.font.TEXT_HEIGHT_SCALE + let_tab.offset = old_letter and old_letter.offset or {x = 0, y = 0} + let_tab.dims = {x = tx/(self.font.FONTSCALE*G.TILESCALE), y = ty/(self.font.FONTSCALE*G.TILESCALE)} + let_tab.pop_in = first_pass and (old_letter and old_letter.pop_in or (self.config.pop_in and 0 or 1)) or 1 + let_tab.prefix = current_letter <= part_a and outer_colour or nil + let_tab.suffix = current_letter > part_b and outer_colour or nil + let_tab.colour = inner_colour or nil + if k > 1 then let_tab.pop_in = 0 end + tempW = tempW + tx/(G.TILESIZE*G.TILESCALE) + tempH = math.max(ty/(G.TILESIZE*G.TILESCALE), tempH) + current_letter = current_letter + 1 + end + + self.strings[k].W = tempW + self.strings[k].H = tempH + end + end + + if Big then + if type(self.strings[k].W) == 'table' then + self.strings[k].W = self.strings[k].W:to_number() + end + if type(self.strings[k].H) == 'table' then + self.strings[k].H = self.strings[k].H:to_number() + end + end + if self.strings[k].W > self.config.W then self.config.W = self.strings[k].W; self.strings[k].W_offset = 0 end + if self.strings[k].H > self.config.H then self.config.H = self.strings[k].H; self.strings[k].H_offset = 0 end + end + + if self.T then + if (self.T.w ~= self.config.W or self.T.h ~= self.config.H) and (not first_pass or self.reset_pop_in) then + self.ui_object_updated = true + self.non_recalc = self.config.non_recalc + end + self.T.w = self.config.W + self.T.h = self.config.H + end + + self.reset_pop_in = false + self.start_pop_in = false + + for k, v in ipairs(self.strings) do + v.W_offset = 0.5*(self.config.W - v.W) + v.H_offset = 0.5*(self.config.H - v.H + (self.config.offset_y or 0)) + end +end + +function DynaText:pop_out(pop_out_timer) + self.config.pop_out = pop_out_timer or 1 + self.pop_out_time = G.TIMERS.REAL + (self.pop_delay or 0) +end + +function DynaText:pop_in(pop_in_timer) + self.reset_pop_in = true + self.config.pop_out = nil + self.config.pop_in = pop_in_timer or 0 + self.created_time = G.TIMERS.REAL + + for k, letter in ipairs(self.strings[self.focused_string].letters) do + if Big then + letter.dims.x = to_big(letter.dims.x):to_number() + letter.dims.y = to_big(letter.dims.y):to_number() + letter.offset.x = to_big(letter.offset.x):to_number() + letter.offset.y = to_big(letter.offset.y):to_number() + end + letter.pop_in = 0 + end + + self:update_text() +end + +function DynaText:align_letters() + if self.pop_cycle then + self.focused_string = (self.config.random_element and math.random(1, #self.strings)) or self.focused_string == #self.strings and 1 or self.focused_string+1 + self.pop_cycle = false + for k, letter in ipairs(self.strings[self.focused_string].letters) do + if Big then + letter.dims.x = to_big(letter.dims.x):to_number() + letter.dims.y = to_big(letter.dims.y):to_number() + letter.offset.x = to_big(letter.offset.x):to_number() + letter.offset.y = to_big(letter.offset.y):to_number() + end + letter.pop_in = 0 + end + self.config.pop_in = 0.1 + self.config.pop_out = nil + self.created_time = G.TIMERS.REAL + end + self.string = self.strings[self.focused_string].string + for k, letter in ipairs(self.strings[self.focused_string].letters) do + if Big then + letter.dims.x = to_big(letter.dims.x):to_number() + letter.dims.y = to_big(letter.dims.y):to_number() + letter.offset.x = to_big(letter.offset.x):to_number() + letter.offset.y = to_big(letter.offset.y):to_number() + end + if self.config.pop_out then + letter.pop_in = math.min(1, math.max((self.config.min_cycle_time or 1)-(G.TIMERS.REAL - self.pop_out_time)*self.config.pop_out/(self.config.min_cycle_time or 1), 0)) + letter.pop_in = letter.pop_in*letter.pop_in + if k == #self.strings[self.focused_string].letters and letter.pop_in <= 0 and #self.strings > 1 then self.pop_cycle = true end + elseif self.config.pop_in then + local prev_pop_in = letter.pop_in + letter.pop_in = math.min(1, math.max((G.TIMERS.REAL - self.config.pop_in - self.created_time)*#self.string*self.pop_in_rate - k + 1, self.config.min_cycle_time == 0 and 1 or 0)) + letter.pop_in = letter.pop_in*letter.pop_in + if prev_pop_in <=0 and letter.pop_in > 0 and not self.silent and + (#self.string < 10 or k%2 == 0) then + if self.T.x > G.ROOM.T.w+2 or + self.T.y > G.ROOM.T.h+2 or + self.T.x <-2 or + self.T.y <-2 then else + play_sound('paper1', 0.45+0.05*math.random()+(0.3/#self.string)*k + (self.config.pitch_shift or 0)) + end + end + if k == #self.strings[self.focused_string].letters and letter.pop_in >= 1 then + if #self.strings > 1 then + self.pop_delay = (G.TIMERS.REAL - self.config.pop_in - self.created_time + (self.config.pop_delay or 1.5)) + self:pop_out(4) + else + self.config.pop_in = nil + end + end + end + letter.r = 0 + letter.scale = 1 + if self.config.rotate then letter.r = (self.config.rotate == 2 and -1 or 1)*(0.2*(-#self.strings[self.focused_string].letters/2 - 0.5 + k)/(#self.strings[self.focused_string].letters)+ (G.SETTINGS.reduced_motion and 0 or 1)*0.02*math.sin(2*G.TIMERS.REAL+k)) end + if self.config.pulse then + letter.scale = letter.scale + (G.SETTINGS.reduced_motion and 0 or 1)*(1/self.config.pulse.width)*self.config.pulse.amount*(math.max( + math.min((self.config.pulse.start - G.TIMERS.REAL)*self.config.pulse.speed + k + self.config.pulse.width, + (G.TIMERS.REAL - self.config.pulse.start)*self.config.pulse.speed - k + self.config.pulse.width+ 2), + 0)) + letter.r = letter.r + (G.SETTINGS.reduced_motion and 0 or 1)*(letter.scale - 1)*(0.02*(-#self.strings[self.focused_string].letters/2 - 0.5 + k)) + if self.config.pulse.start > G.TIMERS.REAL + 2*self.config.pulse.speed*#self.strings[self.focused_string].letters then + self.config.pulse = nil + end + end + if self.config.quiver then + letter.scale = letter.scale + (G.SETTINGS.reduced_motion and 0 or 1)*(0.1*self.config.quiver.amount) + letter.r = letter.r + (G.SETTINGS.reduced_motion and 0 or 1)*0.3*self.config.quiver.amount*( + math.sin(41.12342*G.TIMERS.REAL*self.config.quiver.speed + k*1223.2) + + math.cos(63.21231*G.TIMERS.REAL*self.config.quiver.speed + k*1112.2)*math.sin(36.1231*G.TIMERS.REAL*self.config.quiver.speed) + + math.cos(95.123*G.TIMERS.REAL*self.config.quiver.speed + k*1233.2) - + math.sin(30.133421*G.TIMERS.REAL*self.config.quiver.speed + k*123.2)) + end + if self.config.float then letter.offset.y = (G.SETTINGS.reduced_motion and 0 or 1)*math.sqrt(self.scale)*(2+(self.font.FONTSCALE/G.TILESIZE)*2000*math.sin(2.666*G.TIMERS.REAL+200*k)) + 60*(letter.scale-1) end + if self.config.bump then letter.offset.y = (G.SETTINGS.reduced_motion and 0 or 1)*self.bump_amount*math.sqrt(self.scale)*7*math.max(0, (5+self.bump_rate)*math.sin(self.bump_rate*G.TIMERS.REAL+200*k) - 3 - self.bump_rate) end + end +end + +function DynaText:set_quiver(amt) + self.config.quiver = { + speed = 0.5, + amount = amt or 0.7, + silent = false + } +end + +function DynaText:pulse(amt) + self.config.pulse = { + speed = 40, + width = 2.5, + start = G.TIMERS.REAL, + amount = amt or 0.2, + silent = false + } +end + +function DynaText:draw() +if Big then + self.scale = to_big(self.scale):to_number() + if self.shadow_parallax then self.shadow_parallax.x = to_big(self.shadow_parallax.x):to_number() end +end + if self.children.particle_effect then self.children.particle_effect:draw() end + + if self.shadow then + prep_draw(self, 1) + love.graphics.translate(self.strings[self.focused_string].W_offset + self.text_offset.x*self.font.FONTSCALE/G.TILESIZE, self.strings[self.focused_string].H_offset + self.text_offset.y*self.font.FONTSCALE/G.TILESIZE) + if self.config.spacing then love.graphics.translate(self.config.spacing*self.font.FONTSCALE/G.TILESIZE, 0) end + if self.config.shadow_colour then + love.graphics.setColor(self.config.shadow_colour) + else + love.graphics.setColor(0, 0, 0, 0.3*self.colours[1][4]) + end + for k, letter in ipairs(self.strings[self.focused_string].letters) do + if Big then + letter.dims.x = to_big(letter.dims.x):to_number() + letter.dims.y = to_big(letter.dims.y):to_number() + letter.offset.x = to_big(letter.offset.x):to_number() + letter.offset.y = to_big(letter.offset.y):to_number() + end + local real_pop_in = self.config.min_cycle_time == 0 and 1 or letter.pop_in + love.graphics.draw( + letter.letter, + 0.5*(letter.dims.x - letter.offset.x)*self.font.FONTSCALE/G.TILESIZE -self.shadow_parrallax.x*self.scale/(G.TILESIZE), + 0.5*(letter.dims.y)*self.font.FONTSCALE/G.TILESIZE -self.shadow_parrallax.y*self.scale/(G.TILESIZE), + letter.r or 0, + real_pop_in*self.scale*self.font.FONTSCALE/G.TILESIZE, + real_pop_in*self.scale*self.font.FONTSCALE/G.TILESIZE, + 0.5*letter.dims.x/self.scale, + 0.5*letter.dims.y/self.scale + ) + love.graphics.translate(letter.dims.x*self.font.FONTSCALE/G.TILESIZE, 0) + end + love.graphics.pop() + end + + prep_draw(self, 1) + love.graphics.translate(self.strings[self.focused_string].W_offset + self.text_offset.x*self.font.FONTSCALE/G.TILESIZE, self.strings[self.focused_string].H_offset + self.text_offset.y*self.font.FONTSCALE/G.TILESIZE) + if self.config.spacing then love.graphics.translate(self.config.spacing*self.font.FONTSCALE/G.TILESIZE, 0) end + self.ARGS.draw_shadow_norm = self.ARGS.draw_shadow_norm or {} + local _shadow_norm = self.ARGS.draw_shadow_norm + _shadow_norm.x, _shadow_norm.y = + self.shadow_parrallax.x/math.sqrt(self.shadow_parrallax.y*self.shadow_parrallax.y + self.shadow_parrallax.x*self.shadow_parrallax.x)*self.font.FONTSCALE/G.TILESIZE, + self.shadow_parrallax.y/math.sqrt(self.shadow_parrallax.y*self.shadow_parrallax.y + self.shadow_parrallax.x*self.shadow_parrallax.x)*self.font.FONTSCALE/G.TILESIZE + + for k, letter in ipairs(self.strings[self.focused_string].letters) do + if Big then + letter.dims.x = to_big(letter.dims.x):to_number() + letter.dims.y = to_big(letter.dims.y):to_number() + letter.offset.x = to_big(letter.offset.x):to_number() + letter.offset.y = to_big(letter.offset.y):to_number() + end + local real_pop_in = self.config.min_cycle_time == 0 and 1 or letter.pop_in + love.graphics.setColor(letter.prefix or letter.suffix or letter.colour or self.colours[k%#self.colours + 1]) + + love.graphics.draw( + letter.letter, + 0.5*(letter.dims.x - letter.offset.x)*self.font.FONTSCALE/G.TILESIZE + _shadow_norm.x, + 0.5*(letter.dims.y - letter.offset.y)*self.font.FONTSCALE/G.TILESIZE + _shadow_norm.y, + letter.r or 0, + real_pop_in*letter.scale*self.scale*self.font.FONTSCALE/G.TILESIZE, + real_pop_in*letter.scale*self.scale*self.font.FONTSCALE/G.TILESIZE, + 0.5*letter.dims.x/(self.scale), + 0.5*letter.dims.y/(self.scale) + ) + love.graphics.translate(letter.dims.x*self.font.FONTSCALE/G.TILESIZE, 0) + end + love.graphics.pop() + + add_to_drawhash(self) + self:draw_boundingrect() +end diff --git a/lovely/dump/engine/ui.lua b/lovely/dump/engine/ui.lua new file mode 100644 index 0000000..fa684a8 --- /dev/null +++ b/lovely/dump/engine/ui.lua @@ -0,0 +1,1066 @@ +LOVELY_INTEGRITY = '435a9e8ef1b3d548ef4488606196332b7d61c0a33e2dd2225ca6c7f97e3ec32b' + +--Class +UIBox = Moveable:extend() + +--The base level and container of a graph of 1 or more UIElements. These UIEs are\ +--essentially a node based UI implementation. As the root node of the graph, this\ +--node is the first called for any movement, updates, or changes to ensure that all child\ +--nodes are updated and modified in the correct order.\\ +--The UI_definitions file houses the majority of the definition tables needed for UIBox initialization. +-- +---@param args {T: table, definition: table, config: table} +--**T** A standard transform in game units describing the inital position and size of the object with x, y, w, h\ +--ex - {x = 1, y = 5, w = 2, h = 2, r = 0} +-- +--**definition** A table containing a valid UIBox definition. These are mostly generated from UI_definitions +-- +--**config** A configuration table for the UIBox +--ex - { align = 'cm', offset = {x = 1, y = 1}, parent_rect = A, attach_rect = B, can_collide = true } +function UIBox:init(args) + --First initialize the moveable + Moveable.init(self,{args.T}) + + --Initialization of fields + self.states.drag.can = false + self.draw_layers = {} --if we need to explicitly change the draw order of the UIEs + + --The definition table that contains the schematic of this UIBox + self.definition = args.definition + + if args.config then + self.config = args.config + args.config.major = args.config.major or args.config.parent or self + + self:set_alignment({ + major = args.config.major, + type = args.config.align or args.config.type or '', + bond = args.config.bond or 'Strong', + offset = args.config.offset or {x=0,y=0}, + lr_clamp = args.config.lr_clamp + }) + self:set_role{ + xy_bond = args.config.xy_bond, + r_bond = args.config.r_bond, + wh_bond = args.config.wh_bond or 'Weak', + scale_bond = args.config.scale_bond or 'Weak' + } + self.states.collide.can = true + + if args.config.can_collide == nil then + self.states.collide.can = true + else + self.states.collide.can = args.config.can_collide + end + + self.parent = self.config.parent + end + + --inherit the layered_parallax from the parent if there is any + --self.layered_parallax = self.role.major and self.role.major.layered_parallax or self.layered_parallax + + --Initialization of the UIBox from the definition + --First, set parent-child relationships to create the tree structure of the box + + self:set_parent_child(self.definition, nil) + --Set the midpoint for any future alignments to use + self.Mid = self.Mid or self.UIRoot + --Calculate the correct and width/height and offset for each node + self:calculate_xywh(self.UIRoot, self.T) + + --set the transform w/h to equal that of the calculated box + self.T.w = self.UIRoot.T.w + self.T.h = self.UIRoot.T.h + --Then, calculate the correct width and height for each container + self.UIRoot:set_wh() + --Then, set all of the correct alignments for the ui elements\ + + self.UIRoot:set_alignments() + + self:align_to_major() + self.VT.x, self.VT.y = self.T.x, self.T.y + self.VT.w, self.VT.h = self.T.w, self.T.h + + if self.Mid ~= self and self.Mid.parent and false then + self.VT.x = self.VT.x - self.Mid.role.offset.x + (self.Mid.parent.config.padding or 0) + self.VT.y = self.VT.y - self.Mid.role.offset.y + (self.Mid.parent.config.padding or 0) + end + + if self.alignment and self.alignment.lr_clamp then + self:lr_clamp() + end + + self.UIRoot:initialize_VT(true) + if getmetatable(self) == UIBox then + if args.config.instance_type then + table.insert(G.I[args.config.instance_type], self) + else + table.insert(G.I.UIBOX, self) + end + end +end + +function UIBox:get_UIE_by_ID(id, node) + if not node then node = self.UIRoot end + if node.config and node.config.id == id then return node end + for k, v in pairs(node.children) do + local res = self:get_UIE_by_ID(id, v) + if res then + return res + elseif v.config.object and v.config.object.get_UIE_by_ID then + res = v.config.object:get_UIE_by_ID(id, nil) + if res then + return res + end + end + end + return nil +end + +function UIBox:calculate_xywh(node, _T, recalculate, _scale) + node.ARGS.xywh_node_trans = node.ARGS.xywh_node_trans or {} + local _nt = node.ARGS.xywh_node_trans + local _ct = {} + + _ct.x, _ct.y, _ct.w, _ct.h = 0,0,0,0 + + local padding = node.config.padding or G.UIT.padding + --current node does not contain anything + if node.UIT == G.UIT.B or node.UIT == G.UIT.T or node.UIT == G.UIT.O then + _nt.x, _nt.y, _nt.w, _nt.h = + _T.x, + _T.y, + node.config.w or (node.config.object and node.config.object.T.w), + node.config.h or (node.config.object and node.config.object.T.h) + + if node.UIT == G.UIT.T then + node.config.text_drawable = nil + local scale = node.config.scale or 1 + if node.config.ref_table and node.config.ref_value then + node.config.text = tostring(node.config.ref_table[node.config.ref_value]) + if node.config.func and not recalculate then G.FUNCS[node.config.func](node) end + end + if not node.config.text then node.config.text = '[UI ERROR]' end + node.config.lang = node.config.lang or G.LANG + local tx = node.config.lang.font.FONT:getWidth(node.config.text)*node.config.lang.font.squish*scale*G.TILESCALE*node.config.lang.font.FONTSCALE + local ty = node.config.lang.font.FONT:getHeight()*scale*G.TILESCALE*node.config.lang.font.FONTSCALE*node.config.lang.font.TEXT_HEIGHT_SCALE + if node.config.vert then local thunk = tx; tx = ty; ty = thunk end + _nt.x, _nt.y, _nt.w, _nt.h = + _T.x, + _T.y, + tx/(G.TILESIZE*G.TILESCALE), + ty/(G.TILESIZE*G.TILESCALE) + + node.content_dimensions = node.content_dimensions or {} + node.content_dimensions.w = _T.w + node.content_dimensions.h = _T.h + node:set_values(_nt, recalculate) + elseif node.UIT == G.UIT.B or node.UIT == G.UIT.O then + node.content_dimensions = node.content_dimensions or {} + node.content_dimensions.w = _nt.w + node.content_dimensions.h = _nt.h + node:set_values(_nt, recalculate) + end + return _nt.w, _nt.h + else --For all other node containers, treat them explicitly like a column + for i = 1, 2 do + if i == 1 or (i == 2 and ((node.config.maxw and _ct.w > node.config.maxw) or (node.config.maxh and _ct.h > node.config.maxh))) then + local fac = _scale or 1 + if i == 2 then + local restriction = node.config.maxw or node.config.maxh + fac = fac*restriction/(node.config.maxw and _ct.w or _ct.h) + end + _nt.x, _nt.y, _nt.w, _nt.h = + _T.x, + _T.y, + node.config.minw or 0, + node.config.minh or 0 + + if node.UIT == G.UIT.ROOT then + _nt.x, _nt.y, _nt.w, _nt.h = 0, 0, node.config.minw or 0, node.config.minh or 0 + end + _ct.x, _ct.y, _ct.w, _ct.h = _nt.x+padding, _nt.y+padding, 0, 0 + local _tw, _th + for k, v in ipairs(node.children) do + if getmetatable(v) == UIElement then + if v.config and v.config.scale then v.config.scale = v.config.scale*fac end + _tw, _th = self:calculate_xywh(v, _ct, recalculate, fac) + if _th and _tw then + if Big then + _th = to_big(_th):to_number() + _tw = to_big(_tw):to_number() + end + if v.UIT == G.UIT.R then + _ct.h = _ct.h + _th + padding + _ct.y = _ct.y + _th + padding + if _tw + padding > _ct.w then _ct.w = _tw + padding end + if v.config and v.config.emboss then + _ct.h = _ct.h + v.config.emboss + _ct.y = _ct.y + v.config.emboss + end + else + _ct.w = _ct.w + _tw + padding + _ct.x = _ct.x + _tw + padding + if _th + padding > _ct.h then _ct.h = _th + padding end + if v.config and v.config.emboss then + _ct.h = _ct.h + v.config.emboss + end + end + end + end + end + end + end + + node.content_dimensions = node.content_dimensions or {} + node.content_dimensions.w = _ct.w + padding + node.content_dimensions.h = _ct.h + padding + _nt.w = math.max(_ct.w + padding, _nt.w) + _nt.h = math.max(_ct.h + padding, _nt.h)-- + node:set_values(_nt, recalculate) + return _nt.w, _nt.h + end +end + +function UIBox:remove_group(node, group) + node = node or self.UIRoot + for k, v in pairs(node.children) do + if self:remove_group(v, group) then node.children[k] = nil end + end + if node.config and node.config.group and node.config.group == group then node:remove(); return true end + + if not node.parent or true then self:calculate_xywh(self.UIRoot, self.T, true); self.UIRoot:set_wh(); self.UIRoot:set_alignment() end--self:recalculate() end +end + +function UIBox:get_group(node, group, ingroup) + node = node or self.UIRoot + ingroup = ingroup or {} + for k, v in pairs(node.children) do + self:get_group(v, group, ingroup) + end + if node.config and node.config.group and node.config.group == group then table.insert(ingroup, node); return ingroup end + return ingroup +end + +function UIBox:set_parent_child(node, parent) + local UIE = UIElement(parent, self, node.n, node.config) + + --set the group of the element + if parent and parent.config and parent.config.group then if UIE.config then UIE.config.group = parent.config.group else UIE.config = {group = parent.config.group} end end + + --set the button for the element + if parent and parent.config and parent.config.button then if UIE.config then UIE.config.button_UIE = parent else UIE.config = {button_UIE = parent} end end + if parent and parent.config and parent.config.button_UIE then if UIE.config then UIE.config.button_UIE = parent.config.button_UIE else UIE.config = {button = parent.config.button} end end + + if node.n and node.n == G.UIT.O and UIE.config.button then + UIE.config.object.states.click.can = false + end + + --current node is a container + if (node.n and node.n == G.UIT.C or node.n == G.UIT.R or node.n == G.UIT.ROOT) and node.nodes then + for k, v in pairs(node.nodes) do + self:set_parent_child(v, UIE) + end + end + + if not parent then + self.UIRoot = UIE + self.UIRoot.parent = self + else + table.insert(parent.children, UIE) + end + if node.config and node.config.mid then + self.Mid = UIE + end +end +function UIBox:remove() + if self == G.OVERLAY_MENU then G.REFRESH_ALERTS = true end + self.UIRoot:remove() + for k, v in pairs(G.I[self.config.instance_type or 'UIBOX']) do + if v == self then + table.remove(G.I[self.config.instance_type or 'UIBOX'], k) + break; + end + end + remove_all(self.children) + Moveable.remove(self) +end + +function UIBox:draw() + if self.FRAME.DRAW >= G.FRAMES.DRAW and not G.OVERLAY_TUTORIAL then return end + self.FRAME.DRAW = G.FRAMES.DRAW + + for k, v in pairs(self.children) do + if k ~= 'h_popup' and k ~= 'alert' then v:draw() end + end + + if self.states.visible then + add_to_drawhash(self) + self.UIRoot:draw_self() + self.UIRoot:draw_children() + for k, v in ipairs(self.draw_layers) do + if v.draw_self then v:draw_self() else v:draw() end + if v.draw_children then v:draw_children() end + end + end + + if self.children.alert then self.children.alert:draw() end + + self:draw_boundingrect() +end + +function UIBox:recalculate() + --Calculate the correct dimensions and width/height and offset for each node + self:calculate_xywh(self.UIRoot, self.T, true) + --Then, calculate the correct width and height for each container + self.UIRoot:set_wh() + --Then, set all of the correct alignments for the ui elements + self.UIRoot:set_alignments() + self.T.w = self.UIRoot.T.w + self.T.h = self.UIRoot.T.h + G.REFRESH_FRAME_MAJOR_CACHE = (G.REFRESH_FRAME_MAJOR_CACHE or 0) + 1 + self.UIRoot:initialize_VT() + G.REFRESH_FRAME_MAJOR_CACHE = (G.REFRESH_FRAME_MAJOR_CACHE > 1 and G.REFRESH_FRAME_MAJOR_CACHE - 1 or nil) +end + +function UIBox:move(dt) + Moveable.move(self, dt) + Moveable.move(self.UIRoot, dt) +end + +function UIBox:drag(offset) + Moveable.drag(self,offset) + Moveable.move(self.UIRoot, dt) +end + +function UIBox:add_child(node, parent) + self:set_parent_child(node, parent) + self:recalculate() +end + +function UIBox:set_container(container) + self.UIRoot:set_container(container) + Node.set_container(self, container) +end + +function UIBox:print_topology(indent) + local box_str = '| UIBox | - ID:'..self.ID..' w/h:'..self.T.w..'/'..self.T.h + local indent = indent or 0 + box_str = box_str..self.UIRoot:print_topology(indent) + return box_str +end + +--Class +UIElement = Moveable:extend() +--Class Methods +function UIElement:init(parent, new_UIBox, new_UIT, config) + self.parent = parent + self.UIT = new_UIT + self.UIBox = new_UIBox + self.config = config or {} + if self.config and self.config.object then self.config.object.parent = self end + self.children = {} + self.ARGS = self.ARGS or {} + self.content_dimensions = {w=0, h=0} +end +function UIElement:set_values(_T, recalculate) + if not recalculate or not self.T then + Moveable.init(self,{T = _T}) + self.states.click.can = false + self.states.drag.can = false + self.static_rotation = true + else + self.T.x = _T.x + self.T.y = _T.y + self.T.w = _T.w + self.T.h = _T.h + end + + if self.config.button_UIE then self.states.collide.can = true; self.states.hover.can = false; self.states.click.can = true end + if self.config.button then self.states.collide.can = true; self.states.click.can = true end + + if self.config.on_demand_tooltip or self.config.tooltip or self.config.detailed_tooltip then + self.states.collide.can = true + end + + self:set_role{role_type = 'Minor', major = self.UIBox, offset = {x = _T.x, y = _T.y}, wh_bond = 'Weak', scale_bond = 'Weak'} + + if self.config.draw_layer then + self.UIBox.draw_layers[self.config.draw_layer] = self + end + + if self.config.collideable then self.states.collide.can = true end + + if self.config.can_collide ~= nil then + self.states.collide.can = self.config.can_collide + if self.config.object then self.config.object.states.collide.can = self.states.collide.can end + end + + if self.UIT == G.UIT.O and not self.config.no_role then + self.config.object:set_role(self.config.role or {role_type = 'Minor', major = self, xy_bond = 'Strong', wh_bond = 'Weak', scale_bond = 'Weak'}) + end + + if self.config and self.config.ref_value and self.config.ref_table then + self.config.prev_value = self.config.ref_table[self.config.ref_value] + end + + if self.UIT == G.UIT.T then self.static_rotation = true end + + if self.config.juice then + if self.UIT == G.UIT.ROOT then self:juice_up() end + if self.UIT == G.UIT.T then self:juice_up() end + if self.UIT == G.UIT.O then self.config.object:juice_up(0.5) end + if self.UIT == G.UIT.B then self:juice_up() end + if self.UIT == G.UIT.C then self:juice_up() end + if self.UIT == G.UIT.R then self:juice_up() end + self.config.juice = false + end + + if not self.config.colour then + if self.UIT == G.UIT.ROOT then self.config.colour = G.C.UI.BACKGROUND_DARK end + if self.UIT == G.UIT.T then self.config.colour = G.C.UI.TEXT_LIGHT end + if self.UIT == G.UIT.O then self.config.colour = G.C.WHITE end + if self.UIT == G.UIT.B then self.config.colour = G.C.CLEAR end + if self.UIT == G.UIT.C then self.config.colour = G.C.CLEAR end + if self.UIT == G.UIT.R then self.config.colour = G.C.CLEAR end + end + if not self.config.outline_colour then + if self.UIT == G.UIT.ROOT then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end + if self.UIT == G.UIT.T then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end + if self.UIT == G.UIT.O then self.config.colour = G.C.UI.OUTLINE_LIGHT end + if self.UIT == G.UIT.B then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end + if self.UIT == G.UIT.C then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end + if self.UIT == G.UIT.R then self.config.outline_colour = G.C.UI.OUTLINE_LIGHT end + end + + if self.config.focus_args and not self.config.focus_args.registered then + if self.config.focus_args.button then + G.CONTROLLER:add_to_registry(self.config.button_UIE or self, self.config.focus_args.button) + end + + if self.config.focus_args.snap_to then + G.CONTROLLER:snap_to{node = self} + end + + if self.config.focus_args.funnel_to then + local _par = self.parent + while _par and _par:is(UIElement) do + if _par.config.focus_args and _par.config.focus_args.funnel_from then + _par.config.focus_args.funnel_from = self + self.config.focus_args.funnel_to = _par + break + end + _par = _par.parent + end + end + self.config.focus_args.registered = true + end + + if self.config.force_focus then self.states.collide.can = true end + + if self.config.button_delay and not self.config.button_delay_start then + self.config.button_delay_start = G.TIMERS.REAL + self.config.button_delay_end = G.TIMERS.REAL + self.config.button_delay + self.config.button_delay_progress = 0 + end + + self.layered_parallax = self.layered_parallax or {x=0, y=0} + + if self.config and self.config.func and (((self.config.button_UIE or self.config.button) and self.config.func ~= 'set_button_pip') or self.config.insta_func) then G.FUNCS[self.config.func](self) end +end + +function UIElement:print_topology(indent) + local UIT = '????' + for k, v in pairs(G.UIT) do + if v == self.UIT then UIT = ''..k end + end + local box_str = '\n'..(string.rep(" ", indent))..'| '..UIT..' | - ID:'..self.ID..' w/h:'..self.T.w..'/'..self.T.h + if UIT == 'O' then + box_str = box_str..' OBJ:'..( + getmetatable(self.config.object) == CardArea and 'CardArea' or + getmetatable(self.config.object) == Card and 'Card' or + getmetatable(self.config.object) == UIBox and 'UIBox' or + getmetatable(self.config.object) == Particles and 'Particles' or + getmetatable(self.config.object) == DynaText and 'DynaText' or + getmetatable(self.config.object) == Sprite and 'Sprite' or + getmetatable(self.config.object) == AnimatedSprite and 'AnimatedSprite' or + 'OTHER' + ) + elseif UIT == 'T' then + box_str = box_str..' TEXT:'..(self.config.text or 'REF') + end + + for k, v in ipairs(self.children) do + if v.print_topology then + box_str = box_str..v:print_topology(indent+1) + end + end + return box_str +end + +function UIElement:initialize_VT() + self:move_with_major(0) + self:calculate_parrallax() + + for _, v in pairs(self.children) do + if v.initialize_VT then v:initialize_VT() end + end + + self.VT.w, self.VT.h = self.T.w, self.T.h + + if self.UIT == G.UIT.T then self:update_text() end + if self.config.object then + if not self.config.no_role then + self.config.object:hard_set_T(self.T.x, self.T.y, self.T.w, self.T.h) + self.config.object:move_with_major(0) + self.config.object.alignment.prev_type = '' + self.config.object:align_to_major() + end + if self.config.object.recalculate then + self.config.object:recalculate() + end + end +end + +function UIElement:juice_up(amount, rot_amt) + if self.UIT == G.UIT.O then + if self.config.object then self.config.object:juice_up(amount, rot_amt) end + else + Moveable.juice_up(self, amount, rot_amt) + end +end + +function UIElement:can_drag() + if self.states.drag.can then return self end + return self.UIBox:can_drag() +end + +function UIElement:draw() +end + +function UIElement:draw_children(layer) + if self.states.visible then + for k, v in pairs(self.children) do + if not v.config.draw_layer and k ~= 'h_popup' and k~= 'alert' then + if v.draw_self and not v.config.draw_after then v:draw_self() else v:draw() end + if v.draw_children then v:draw_children() end + if v.draw_self and v.config.draw_after then v:draw_self() else v:draw() end + end + end + end +end + +function UIElement:set_wh() + --Iterate through all children of this node + local padding = (self.config and self.config.padding) or G.UIT.padding + + local _max_w, _max_h = 0,0 + + if next(self.children) == nil or self.config.no_fill then + return self.T.w, self.T.h + else + for k, w in pairs(self.children) do + if w.set_wh then + local _cw, _ch = w:set_wh() + if Big and G.STATE == G.STATES.MENU then _cw = to_big(_cw):to_number(); _ch = to_big(_ch):to_number() end + if _cw and _ch then + if _cw > _max_w then _max_w = _cw end + if _ch > _max_h then _max_h = _ch end + else + _max_w = padding + _max_h = padding + end + end + end + for k, w in pairs(self.children) do + if w.UIT == G.UIT.R then w.T.w = _max_w end + if w.UIT == G.UIT.C then w.T.h = _max_h end + end + end + return self.T.w, self.T.h +end + +function UIElement:align(x, y) + self.role.offset.y = self.role.offset.y + y + self.role.offset.x = self.role.offset.x + x + for _, v in pairs(self.children) do + if v.align then + v:align(x, y) + end + end +end + +function UIElement:set_alignments() + --vertically centered is c = centered + --horizontally centered is m = middle + --top and left are default + --bottom is b + --right is r + for k, v in pairs(self.children) do + if self.config and self.config.align and v.align then + + local padding = self.config.padding or G.UIT.padding + + if string.find(self.config.align, "c") then + if v.UIT == G.UIT.T or v.UIT == G.UIT.B or v.UIT == G.UIT.O then + v:align(0,0.5*(self.T.h - 2*padding - v.T.h)) + else + v:align(0,0.5*(self.T.h - self.content_dimensions.h)) + end + end + if string.find(self.config.align, "m") then + v:align(0.5*(self.T.w - self.content_dimensions.w),0) + end + if string.find(self.config.align, "b") then + v:align(0, self.T.h - self.content_dimensions.h) + end + if string.find(self.config.align, "r") then + v:align((self.T.w - self.content_dimensions.w), 0) + end + end + if v.set_alignments then v:set_alignments() end + end +end +function UIElement:update_text() + if self.config and self.config.text and not self.config.text_drawable then + self.config.lang = self.config.lang or G.LANG + self.config.text_drawable = love.graphics.newText(self.config.lang.font.FONT, {G.C.WHITE,self.config.text}) + end + + if self.config.ref_table and self.config.ref_table[self.config.ref_value] ~= self.config.prev_value then + self.config.text = tostring(self.config.ref_table[self.config.ref_value]) + self.config.text_drawable:set(self.config.text) + if not self.config.no_recalc and self.config.prev_value and string.len(self.config.prev_value) ~= string.len(self.config.text) then self.UIBox:recalculate() end + self.config.prev_value = self.config.ref_table[self.config.ref_value] + end +end + +function UIElement:update_object() + if self.config.ref_table and self.config.ref_value and self.config.ref_table[self.config.ref_value] ~= self.config.object then + self.config.object = self.config.ref_table[self.config.ref_value] + self.UIBox:recalculate() + end + + if self.config.object then + self.config.object.config.refresh_movement = true + if self.config.object.states.hover.is and not self.states.hover.is then + self:hover() + self.states.hover.is = true + end + if not self.config.object.states.hover.is and self.states.hover.is then + self:stop_hover() + self.states.hover.is = false + end + end + + if self.config.object and self.config.object.ui_object_updated then + self.config.object.ui_object_updated = nil + self.config.object.parent = self + self.config.object:set_role(self.config.role or {role_type = 'Minor', major = self}) + self.config.object:move_with_major(0) + if self.config.object.non_recalc then + self.parent.content_dimensions.w = self.config.object.T.w + self:align(self.parent.T.x - self.config.object.T.x, self.parent.T.y - self.config.object.T.y) + self.parent:set_alignments() + else + self.UIBox:recalculate() + end + end +end +function UIElement:draw_self() + if not self.states.visible then + if self.config.force_focus then add_to_drawhash(self) end + return + end + + if self.config.force_focus or self.config.force_collision or self.config.button_UIE or self.config.button or self.states.collide.can then + add_to_drawhash(self) + end + + local button_active = true + local parallax_dist = 1.5 + local button_being_pressed = false + + if (self.config.button or self.config.button_UIE) then + self.layered_parallax.x = ((self.parent and self.parent ~= self.UIBox and self.parent.layered_parallax.x or 0) + (self.config.shadow and 0.4*self.shadow_parrallax.x or 0)/G.TILESIZE) + self.layered_parallax.y = ((self.parent and self.parent ~= self.UIBox and self.parent.layered_parallax.y or 0) + (self.config.shadow and 0.4*self.shadow_parrallax.y or 0)/G.TILESIZE) + + if self.config.button and ((self.last_clicked and self.last_clicked > G.TIMERS.REAL - 0.1) or ((self.config.button and (self.states.hover.is or self.states.drag.is)) + and G.CONTROLLER.is_cursor_down)) then + self.layered_parallax.x = self.layered_parallax.x - parallax_dist*self.shadow_parrallax.x/G.TILESIZE*(self.config.button_dist or 1) + self.layered_parallax.y = self.layered_parallax.y - parallax_dist*self.shadow_parrallax.y/G.TILESIZE*(self.config.button_dist or 1) + parallax_dist = 0 + button_being_pressed = true + end + + if self.config.button_UIE and not self.config.button_UIE.config.button then button_active = false end + end + if self.config.colour[4] > 0.01 then + if self.UIT == G.UIT.T and self.config.scale then + self.ARGS.text_parallax = self.ARGS.text_parallax or {} + self.ARGS.text_parallax.sx = -self.shadow_parrallax.x*0.5/(self.config.scale*self.config.lang.font.FONTSCALE) + self.ARGS.text_parallax.sy = -self.shadow_parrallax.y*0.5/(self.config.scale*self.config.lang.font.FONTSCALE) + + if (self.config.button_UIE and button_active) or (not self.config.button_UIE and self.config.shadow and G.SETTINGS.GRAPHICS.shadows == 'On') then + prep_draw(self, 0.97) + if Big and G.STATE == G.STATES.MENU then self.config.scale = to_big(self.config.scale):to_number() end + if self.config.vert then love.graphics.translate(0,self.VT.h); love.graphics.rotate(-math.pi/2) end + if (self.config.shadow or (self.config.button_UIE and button_active)) and G.SETTINGS.GRAPHICS.shadows == 'On' then + love.graphics.setColor(0, 0, 0, 0.3*self.config.colour[4]) + love.graphics.draw( + self.config.text_drawable, + (self.config.lang.font.TEXT_OFFSET.x + (self.config.vert and -self.ARGS.text_parallax.sy or self.ARGS.text_parallax.sx))*(self.config.scale or 1)*self.config.lang.font.FONTSCALE/G.TILESIZE, + (self.config.lang.font.TEXT_OFFSET.y + (self.config.vert and self.ARGS.text_parallax.sx or self.ARGS.text_parallax.sy))*(self.config.scale or 1)*self.config.lang.font.FONTSCALE/G.TILESIZE, + 0, + (self.config.scale)*self.config.lang.font.squish*self.config.lang.font.FONTSCALE/G.TILESIZE, + (self.config.scale)*self.config.lang.font.FONTSCALE/G.TILESIZE + ) + end + love.graphics.pop() + end + + prep_draw(self, 1) + if Big and G.STATE == G.STATES.MENU then self.config.scale = to_big(self.config.scale):to_number() end + if self.config.vert then love.graphics.translate(0,self.VT.h); love.graphics.rotate(-math.pi/2) end + if not button_active then + love.graphics.setColor(G.C.UI.TEXT_INACTIVE) + else + love.graphics.setColor(self.config.colour) + end + love.graphics.draw( + self.config.text_drawable, + self.config.lang.font.TEXT_OFFSET.x*(self.config.scale)*self.config.lang.font.FONTSCALE/G.TILESIZE, + self.config.lang.font.TEXT_OFFSET.y*(self.config.scale)*self.config.lang.font.FONTSCALE/G.TILESIZE, + 0, + (self.config.scale)*self.config.lang.font.squish*self.config.lang.font.FONTSCALE/G.TILESIZE, + (self.config.scale)*self.config.lang.font.FONTSCALE/G.TILESIZE + ) + love.graphics.pop() + elseif self.UIT == G.UIT.B or self.UIT == G.UIT.C or self.UIT == G.UIT.R or self.UIT == G.UIT.ROOT then + prep_draw(self, 1) + love.graphics.scale(1/(G.TILESIZE)) + if self.config.shadow and G.SETTINGS.GRAPHICS.shadows == 'On' then + love.graphics.scale(0.98) + if self.config.shadow_colour then + love.graphics.setColor(self.config.shadow_colour) + else + love.graphics.setColor(0,0,0,0.3*self.config.colour[4]) + end + if self.config.r and self.VT.w > 0.01 then + self:draw_pixellated_rect('shadow', parallax_dist) + else + love.graphics.rectangle('fill', -self.shadow_parrallax.x*parallax_dist, -self.shadow_parrallax.y*parallax_dist, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE) + end + love.graphics.scale(1/0.98) + end + + love.graphics.scale(button_being_pressed and 0.985 or 1) + if self.config.emboss then + love.graphics.setColor(darken(self.config.colour, self.states.hover.is and 0.5 or 0.3, true)) + self:draw_pixellated_rect('emboss', parallax_dist, self.config.emboss) + end + local collided_button = self.config.button_UIE or self + self.ARGS.button_colours = self.ARGS.button_colours or {} + self.ARGS.button_colours[1] = self.config.button_delay and mix_colours(self.config.colour, G.C.L_BLACK, 0.5) or self.config.colour + self.ARGS.button_colours[2] = (((collided_button.config.hover and collided_button.states.hover.is) or (collided_button.last_clicked and collided_button.last_clicked > G.TIMERS.REAL - 0.1)) and G.C.UI.HOVER or nil) + for k, v in ipairs(self.ARGS.button_colours) do + love.graphics.setColor(v) + if self.config.r and self.VT.w > 0.01 then + if self.config.button_delay then + love.graphics.setColor(G.C.GREY) + self:draw_pixellated_rect('fill', parallax_dist) + love.graphics.setColor(v) + self:draw_pixellated_rect('fill', parallax_dist, nil, self.config.button_delay_progress) + elseif self.config.progress_bar then + love.graphics.setColor(self.config.progress_bar.empty_col or G.C.GREY) + self:draw_pixellated_rect('fill', parallax_dist) + love.graphics.setColor(self.config.progress_bar.filled_col or G.C.BLUE) + self:draw_pixellated_rect('fill', parallax_dist, nil, self.config.progress_bar.ref_table[self.config.progress_bar.ref_value]/self.config.progress_bar.max) + else + self:draw_pixellated_rect('fill', parallax_dist) + end + else + love.graphics.rectangle('fill', 0,0, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE) + end + end + love.graphics.pop() + elseif self.UIT == G.UIT.O and self.config.object then + --Draw the outline for highlighted objext + if self.config.focus_with_object and self.config.object.states.focus.is then + self.object_focus_timer = self.object_focus_timer or G.TIMERS.REAL + local lw = 50*math.max(0, self.object_focus_timer - G.TIMERS.REAL + 0.3)^2 + prep_draw(self, 1) + love.graphics.scale((1)/(G.TILESIZE)) + love.graphics.setLineWidth(lw + 1.5) + love.graphics.setColor(adjust_alpha(G.C.WHITE, 0.2*lw, true)) + self:draw_pixellated_rect('fill', parallax_dist) + love.graphics.setColor(self.config.colour[4] > 0 and mix_colours(G.C.WHITE, self.config.colour, 0.8) or G.C.WHITE) + self:draw_pixellated_rect('line', parallax_dist) + love.graphics.pop() + else + self.object_focus_timer = nil + end + self.config.object:draw() + end + end + + --Draw the outline of the object + if self.config.outline and self.config.outline_colour[4] > 0.01 then + if self.config.outline then + prep_draw(self, 1) + love.graphics.scale(1/(G.TILESIZE)) + love.graphics.setLineWidth(self.config.outline) + if self.config.line_emboss then + love.graphics.setColor(darken(self.config.outline_colour, self.states.hover.is and 0.5 or 0.3, true)) + self:draw_pixellated_rect('line_emboss', parallax_dist, self.config.line_emboss) + end + love.graphics.setColor(self.config.outline_colour) + if self.config.r and self.VT.w > 0.01 then + self:draw_pixellated_rect('line', parallax_dist) + else + love.graphics.rectangle('line', 0,0, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE) + end + love.graphics.pop() + end + end + + --Draw the outline for highlighted buttons + if self.states.focus.is then + self.focus_timer = self.focus_timer or G.TIMERS.REAL + local lw = 50*math.max(0, self.focus_timer - G.TIMERS.REAL + 0.3)^2 + prep_draw(self, 1) + love.graphics.scale((1)/(G.TILESIZE)) + love.graphics.setLineWidth(lw + 1.5) + love.graphics.setColor(adjust_alpha(G.C.WHITE, 0.2*lw, true)) + self:draw_pixellated_rect('fill', parallax_dist) + love.graphics.setColor(self.config.colour[4] > 0 and mix_colours(G.C.WHITE, self.config.colour, 0.8) or G.C.WHITE) + self:draw_pixellated_rect('line', parallax_dist) + love.graphics.pop() + else + self.focus_timer = nil + end + + --Draw the 'chosen triangle' + if self.config.chosen then + prep_draw(self, 0.98) + love.graphics.scale(1/(G.TILESIZE)) + if self.config.shadow and G.SETTINGS.GRAPHICS.shadows == 'On' then + love.graphics.setColor(0,0,0,0.3*self.config.colour[4]) + love.graphics.polygon("fill", get_chosen_triangle_from_rect(self.layered_parallax.x - self.shadow_parrallax.x*parallax_dist*0.5, self.layered_parallax.y - self.shadow_parrallax.y*parallax_dist*0.5, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE, self.config.chosen == 'vert')) + end + love.graphics.pop() + + prep_draw(self, 1) + love.graphics.scale(1/(G.TILESIZE)) + love.graphics.setColor(G.C.RED) + love.graphics.setColor(self.config.colour) + love.graphics.polygon("fill", get_chosen_triangle_from_rect(self.layered_parallax.x, self.layered_parallax.y, self.VT.w*G.TILESIZE, self.VT.h*G.TILESIZE, self.config.chosen == 'vert')) + love.graphics.pop() + end + self:draw_boundingrect() +end + +function UIElement:draw_pixellated_rect(_type, _parallax, _emboss, _progress) + if not self.pixellated_rect or + #self.pixellated_rect[_type].vertices < 1 or + _parallax ~= self.pixellated_rect.parallax or + self.pixellated_rect.w ~= self.VT.w or + self.pixellated_rect.h ~= self.VT.h or + self.pixellated_rect.sw ~= self.shadow_parrallax.x or + self.pixellated_rect.sh ~= self.shadow_parrallax.y or + self.pixellated_rect.progress ~= (_progress or 1) + then + self.pixellated_rect = { + w = self.VT.w, + h = self.VT.h, + sw = self.shadow_parrallax.x, + sh = self.shadow_parrallax.y, + progress = (_progress or 1), + fill = {vertices = {}}, + shadow = {vertices = {}}, + line = {vertices = {}}, + emboss = {vertices = {}}, + line_emboss = {vertices = {}}, + parallax = _parallax + } + local ext_up = self.config.ext_up and self.config.ext_up*G.TILESIZE or 0 + local res = self.config.res or math.min(self.VT.w, self.VT.h + math.abs(ext_up)/G.TILESIZE) > 3.5 and 0.8 or math.min(self.VT.w, self.VT.h + math.abs(ext_up)/G.TILESIZE) > 0.3 and 0.6 or 0.15 + local totw, toth, subw, subh = self.VT.w*G.TILESIZE, (self.VT.h + math.abs(ext_up)/G.TILESIZE)*G.TILESIZE, self.VT.w*G.TILESIZE-4*res, (self.VT.h + math.abs(ext_up)/G.TILESIZE)*G.TILESIZE-4*res + + local vertices = { + subw/2, subh/2-ext_up, + 0,4*res-ext_up, + 1*res,4*res-ext_up, + 1*res,2*res-ext_up, + 2*res,2*res-ext_up, + 2*res,1*res-ext_up, + 4*res,1*res-ext_up, + 4*res,0*res-ext_up, + subw,0*res-ext_up, + subw,1*res-ext_up, + subw+2*res,1*res-ext_up, + subw+2*res,2*res-ext_up, + subw+3*res,2*res-ext_up, + subw+3*res,4*res-ext_up, + totw,4*res-ext_up, + totw,subh-ext_up, + subw+3*res, subh-ext_up, + subw+3*res, subh+2*res-ext_up, + subw+2*res, subh+2*res-ext_up, + subw+2*res, subh+3*res-ext_up, + subw, subh+3*res-ext_up, + subw, toth-ext_up, + 4*res, toth-ext_up, + 4*res, subh+3*res-ext_up, + 2*res, subh+3*res-ext_up, + 2*res, subh+2*res-ext_up, + 1*res, subh+2*res-ext_up, + 1*res, subh-ext_up, + 0, subh-ext_up, + 0,4*res-ext_up, + } + for k, v in ipairs(vertices) do + if k%2 == 1 and v > totw*self.pixellated_rect.progress then v = totw*self.pixellated_rect.progress end + self.pixellated_rect.fill.vertices[k] = v + if k > 4 then + self.pixellated_rect.line.vertices[k-4] = v + if _emboss then + self.pixellated_rect.line_emboss.vertices[k-4] = v + (k%2 == 0 and -_emboss*self.shadow_parrallax.y or -0.7*_emboss*self.shadow_parrallax.x) + end + end + if k%2 == 0 then + self.pixellated_rect.shadow.vertices[k] = v -self.shadow_parrallax.y*_parallax + if _emboss then + self.pixellated_rect.emboss.vertices[k] = v + _emboss*G.TILESIZE + end + else + self.pixellated_rect.shadow.vertices[k] = v -self.shadow_parrallax.x*_parallax + if _emboss then + self.pixellated_rect.emboss.vertices[k] = v + end + end + end + end + + love.graphics.polygon((_type == 'line' or _type == 'line_emboss') and 'line' or "fill", self.pixellated_rect[_type].vertices) +end + +function UIElement:update(dt) + G.ARGS.FUNC_TRACKER = G.ARGS.FUNC_TRACKER or {} + if self.config.button_delay then + self.config.button_temp = self.config.button or self.config.button_temp + self.config.button = nil + self.config.button_delay_progress = (G.TIMERS.REAL - self.config.button_delay_start)/self.config.button_delay + if G.TIMERS.REAL >= self.config.button_delay_end then self.config.button_delay = nil end + end + if self.config.button_temp and not self.config.button_delay then self.config.button = self.config.button_temp end + if self.button_clicked then self.button_clicked = nil end + if self.config and self.config.func then + G.ARGS.FUNC_TRACKER[self.config.func] = (G.ARGS.FUNC_TRACKER[self.config.func] or 0) + 1 + G.FUNCS[self.config.func](self) + end + if self.UIT == G.UIT.T then self:update_text() end + if self.UIT == G.UIT.O then self:update_object() end + Node.update(self, dt) +end + +function UIElement:collides_with_point(cursor_trans) + if self.UIBox.states.collide.can then + return Node.collides_with_point(self, cursor_trans) + else + return false + end +end + +function UIElement:click() + if self.config.button and (not self.last_clicked or self.last_clicked + 0.1 < G.TIMERS.REAL) and self.states.visible and not self.under_overlay and not self.disable_button then + if self.config.one_press then self.disable_button = true end + self.last_clicked = G.TIMERS.REAL + + --Removes a layer from the overlay menu stack + if self.config.id == 'overlay_menu_back_button' then + G.CONTROLLER:mod_cursor_context_layer(-1) + G.NO_MOD_CURSOR_STACK = true + end + if G.OVERLAY_TUTORIAL and G.OVERLAY_TUTORIAL.button_listen == self.config.button then + G.FUNCS.tut_next() + end + G.FUNCS[self.config.button](self) + + G.NO_MOD_CURSOR_STACK = nil + + if self.config.choice then + local chosen_temp = self.config.chosen + local chosen_temp = self.config.chosen + local choices = self.UIBox:get_group(nil, self.config.group) + for k, v in pairs(choices) do + if v.config and v.config.choice then v.config.chosen = false end + end + self.config.chosen = chosen_temp or true + end + play_sound('button', 1, 0.3) + G.ROOM.jiggle = G.ROOM.jiggle + 0.5 + self.button_clicked = true + end + if self.config.button_UIE then + self.config.button_UIE:click() + end +end + +function UIElement:put_focused_cursor() + if self.config.focus_args and self.config.focus_args.type == 'tab' then + for k, v in pairs(self.children) do + if v.children[1].config.chosen then return v.children[1]:put_focused_cursor() end + end + else + return Node.put_focused_cursor(self) + end +end + +function UIElement:remove() + if self.config and self.config.object then + self.config.object:remove() + self.config.object = nil + end + + if self == G.CONTROLLER.text_input_hook then + G.CONTROLLER.text_input_hook = nil + end + remove_all(self.children) + + Moveable.remove(self) +end + +function UIElement:hover() + if self.config and self.config.on_demand_tooltip then + self.config.h_popup = create_popup_UIBox_tooltip(self.config.on_demand_tooltip) + self.config.h_popup_config ={align=self.T.y > G.ROOM.T.h/2 and 'tm' or 'bm', offset = {x=0,y=self.T.y > G.ROOM.T.h/2 and -0.1 or 0.1}, parent = self} + end + if self.config.tooltip then + self.config.h_popup = create_popup_UIBox_tooltip(self.config.tooltip) + self.config.h_popup_config ={align="tm", offset = {x=0,y=-0.1}, parent = self} + end + if self.config.detailed_tooltip and G.CONTROLLER.HID.pointer then + self.config.h_popup = create_UIBox_detailed_tooltip(self.config.detailed_tooltip) + self.config.h_popup_config ={align="tm", offset = {x=0,y=-0.1}, parent = self} + end + Node.hover(self) +end + +function UIElement:stop_hover() + Node.stop_hover(self) + if self.config and self.config.on_demand_tooltip then + self.config.h_popup = nil + end +end + +function UIElement:release(other) + if self.parent then self.parent:release(other) end +end + +function is_UI_containter(node) + if node.UIT ~= G.UIT.C and node.UIT ~= G.UIT.R and node.UIT ~= G.UIT.ROOT then + return false + end + return true +end diff --git a/lovely/dump/functions/UI_definitions.lua b/lovely/dump/functions/UI_definitions.lua new file mode 100644 index 0000000..f68d60a --- /dev/null +++ b/lovely/dump/functions/UI_definitions.lua @@ -0,0 +1,6482 @@ +LOVELY_INTEGRITY = '6dc6391935253a5dba5a3febb135e71775ed356629edd2463e2d01e436702ddd' + +--Create a global UIDEF that contains all UI definition functions\ +--As a rule, these contain functions that return a table T representing the definition for a UIBox +G.UIDEF = {} + +function create_UIBox_debug_tools() +local debugplus = require("debugplus.core") +debugplus.registerButtons() + G.debug_tool_config = G.debug_tool_config or {} + G.FUNCS.DT_add_money = function() if G.STAGE == G.STAGES.RUN then ease_dollars(10) end end + G.FUNCS.DT_add_round = function() if G.STAGE == G.STAGES.RUN then ease_round(1) end end + G.FUNCS.DT_add_ante = function() if G.STAGE == G.STAGES.RUN then ease_ante(1) end end + G.FUNCS.DT_add_hand = function() if G.STAGE == G.STAGES.RUN then ease_hands_played(1) end end + G.FUNCS.DT_add_discard = function() if G.STAGE == G.STAGES.RUN then ease_discard(1) end end + G.FUNCS.DT_reroll_boss = function() if G.STAGE == G.STAGES.RUN and G.blind_select_opts then G.from_boss_tag = true; G.FUNCS.reroll_boss(); G.from_boss_tag = nil end end + G.FUNCS.DT_toggle_background = function() G.debug_background_toggle = not G.debug_background_toggle end + G.FUNCS.DT_add_chips = function() if G.STAGE == G.STAGES.RUN then update_hand_text({delay = 0}, {chips = 10 + G.GAME.current_round.current_hand.chips}); play_sound('chips1') end end + G.FUNCS.DT_add_mult = function() if G.STAGE == G.STAGES.RUN then update_hand_text({delay = 0}, {mult = 10 + G.GAME.current_round.current_hand.mult}); play_sound('multhit1') end end + G.FUNCS.DT_x_chips = function() if G.STAGE == G.STAGES.RUN then update_hand_text({delay = 0}, {chips = 2*G.GAME.current_round.current_hand.chips}); play_sound('chips1') end end + G.FUNCS.DT_x_mult = function() if G.STAGE == G.STAGES.RUN then update_hand_text({delay = 0}, {mult = 10*G.GAME.current_round.current_hand.mult}); play_sound('multhit2') end end + G.FUNCS.DT_chip_mult_reset = function() if G.STAGE == G.STAGES.RUN then update_hand_text({delay = 0}, {mult = 0, chips = 0}) end end + G.FUNCS.DT_win_game = function() if G.STAGE == G.STAGES.RUN then win_game() end end + G.FUNCS.DT_lose_game = function() if G.STAGE == G.STAGES.RUN then G.STATE = G.STATES.GAME_OVER; G.STATE_COMPLETE = false end end + G.FUNCS.DT_jimbo_toggle = function() + if G.DT_jimbo then + if G.DT_jimbo.children.particles.states.visible then + if G.DT_jimbo.children.card.states.visible then + G.DT_jimbo.children.card.states.visible = false + else + G.DT_jimbo.children.card.states.visible = true + G.DT_jimbo.children.particles.states.visible = false + end + else + G.DT_jimbo:remove() + G.DT_jimbo = nil + if G.SPLASH_LOGO then + G.SPLASH_LOGO.states.visible = true + if G.title_top and G.title_top.cards[1] then G.title_top.cards[1].states.visible = true end + end + end + else + if G.SPLASH_LOGO then + G.SPLASH_LOGO.states.visible = false + if G.title_top and G.title_top.cards[1] then G.title_top.cards[1].states.visible = false end + end + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.4, + blockable = false, + func = function() + G.DT_jimbo = Card_Character({x = G.ROOM.T.w/2,y = G.ROOM.T.h/2}) + + G.DT_jimbo:set_alignment{ + major = G.ROOM_ATTACH, + type = 'cm' + } + return true + end + })) + + end end + G.FUNCS.DT_jimbo_talk = function() + if G.DT_jimbo then + G.DT_jimbo:add_speech_bubble({ + " ", + " ", + " ", + }, 'cr') + G.DT_jimbo:say_stuff(4) end + end + + local t = {n=G.UIT.ROOT, config = {align = 'cm', r = 0.1}, nodes={ + UIBox_dyn_container({ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "While in collection, hover over a card", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "and press the following keys:", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.BLUE, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.T, config={text = "[1] Unlock", scale = 0.25, colour = G.C.WHITE}} + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.BLUE, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.T, config={text = "[2] Discover", scale = 0.25, colour = G.C.WHITE}} + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.BLUE, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.T, config={text = "[3] Spawn", scale = 0.25, colour = G.C.WHITE}} + }} + }} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "Hover over any Joker/Playing card", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "hold [" .. require("debugplus.util").ctrlText .. "] (togglable in config)", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "and press [Q] to cycle Edition", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [W] to cycle Enhancement", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [E] to cycle Seal", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [A/S/D] to toggle Eternal/Perishable/Rental", scale = 0.2, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [F] to toggle Coupon (make free)", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [R/C] to destroy/copy card", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [M] to reload atlases", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [UP/DOWN] to cycle rank", scale = 0.2, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.00}, nodes={ + {n=G.UIT.T, config={text = "press [RIGHT/LEFT] to cycle suit", scale = 0.2, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "press [z] plus [1-3] to save a save state", scale = 0.2, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.00}, nodes={ + {n=G.UIT.T, config={text = "press [x] plus [1-3] to load a save state", scale = 0.2, colour = G.C.WHITE, shadow = true}} + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "Press [H] to isolate background", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "Press [J] to play splash animation", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "Press [8] to toggle cursor", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = "Press [9] to toggle all tooltips", scale = 0.25, colour = G.C.WHITE, shadow = true}} + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.15}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes={ + UIBox_button{ label = {"$10"}, button = "DT_add_money", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"+1 Round"}, button = "DT_add_round", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"+1 Ante"}, button = "DT_add_ante", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"+1 Hand"}, button = "DT_add_hand", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"+1 Discard"}, button = "DT_add_discard", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Boss Reroll"}, button = "DT_reroll_boss", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Background"}, button = "DT_toggle_background", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Win Blind"}, button = "DT_win_blind", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Double Tag"}, button = "DT_double_tag", minw = 1.7, minh = 0.4, scale = 0.35}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes={ + UIBox_button{ label = {"+10 chips"}, button = "DT_add_chips", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"+10 mult"}, button = "DT_add_mult", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"X2 chips"}, button = "DT_x_chips", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"X10 mult"}, button = "DT_x_mult", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Win this Run"}, button = "DT_win_game", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Lose this Run"}, button = "DT_lose_game", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Reset"}, button = "DT_chip_mult_reset", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Jimbo"}, button = "DT_jimbo_toggle", minw = 1.7, minh = 0.4, scale = 0.35}, + UIBox_button{ label = {"Jimbo talk"}, button = "DT_jimbo_talk", minw = 1.7, minh = 0.4, scale = 0.35}, + }} + }} + }, true) + }} + return t +end + +function create_UIBox_notify_alert(_achievement, _type) + local _c, _atlas = G.P_CENTERS[_achievement], + _type == 'Joker' and G.ASSET_ATLAS["Joker"] or + _type == 'Voucher' and G.ASSET_ATLAS["Voucher"] or + _type == 'Back' and G.ASSET_ATLAS["centers"] or + G.ASSET_ATLAS["icons"] + local _smods_atlas = _c and ((G.SETTINGS.colourblind_option and _c.hc_atlas or _c.lc_atlas) or _c.atlas) + if _smods_atlas then + _atlas = G.ASSET_ATLAS[_smods_atlas] or _atlas + end + + if SMODS.Achievements[_achievement] then _c = SMODS.Achievements[_achievement]; _atlas = G.ASSET_ATLAS[_c.atlas] end + local t_s = Sprite(0,0,1.5*(_atlas.px/_atlas.py),1.5,_atlas, _c and _c.pos or {x=3, y=0}) + t_s.states.drag.can = false + t_s.states.hover.can = false + t_s.states.collide.can = false + + local subtext = _type == 'achievement' and localize(G.F_TROPHIES and 'k_trophy' or 'k_achievement') or + _type == 'Joker' and localize('k_joker') or + _type == 'Voucher' and localize('k_voucher') or + _type == 'Back' and localize('k_deck') or 'ERROR' + + if _achievement == 'b_challenge' then subtext = localize('k_challenges') end + local name = _type == 'achievement' and localize(_achievement, 'achievement_names') or 'ERROR' + + local t = {n=G.UIT.ROOT, config = {align = 'cl', r = 0.1, padding = 0.06, colour = G.C.UI.TRANSPARENT_DARK}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0.2, minw = 20, r = 0.1, colour = G.C.BLACK, outline = 1.5, outline_colour = G.C.GREY}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1}, nodes={ + {n=G.UIT.O, config={object = t_s}}, + }}, + _type ~= 'achievement' and {n=G.UIT.R, config={align = "cm", padding = 0.04}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 3.4}, nodes={ + {n=G.UIT.T, config={text = subtext, scale = 0.5, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", maxw = 3.4}, nodes={ + {n=G.UIT.T, config={text = localize('k_unlocked_ex'), scale = 0.35, colour = G.C.FILTER, shadow = true}}, + }} + }} + or {n=G.UIT.R, config={align = "cm", padding = 0.04}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 3.4, padding = 0.1}, nodes={ + {n=G.UIT.T, config={text = name, scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", maxw = 3.4}, nodes={ + {n=G.UIT.T, config={text = subtext, scale = 0.3, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", maxw = 3.4}, nodes={ + {n=G.UIT.T, config={text = localize('k_unlocked_ex'), scale = 0.35, colour = G.C.FILTER, shadow = true}}, + }} + }} + }} + }} + }} + return t +end + +function create_UIBox_online_high_scores() + G.HTTP_MANAGER.out_channel:push({get_score = true}) + local padding, col, minw = 0.05, G.C.UI.TRANSPARENT_DARK, 0 + local t = {n=G.UIT.ROOT, config = {align = 'cm', minw=minw, r = 0.1, colour = col, padding = padding}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1, r = 0.1, colour = G.C.BLACK}, nodes={ + }} + }} + return t +end + +function create_UIBox_high_scores_filling(_resp) + local scores = {} + _resp = assert(loadstring(_resp))() + if not _resp then + return {n=G.UIT.ROOT, config = {align = 'cm', r = 0.1, colour = G.C.L_BLACK, padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1, minh = 1.3}, nodes={ + {n=G.UIT.T, config={text = 'ERROR', scale = 0.9, colour = G.C.RED, shadow = true}}, + }} + }} + end + for i = 1, 6 do + local v = _resp[i] or {username = '-'} + v.score = v.score and math.floor(v.score) or nil + local name_col = v.username == (G.SETTINGS.COMP and G.SETTINGS.COMP.name or nil) and G.C.FILTER or G.C.WHITE + scores[#scores+1] = {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cl", padding = 0, minw = 0.3}, nodes={ + {n=G.UIT.T, config={text = i..'.', scale = 0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.C, config={align = "cl", padding = 0, minw = 1.7, maxw = 1.6}, nodes={ + {n=G.UIT.T, config={text = (v.username), scale = math.min(0.6, 8*0.56/v.username:len()), colour = v.score and name_col or G.C.UI.TRANSPARENT_LIGHT, shadow = true}} + }}, + {n=G.UIT.C, config={align = "cl", minh = 0.8, r = 0.1, minw = 2.5, colour = G.C.BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, minw = 2.6}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {type(v.score) == 'number' and number_format(v.score) or ''}, colours = {G.C.RED},shadow = true, float = true,maxw = 2.5, scale = math.min(0.75, score_number_scale(1.5, v.score))})}}, + }}, + }}, + }}, + }} + end + return {n=G.UIT.ROOT, config = {align = 'cm', r = 0.1, colour = G.C.L_BLACK, padding = 0.05}, nodes=scores} +end + +function G.UIDEF.use_and_sell_buttons(card) + local sell = nil + local use = nil + if card.area and card.area.config.type == 'joker' then + sell = {n=G.UIT.C, config={align = "cr"}, nodes={ + {n=G.UIT.C, config={ref_table = card, align = "cr",padding = 0.1, r=0.08, minw = 1.25, hover = true, shadow = true, colour = G.C.UI.BACKGROUND_INACTIVE, one_press = true, button = 'sell_card', func = 'can_sell_card'}, nodes={ + {n=G.UIT.B, config = {w=0.1,h=0.6}}, + {n=G.UIT.C, config={align = "tm"}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 1.25}, nodes={ + {n=G.UIT.T, config={text = localize('b_sell'),colour = G.C.UI.TEXT_LIGHT, scale = 0.4, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = localize('$'),colour = G.C.WHITE, scale = 0.4, shadow = true}}, + {n=G.UIT.T, config={ref_table = card, ref_value = 'sell_cost_label',colour = G.C.WHITE, scale = 0.55, shadow = true}} + }} + }} + }}, + }} + end + if card.ability.consumeable then + if (card.area == G.pack_cards and G.pack_cards) then + return { + n=G.UIT.ROOT, config = {padding = 0, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={mid = true}, nodes={ + }}, + {n=G.UIT.R, config={ref_table = card, r = 0.08, padding = 0.1, align = "bm", minw = 0.5*card.T.w - 0.15, minh = 0.8*card.T.h, maxw = 0.7*card.T.w - 0.15, hover = true, shadow = true, colour = G.C.UI.BACKGROUND_INACTIVE, one_press = true, button = 'use_card', func = 'can_use_consumeable'}, nodes={ + {n=G.UIT.T, config={text = localize('b_use'),colour = G.C.UI.TEXT_LIGHT, scale = 0.55, shadow = true}} + }}, + }} + end + use = + {n=G.UIT.C, config={align = "cr"}, nodes={ + + {n=G.UIT.C, config={ref_table = card, align = "cr",maxw = 1.25, padding = 0.1, r=0.08, minw = 1.25, minh = (card.area and card.area.config.type == 'joker') and 0 or 1, hover = true, shadow = true, colour = G.C.UI.BACKGROUND_INACTIVE, one_press = true, button = 'use_card', func = 'can_use_consumeable'}, nodes={ + {n=G.UIT.B, config = {w=0.1,h=0.6}}, + {n=G.UIT.T, config={text = localize('b_use'),colour = G.C.UI.TEXT_LIGHT, scale = 0.55, shadow = true}} + }} + }} + elseif card.area and card.area == G.pack_cards then + return { + n=G.UIT.ROOT, config = {padding = 0, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={ref_table = card, r = 0.08, padding = 0.1, align = "bm", minw = 0.5*card.T.w - 0.15, maxw = 0.9*card.T.w - 0.15, minh = 0.3*card.T.h, hover = true, shadow = true, colour = G.C.UI.BACKGROUND_INACTIVE, one_press = true, button = 'use_card', func = 'can_select_card'}, nodes={ + {n=G.UIT.T, config={text = localize('b_select'),colour = G.C.UI.TEXT_LIGHT, scale = 0.45, shadow = true}} + }}, + }} + end + local t = { + n=G.UIT.ROOT, config = {padding = 0, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.C, config={padding = 0.15, align = 'cl'}, nodes={ + {n=G.UIT.R, config={align = 'cl'}, nodes={ + sell + }}, + {n=G.UIT.R, config={align = 'cl'}, nodes={ + use + }}, + }}, + }} + return t +end + +function G.UIDEF.card_focus_ui(card) + local card_width = card.T.w + (card.ability.consumeable and -0.1 or card.ability.set == 'Voucher' and -0.16 or 0) + + local playing_card_colour = copy_table(G.C.WHITE) + playing_card_colour[4] = 1.5 + if G.hand and card.area == G.hand then ease_value(playing_card_colour, 4, -1.5, nil, 'REAL',nil, 0.2, 'quad') end + + local tcnx, tcny = card.T.x + card.T.w/2 - G.ROOM.T.w/2, card.T.y + card.T.h/2 - G.ROOM.T.h/2 + + local base_background = UIBox{ + T = {card.VT.x,card.VT.y,0,0}, + definition = + (not G.hand or card.area ~= G.hand) and {n=G.UIT.ROOT, config = {align = 'cm', minw = card_width + 0.3, minh = card.T.h + 0.3, r = 0.1, colour = adjust_alpha(G.C.BLACK, 0.7), outline_colour = lighten(G.C.JOKER_GREY, 0.5), outline = 1.5, line_emboss = 0.8}, nodes={ + {n=G.UIT.R, config={id = 'ATTACH_TO_ME'}, nodes={}} + }} or + {n=G.UIT.ROOT, config = {align = 'cm', minw = card_width, minh = card.T.h, r = 0.1, colour = playing_card_colour}, nodes={ + {n=G.UIT.R, config={id = 'ATTACH_TO_ME'}, nodes={}} + }}, + config = { + align = 'cm', + offset = {x= 0.007*tcnx*card.T.w, y = 0.007*tcny*card.T.h}, + parent = card, + r_bond = (not G.hand or card.area ~= G.hand) and 'Weak' or 'Strong' + } + } + + base_background.set_alignment = function() + local cnx, cny = card.T.x + card.T.w/2 - G.ROOM.T.w/2, card.T.y + card.T.h/2 - G.ROOM.T.h/2 + Moveable.set_alignment(card.children.focused_ui, {offset = {x= 0.007*cnx*card.T.w, y = 0.007*cny*card.T.h}}) + end + + local base_attach = base_background:get_UIE_by_ID('ATTACH_TO_ME') + + --The card UI can have BUY, REDEEM, USE, and SELL buttons depending on the context of the card + if card.area == G.shop_jokers and G.shop_jokers then --Add a buy button + local buy_and_use = nil + if card.ability.consumeable then + base_attach.children.buy_and_use = G.UIDEF.card_focus_button{ + card = card, parent = base_attach, type = 'buy_and_use', + func = 'can_buy_and_use', button = 'buy_from_shop', card_width = card_width + } + buy_and_use = true + end + base_attach.children.buy = G.UIDEF.card_focus_button{ + card = card, parent = base_attach, type = 'buy', + func = 'can_buy', button = 'buy_from_shop', card_width = card_width, buy_and_use = buy_and_use + } + end + if card.area == G.shop_vouchers and G.shop_vouchers then --Add a redeem button + base_attach.children.redeem = G.UIDEF.card_focus_button{ + card = card, parent = base_attach, type = 'buy', + func = 'can_redeem', button = 'redeem_from_shop', card_width = card_width + } + end + if card.area == G.shop_booster and G.shop_booster then --Add a redeem button + base_attach.children.redeem = G.UIDEF.card_focus_button{ + card = card, parent = base_attach, type = 'buy', + func = 'can_open', button = 'open_booster', card_width = card_width*0.85 + } + end + if ((card.area == G.consumeables and G.consumeables) or (card.area == G.pack_cards and G.pack_cards)) and + card.ability.consumeable then --Add a use button + base_attach.children.use = G.UIDEF.card_focus_button{ + card = card, parent = base_attach, type = 'use', + func = 'can_use_consumeable', button = 'use_card', card_width = card_width + } + end + if (card.area == G.pack_cards and G.pack_cards) and not card.ability.consumeable then --Add a use button + base_attach.children.use = G.UIDEF.card_focus_button{ + card = card, parent = base_attach, type = 'select', + func = 'can_select_card', button = 'use_card', card_width = card_width + } + end + if (card.area == G.jokers and G.jokers or card.area == G.consumeables and G.consumeables) and G.STATE ~= G.STATES.TUTORIAL then --Add a sell button + base_attach.children.sell = G.UIDEF.card_focus_button{ + card = card, parent = base_attach, type = 'sell', + func = 'can_sell_card', button = 'sell_card', card_width = card_width + } + end + + return base_background +end + +function G.UIDEF.card_focus_button(args) + if not args then return end + + local button_contents = {} + if args.type == 'sell' then + button_contents = + {n=G.UIT.C, config={align = "cl"}, nodes={ + {n=G.UIT.R, config={align = "cl", maxw = 1}, nodes={ + {n=G.UIT.T, config={text = localize('b_sell'),colour = G.C.UI.TEXT_LIGHT, scale = 0.4, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cl"}, nodes={ + {n=G.UIT.T, config={text = localize('$'),colour = G.C.WHITE, scale = 0.4, shadow = true}}, + {n=G.UIT.T, config={ref_table = args.card, ref_value = 'sell_cost_label',colour = G.C.WHITE, scale = 0.55, shadow = true}} + }} + }} + elseif args.type == 'buy' then + button_contents = {n=G.UIT.T, config={text = localize('b_buy'),colour = G.C.WHITE, scale = 0.5}} + elseif args.type == 'select' then + button_contents = {n=G.UIT.T, config={text = localize('b_select'),colour = G.C.WHITE, scale = 0.3}} + elseif args.type == 'redeem' then + button_contents = {n=G.UIT.T, config={text = localize('b_redeem'),colour = G.C.WHITE, scale = 0.5}} + elseif args.type == 'use' then + button_contents = {n=G.UIT.T, config={text = localize('b_use'),colour = G.C.WHITE, scale = 0.5}} + elseif args.type == 'buy_and_use' then + button_contents = + {n=G.UIT.C, config={align = "cr"}, nodes={ + {n=G.UIT.R, config={align = "cr", maxw = 1}, nodes={ + {n=G.UIT.T, config={text = localize('b_buy'),colour = G.C.UI.TEXT_LIGHT, scale = 0.4, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cr", maxw = 1}, nodes={ + {n=G.UIT.T, config={text = localize('b_and_use'),colour = G.C.WHITE, scale = 0.3, shadow = true}}, + }} + }} + end + + return UIBox{ + T = {args.card.VT.x,args.card.VT.y,0,0}, + definition = + {n=G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={id = args.type == 'buy_and_use' and 'buy_and_use' or nil, ref_table = args.card, ref_parent = args.parent, align = args.type == 'sell' and 'cl' or 'cr', colour = G.C.BLACK, shadow = true, r = 0.08, func = args.func, one_press = true, button = args.button, focus_args = {type = 'none'}, hover = true}, nodes={ + {n=G.UIT.R, config={align = args.type == 'sell' and 'cl' or 'cr', minw = 1 + (args.type == 'select' and 0.1 or 0), minh = args.type == 'sell' and 1.5 or 1, padding = 0.08, + focus_args = {button = args.type == 'sell' and 'leftshoulder' or args.type == 'buy_and_use' and 'leftshoulder' or 'rightshoulder', scale = 0.55, orientation = args.type == 'sell' and 'tli' or 'tri', offset = {x = args.type == 'sell' and 0.1 or -0.1, y = 0}, type = 'none'}, + func = 'set_button_pip'}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.3}, nodes={}}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + args.type ~= 'sell' and {n=G.UIT.C, config={align = "cm",minw = 0.2, minh = 0.6}, nodes={}} or nil, + {n=G.UIT.C, config={align = "cm", maxw = 1}, nodes={ + button_contents + }}, + args.type == 'sell' and {n=G.UIT.C, config={align = "cm",minw = 0.2, minh = 0.6}, nodes={}} or nil, + }} + }} + }} + }}, + config = { + align = args.type == 'sell' and 'cl' or 'cr', + offset = {x=(args.type == 'sell' and -1 or 1)*((args.card_width or 0) - 0.17 - args.card.T.w/2),y=args.type == 'buy_and_use' and 0.6 or (args.buy_and_use) and -0.6 or 0}, + parent = args.parent, + } + } +end + +function G.UIDEF.speech_bubble(text_key, loc_vars) + local text = {} + if loc_vars and loc_vars.quip then + localize{type = 'quips', key = text_key or 'lq_1', vars = loc_vars or {}, nodes = text} + else + localize{type = 'tutorial', key = text_key or 'sb_1', vars = loc_vars or {}, nodes = text} + end + local row = {} + for k, v in ipairs(text) do + row[#row+1] = {n=G.UIT.R, config={align = "cl"}, nodes=v} + end + local t = {n=G.UIT.ROOT, config = {align = "cm", minh = 1,r = 0.3, padding = 0.07, minw = 1, colour = G.C.JOKER_GREY, shadow = true}, nodes={ + {n=G.UIT.C, config={align = "cm", minh = 1,r = 0.2, padding = 0.1, minw = 1, colour = G.C.WHITE}, nodes={ + {n=G.UIT.C, config={align = "cm", minh = 1,r = 0.2, padding = 0.03, minw = 1, colour = G.C.WHITE}, nodes=row}} + } + }} + return t +end + +function create_UIBox_highlight(rect) + local t = {n=G.UIT.ROOT, config = {align = "cm", minh = rect.T.h+0.1, minw = rect.T.w+0.15, r = 0.15, colour = G.C.DARK_EDITION}, nodes={ + }} +return t +end + +function G.UIDEF.deck_preview(args) + + local _minh, _minw = 0.35, 0.5 + local suit_labels = {} + local suit_counts = { + Spades = 0, + Hearts = 0, + Clubs = 0, + Diamonds = 0 + } + local mod_suit_counts = { + Spades = 0, + Hearts = 0, + Clubs = 0, + Diamonds = 0 + } + local mod_suit_diff = false + local wheel_flipped, wheel_flipped_text = 0, nil + local flip_col = G.C.WHITE + local rank_counts = {} + local deck_tables = {} + remove_nils(G.playing_cards) + table.sort(G.playing_cards, function (a, b) return a:get_nominal('suit') > b:get_nominal('suit') end ) + local SUITS = { + Spades = {}, + Hearts = {}, + Clubs = {}, + Diamonds = {}, + } + + for k, v in pairs(SUITS) do + for i = 1, 14 do + SUITS[k][#SUITS[k]+1] = {} + end + end + + local suit_map = {'Spades', 'Hearts', 'Clubs', 'Diamonds'} + local SUITS_SORTED = Cartomancer.tablecopy(SUITS) + local stones = nil + local rank_name_mapping = {'A','K','Q','J','10',9,8,7,6,5,4,3,2} + + for k, v in ipairs(G.playing_cards) do + if v.ability.effect == 'Stone Card' then + stones = stones or 0 + end + if (v.area and v.area == G.deck) or v.ability.wheel_flipped then + if v.ability.wheel_flipped then wheel_flipped = wheel_flipped + 1 end + if v.ability.effect == 'Stone Card' then + stones = stones + 1 + else + for kk, vv in pairs(suit_counts) do + if v.base.suit == kk then suit_counts[kk] = suit_counts[kk] + 1 end + if v:is_suit(kk) then mod_suit_counts[kk] = mod_suit_counts[kk] + 1 end + end + if SUITS[v.base.suit][v.base.id] then + table.insert(SUITS[v.base.suit][v.base.id], v) + end + rank_counts[v.base.id] = (rank_counts[v.base.id] or 0) + 1 + end + end + end + + wheel_flipped_text = (wheel_flipped > 0) and {n=G.UIT.T, config={text = '?',colour = G.C.FILTER, scale =0.25, shadow = true}} or nil + flip_col = wheel_flipped_text and mix_colours(G.C.FILTER, G.C.WHITE,0.7) or G.C.WHITE + + suit_labels[#suit_labels+1] = {n=G.UIT.R, config={align = "cm", r = 0.1, padding = 0.04, minw = _minw, minh = 2*_minh+0.25}, nodes={ + stones and {n=G.UIT.T, config={text = localize('ph_deck_preview_stones')..': ',colour = G.C.WHITE, scale =0.25, shadow = true}} + or nil, + stones and {n=G.UIT.T, config={text = ''..stones,colour = (stones > 0 and G.C.WHITE or G.C.UI.TRANSPARENT_LIGHT), scale =0.4, shadow = true}} + or nil, + }} + + local _row = {} + local _bg_col = G.C.JOKER_GREY + for k, v in ipairs(rank_name_mapping) do + local _tscale = 0.3 + local _colour = G.C.BLACK + local rank_col = v == 'A' and _bg_col or (v == 'K' or v == 'Q' or v == 'J') and G.C.WHITE or _bg_col + rank_col = mix_colours(rank_col, _bg_col, 0.8) + + local _col = {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", r = 0.1, minw = _minw, minh = _minh, colour = rank_col, emboss = 0.04, padding = 0.03}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = ''..v,colour = _colour, scale =1.6*_tscale}}, + }}, + {n=G.UIT.R, config={align = "cm", minw = _minw+0.04, minh = _minh, colour = G.C.L_BLACK, r = 0.1}, nodes={ + {n=G.UIT.T, config={text = ''..(rank_counts[15 - k] or 0),colour = flip_col, scale =_tscale, shadow = true}} + }} + }} + }} + table.insert(_row, _col) + end + table.insert(deck_tables, {n=G.UIT.R, config={align = "cm", padding = 0.04}, nodes=_row}) + + for j = 1, 4 do + _row = {} + _bg_col = mix_colours(G.C.SUITS[suit_map[j]], G.C.L_BLACK, 0.7) + for i = 14, 2, -1 do + local _tscale = #SUITS[suit_map[j]][i] > 0 and 0.3 or 0.25 + local _colour = #SUITS[suit_map[j]][i] > 0 and flip_col or G.C.UI.TRANSPARENT_LIGHT + + local _col = {n=G.UIT.C, config={align = "cm",padding = 0.05, minw = _minw+0.098, minh = _minh}, nodes={ + {n=G.UIT.T, config={text = ''..#SUITS[suit_map[j]][i],colour = _colour, scale =_tscale, shadow = true, lang = G.LANGUAGES['en-us']}}, + }} + table.insert(_row, _col) + end + table.insert(deck_tables, {n=G.UIT.R, config={align = "cm", r = 0.1, padding = 0.04, minh = 0.4, colour = _bg_col}, nodes=_row}) + end + + for k, v in ipairs(suit_map) do + local _x = (v == 'Spades' and 3) or (v == 'Hearts' and 0) or (v == 'Clubs' and 2) or (v == 'Diamonds' and 1) + local t_s = Sprite(0,0,0.3,0.3,G.ASSET_ATLAS["ui_"..(G.SETTINGS.colourblind_option and 2 or 1)], {x=_x, y=1}) + t_s.states.drag.can = false + t_s.states.hover.can = false + t_s.states.collide.can = false + + if mod_suit_counts[v] ~= suit_counts[v] then mod_suit_diff = true end + + suit_labels[#suit_labels+1] = + {n=G.UIT.R, config={align = "cm", r = 0.1, padding = 0.03, colour = G.C.JOKER_GREY}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = _minw, minh = _minh}, nodes={ + {n=G.UIT.O, config={can_collide = false, object = t_s}} + }}, + {n=G.UIT.C, config={align = "cm", minw = _minw*2.4, minh = _minh, colour = G.C.L_BLACK, r = 0.1}, nodes={ + {n=G.UIT.T, config={text = ''..suit_counts[v],colour = flip_col, scale =0.3, shadow = true, lang = G.LANGUAGES['en-us']}}, + mod_suit_counts[v] ~= suit_counts[v] and {n=G.UIT.T, config={text = ' ('..mod_suit_counts[v]..')',colour = mix_colours(G.C.BLUE, G.C.WHITE,0.7), scale =0.28, shadow = true, lang = G.LANGUAGES['en-us']}} or nil, + }} + }} + end + + + local t = + {n=G.UIT.ROOT, config={align = "cm", colour = G.C.JOKER_GREY, r = 0.1, emboss = 0.05, padding = 0.07}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1, emboss = 0.05, colour = G.C.BLACK, padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.04}, nodes=suit_labels}, + {n=G.UIT.C, config={align = "cm", padding = 0.02}, nodes=deck_tables} + }}, + mod_suit_diff and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={padding = 0.3, r = 0.1, colour = mix_colours(G.C.BLUE, G.C.WHITE,0.7)}, nodes = {}}, + {n=G.UIT.T, config={text =' '..localize('ph_deck_preview_effective'),colour = G.C.WHITE, scale =0.3}}, + }} or nil, + wheel_flipped_text and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={padding = 0.3, r = 0.1, colour = flip_col}, nodes = {}}, + {n=G.UIT.T, config={text =' '..(wheel_flipped > 1 and + localize{type = 'variable', key = 'deck_preview_wheel_plural', vars = {wheel_flipped}} or + localize{type = 'variable', key = 'deck_preview_wheel_singular', vars = {wheel_flipped}}),colour = G.C.WHITE, scale =0.3}}, + }} or nil, + }} + }} + return t +end + +function create_UIBox_character_button(args) + local button = args.button or "NONE" + local func = args.func or nil + local colour = args.colour or G.C.RED + local update_func = args.update_func or nil + + local t = {n=G.UIT.ROOT, config = {align = "cm", padding = 0.1, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.C, config={align = "tm", minw = 1.9, padding = 0.2, minh = 1.2, r = 0.1, hover = true, colour = colour, button = func, func = update_func, shadow = true, maxw = args.maxw}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = button, scale = 0.55, colour = G.C.UI.TEXT_LIGHT, focus_args = {button = 'x', orientation = 'bm'}, func = 'set_button_pip'}} + }} + }}, + }} + return t +end + +function G.UIDEF.shop() + G.shop_jokers = CardArea( + G.hand.T.x+0, + G.hand.T.y+G.ROOM.T.y + 9, + math.min(G.GAME.shop.joker_max,4)*1.02*G.CARD_W, + 1.05*G.CARD_H, + {card_limit = G.GAME.shop.joker_max, type = 'shop', highlight_limit = 1}) + + + G.shop_vouchers = CardArea( + G.hand.T.x+0, + G.hand.T.y+G.ROOM.T.y + 9, + 2.1*G.CARD_W, + 1.05*G.CARD_H, + {card_limit = 1, type = 'shop', highlight_limit = 1}) + + G.shop_booster = CardArea( + G.hand.T.x+0, + G.hand.T.y+G.ROOM.T.y + 9, + 2.4*G.CARD_W, + 1.15*G.CARD_H, + {card_limit = 2, type = 'shop', highlight_limit = 1, card_w = 1.27*G.CARD_W}) + + local shop_sign = AnimatedSprite(0,0, 4.4, 2.2, G.ANIMATION_ATLAS['shop_sign']) + shop_sign:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'} + }) + G.SHOP_SIGN = UIBox{ + definition = + {n=G.UIT.ROOT, config = {colour = G.C.DYN_UI.MAIN, emboss = 0.05, align = 'cm', r = 0.1, padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1, minw = 4.72, minh = 3.1, colour = G.C.DYN_UI.DARK, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = shop_sign}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('ph_improve_run')}, colours = {lighten(G.C.GOLD, 0.3)},shadow = true, rotate = true, float = true, bump = true, scale = 0.5, spacing = 1, pop_in = 1.5, maxw = 4.3})}} + }}, + }}, + }}, + config = { + align="cm", + offset = {x=0,y=-15}, + major = G.HUD:get_UIE_by_ID('row_blind'), + bond = 'Weak' + } + } + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + G.SHOP_SIGN.alignment.offset.y = 0 + return true + end) + })) + local t = {n=G.UIT.ROOT, config = {align = 'cl', colour = G.C.CLEAR}, nodes={ + UIBox_dyn_container({ + {n=G.UIT.C, config={align = "cm", padding = 0.1, emboss = 0.05, r = 0.1, colour = G.C.DYN_UI.BOSS_MAIN}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.R,config={id = 'next_round_button', align = "cm", minw = 2.8, minh = 1.5, r=0.15,colour = G.C.RED, one_press = true, button = 'toggle_shop', hover = true,shadow = true}, nodes = { + {n=G.UIT.R, config={align = "cm", padding = 0.07, focus_args = {button = 'y', orientation = 'cr'}, func = 'set_button_pip'}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 1.3}, nodes={ + {n=G.UIT.T, config={text = localize('b_next_round_1'), scale = 0.4, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", maxw = 1.3}, nodes={ + {n=G.UIT.T, config={text = localize('b_next_round_2'), scale = 0.4, colour = G.C.WHITE, shadow = true}} + }} + }}, + }}, + {n=G.UIT.R, config={align = "cm", minw = 2.8, minh = 1.6, r=0.15,colour = G.C.GREEN, button = 'reroll_shop', func = 'can_reroll', hover = true,shadow = true}, nodes = { + {n=G.UIT.R, config={align = "cm", padding = 0.07, focus_args = {button = 'x', orientation = 'cr'}, func = 'set_button_pip'}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 1.3}, nodes={ + {n=G.UIT.T, config={text = localize('k_reroll'), scale = 0.4, colour = G.C.WHITE, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", maxw = 1.3, minw = 1}, nodes={ + {n=G.UIT.T, config={text = localize('$'), scale = 0.7, colour = G.C.WHITE, shadow = true}}, + {n=G.UIT.T, config={ref_table = G.GAME.current_round, ref_value = 'reroll_cost', scale = 0.75, colour = G.C.WHITE, shadow = true}}, + }} + }} + }}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.2, r=0.2, colour = G.C.L_BLACK, emboss = 0.05, minw = 8.2}, nodes={ + {n=G.UIT.O, config={object = G.shop_jokers}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.2}, nodes={}}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.15, r=0.2, colour = G.C.L_BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.2, r=0.2, colour = G.C.BLACK, maxh = G.shop_vouchers.T.h+0.4}, nodes={ + {n=G.UIT.T, config={text = localize{type = 'variable', key = 'ante_x_voucher', vars = {G.GAME.round_resets.ante}}, scale = 0.45, colour = G.C.L_BLACK, vert = true}}, + {n=G.UIT.O, config={object = G.shop_vouchers}}, + }}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.15, r=0.2, colour = G.C.L_BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.O, config={object = G.shop_booster}}, + }}, + }} + } + }, + + }, false) + }} + return t +end + + function create_card_for_shop(area) + if area == G.shop_jokers and G.SETTINGS.tutorial_progress and G.SETTINGS.tutorial_progress.forced_shop and G.SETTINGS.tutorial_progress.forced_shop[#G.SETTINGS.tutorial_progress.forced_shop] then + local t = G.SETTINGS.tutorial_progress.forced_shop + local _center = G.P_CENTERS[t[#t]] or G.P_CENTERS.c_empress + local card = Card(area.T.x + area.T.w/2, area.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, _center, {bypass_discovery_center = true, bypass_discovery_ui = true}) + t[#t] = nil + if not t[1] then G.SETTINGS.tutorial_progress.forced_shop = nil end + + create_shop_card_ui(card) + return card + else + local forced_tag = nil + for k, v in ipairs(G.GAME.tags) do + if not forced_tag then + forced_tag = v:apply_to_run({type = 'store_joker_create', area = area}) + if forced_tag then + for kk, vv in ipairs(G.GAME.tags) do + if vv:apply_to_run({type = 'store_joker_modify', card = forced_tag}) then break end + end + return forced_tag end + end + end + G.GAME.spectral_rate = G.GAME.spectral_rate or 0 + local total_rate = G.GAME.joker_rate + G.GAME.playing_card_rate + for _,v in ipairs(SMODS.ConsumableType.ctype_buffer) do + total_rate = total_rate + G.GAME[v:lower()..'_rate'] + end + local polled_rate = pseudorandom(pseudoseed('cdt'..G.GAME.round_resets.ante))*total_rate + local check_rate = 0 + -- need to preserve order to leave RNG unchanged + local rates = { + {type = 'Joker', val = G.GAME.joker_rate}, + {type = 'Tarot', val = G.GAME.tarot_rate}, + {type = 'Planet', val = G.GAME.planet_rate}, + {type = (G.GAME.used_vouchers["v_illusion"] and pseudorandom(pseudoseed('illusion')) > 0.6) and 'Enhanced' or 'Base', val = G.GAME.playing_card_rate}, + {type = 'Spectral', val = G.GAME.spectral_rate}, + } + for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do + if not (v == 'Tarot' or v == 'Planet' or v == 'Spectral') then + table.insert(rates, { type = v, val = G.GAME[v:lower()..'_rate'] }) + end + end + for _, v in ipairs(rates) do + if polled_rate > check_rate and polled_rate <= check_rate + v.val then + local card = create_card(v.type, area, nil, nil, nil, nil, nil, 'sho') + create_shop_card_ui(card, v.type, area) + G.E_MANAGER:add_event(Event({ + func = (function() + for k, v in ipairs(G.GAME.tags) do + if v:apply_to_run({type = 'store_joker_modify', card = card}) then break end + end + return true + end) + })) + if (v.type == 'Base' or v.type == 'Enhanced') and G.GAME.used_vouchers["v_illusion"] and pseudorandom(pseudoseed('illusion')) > 0.8 then + local edition_poll = pseudorandom(pseudoseed('illusion')) + local edition = {} + if edition_poll > 1 - 0.15 then edition.polychrome = true + elseif edition_poll > 0.5 then edition.holo = true + else edition.foil = true + end + card:set_edition(edition) + end + return card + end + check_rate = check_rate + v.val + end + end + end + + function create_shop_card_ui(card, type, area) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.43, + blocking = false, + blockable = false, + func = (function() + if card.opening then return true end + local t1 = { + n=G.UIT.ROOT, config = {minw = 0.6, align = 'tm', colour = darken(G.C.BLACK, 0.2), shadow = true, r = 0.05, padding = 0.05, minh = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", colour = lighten(G.C.BLACK, 0.1), r = 0.1, minw = 1, minh = 0.55, emboss = 0.05, padding = 0.03}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{prefix = localize('$'), ref_table = card, ref_value = 'cost'}}, colours = {G.C.MONEY},shadow = true, silent = true, bump = true, pop_in = 0, scale = 0.5})}}, + }} + }} + local t2 = card.ability.set == 'Voucher' and { + n=G.UIT.ROOT, config = {ref_table = card, minw = 1.1, maxw = 1.3, padding = 0.1, align = 'bm', colour = G.C.GREEN, shadow = true, r = 0.08, minh = 0.94, func = 'can_redeem', one_press = true, button = 'redeem_from_shop', hover = true}, nodes={ + {n=G.UIT.T, config={text = localize('b_redeem'),colour = G.C.WHITE, scale = 0.4}} + }} or card.ability.set == 'Booster' and { + n=G.UIT.ROOT, config = {ref_table = card, minw = 1.1, maxw = 1.3, padding = 0.1, align = 'bm', colour = G.C.GREEN, shadow = true, r = 0.08, minh = 0.94, func = 'can_open', one_press = true, button = 'open_booster', hover = true}, nodes={ + {n=G.UIT.T, config={text = localize('b_open'),colour = G.C.WHITE, scale = 0.5}} + }} or { + n=G.UIT.ROOT, config = {ref_table = card, minw = 1.1, maxw = 1.3, padding = 0.1, align = 'bm', colour = G.C.GOLD, shadow = true, r = 0.08, minh = 0.94, func = 'can_buy', one_press = true, button = 'buy_from_shop', hover = true}, nodes={ + {n=G.UIT.T, config={text = localize('b_buy'),colour = G.C.WHITE, scale = 0.5}} + }} + local t3 = { + n=G.UIT.ROOT, config = {id = 'buy_and_use', ref_table = card, minh = 1.1, padding = 0.1, align = 'cr', colour = G.C.RED, shadow = true, r = 0.08, minw = 1.1, func = 'can_buy_and_use', one_press = true, button = 'buy_from_shop', hover = true, focus_args = {type = 'none'}}, nodes={ + {n=G.UIT.B, config = {w=0.1,h=0.6}}, + {n=G.UIT.C, config = {align = 'cm'}, nodes={ + {n=G.UIT.R, config = {align = 'cm', maxw = 1}, nodes={ + {n=G.UIT.T, config={text = localize('b_buy'),colour = G.C.WHITE, scale = 0.5}} + }}, + {n=G.UIT.R, config = {align = 'cm', maxw = 1}, nodes={ + {n=G.UIT.T, config={text = localize('b_and_use'),colour = G.C.WHITE, scale = 0.3}} + }}, + }} + }} + + + card.children.price = UIBox{ + definition = t1, + config = { + align="tm", + offset = {x=0,y=1.5}, + major = card, + bond = 'Weak', + parent = card + } + } + + card.children.buy_button = UIBox{ + definition = t2, + config = { + align="bm", + offset = {x=0,y=-0.3}, + major = card, + bond = 'Weak', + parent = card + } + } + + if card.ability.consumeable then --and card:can_use_consumeable(true, true) + card.children.buy_and_use_button = UIBox{ + definition = t3, + config = { + align="cr", + offset = {x=-0.3,y=0}, + major = card, + bond = 'Weak', + parent = card + } + } + end + + card.children.price.alignment.offset.y = card.ability.set == 'Booster' and 0.5 or 0.38 + + return true + end) + })) + end + + + function attention_text(args) + args = args or {} + args.text = args.text or 'test' + args.scale = args.scale or 1 + args.colour = copy_table(args.colour or G.C.WHITE) + args.hold = (args.hold or 0) + 0.1*(G.SPEEDFACTOR) + args.pos = args.pos or {x = 0, y = 0} + args.align = args.align or 'cm' + args.emboss = args.emboss or nil + + args.fade = 1 + + if args.cover then + args.cover_colour = copy_table(args.cover_colour or G.C.RED) + args.cover_colour_l = copy_table(lighten(args.cover_colour, 0.2)) + args.cover_colour_d = copy_table(darken(args.cover_colour, 0.2)) + else + args.cover_colour = copy_table(G.C.CLEAR) + end + + args.uibox_config = { + align = args.align or 'cm', + offset = args.offset or {x=0,y=0}, + major = args.cover or args.major or nil, + } + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0, + blockable = false, + blocking = false, + func = function() + args.AT = UIBox{ + T = {args.pos.x,args.pos.y,0,0}, + definition = + {n=G.UIT.ROOT, config = {align = args.cover_align or 'cm', minw = (args.cover and args.cover.T.w or 0.001) + (args.cover_padding or 0), minh = (args.cover and args.cover.T.h or 0.001) + (args.cover_padding or 0), padding = 0.03, r = 0.1, emboss = args.emboss, colour = args.cover_colour}, nodes={ + {n=G.UIT.O, config={draw_layer = 1, object = DynaText({scale = args.scale, string = args.text, maxw = args.maxw, colours = {args.colour},float = true, shadow = true, silent = not args.noisy, args.scale, pop_in = 0, pop_in_rate = 6, rotate = args.rotate or nil})}}, + }}, + config = args.uibox_config + } + args.AT.attention_text = true + + args.text = args.AT.UIRoot.children[1].config.object + args.text:pulse(0.5) + + if args.cover then + Particles(args.pos.x,args.pos.y, 0,0, { + timer_type = 'TOTAL', + timer = 0.01, + pulse_max = 15, + max = 0, + scale = 0.3, + vel_variation = 0.2, + padding = 0.1, + fill=true, + lifespan = 0.5, + speed = 2.5, + attach = args.AT.UIRoot, + colours = {args.cover_colour, args.cover_colour_l, args.cover_colour_d}, + }) + end + if args.backdrop_colour then + args.backdrop_colour = copy_table(args.backdrop_colour) + Particles(args.pos.x,args.pos.y,0,0,{ + timer_type = 'TOTAL', + timer = 5, + scale = 2.4*(args.backdrop_scale or 1), + lifespan = 5, + speed = 0, + attach = args.AT, + colours = {args.backdrop_colour} + }) + end + return true + end + })) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = args.hold, + blockable = false, + blocking = false, + func = function() + if not args.start_time then + args.start_time = G.TIMERS.TOTAL + args.text:pop_out(3) + else + --args.AT:align_to_attach() + args.fade = math.max(0, 1 - 3*(G.TIMERS.TOTAL - args.start_time)) + if args.cover_colour then args.cover_colour[4] = math.min(args.cover_colour[4], 2*args.fade) end + if args.cover_colour_l then args.cover_colour_l[4] = math.min(args.cover_colour_l[4], args.fade) end + if args.cover_colour_d then args.cover_colour_d[4] = math.min(args.cover_colour_d[4], args.fade) end + if args.backdrop_colour then args.backdrop_colour[4] = math.min(args.backdrop_colour[4], args.fade) end + args.colour[4] = math.min(args.colour[4], args.fade) + if args.fade <= 0 then + args.AT:remove() + return true + end + end + end + })) + end + + function create_UIBox_buttons() + if G.hand and G.hand.cart_sorting == nil then G.hand.cart_sorting = true end + local text_scale = 0.45 + local button_height = 1.3 + local play_button = {n=G.UIT.C, config={id = 'play_button', align = "tm", minw = 2.5, padding = 0.3, r = 0.1, hover = true, colour = G.C.BLUE, button = "play_cards_from_highlighted", one_press = true, shadow = true, func = 'can_play'}, nodes={ + {n=G.UIT.R, config={align = "bcm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize('b_play_hand'), scale = text_scale, colour = G.C.UI.TEXT_LIGHT, focus_args = {button = 'x', orientation = 'bm'}, func = 'set_button_pip'}} + }}, + }} + + local discard_button = {n=G.UIT.C, config={id = 'discard_button',align = "tm", padding = 0.3, r = 0.1, minw = 2.5, minh = button_height, hover = true, colour = G.C.RED, button = "discard_cards_from_highlighted", one_press = true, shadow = true, func = 'can_discard'}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize('b_discard'), scale = text_scale, colour = G.C.UI.TEXT_LIGHT, focus_args = {button = 'y', orientation = 'bm'}, func = 'set_button_pip'}} + }} + }} + + local t = { + n=G.UIT.ROOT, config = {align = "cm", minw = 1, minh = 0.3,padding = 0.15, r = 0.1, colour = G.C.CLEAR}, nodes={ + G.SETTINGS.play_button_pos == 1 and discard_button or play_button, + + {n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, colour =G.C.UI.TRANSPARENT_DARK, outline = 1.5, outline_colour = mix_colours(G.C.WHITE,G.C.JOKER_GREY, 0.7), line_emboss = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + Cartomancer.SETTINGS.improved_hand_sorting and + create_toggle{ col = true, label = localize('b_sort_hand'), label_scale = text_scale*0.8, scale = 0.30, w = 0, shadow = true, ref_table = G.hand, ref_value = 'cart_sorting', callback = function () G.FUNCS.cartomancer_sort_hand_off() end } + or + {n=G.UIT.T, config={text = localize('b_sort_hand'), scale = text_scale*0.8, colour = G.C.UI.TEXT_LIGHT}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", minh = 0.7, minw = 0.9, padding = 0.1, r = 0.1, hover = true, colour =G.C.ORANGE, button = "sort_hand_value", shadow = true}, nodes={ + {n=G.UIT.T, config={text = localize('k_rank'), scale = text_scale*0.7, colour = G.C.UI.TEXT_LIGHT}} + }}, + {n=G.UIT.C, config={align = "cm", minh = 0.7, minw = 0.9, padding = 0.1, r = 0.1, hover = true, colour =G.C.ORANGE, button = "sort_hand_suit", shadow = true}, nodes={ + {n=G.UIT.T, config={text = localize('k_suit'), scale = text_scale*0.7, colour = G.C.UI.TEXT_LIGHT}} + }} + }} + }} + }}, + + G.SETTINGS.play_button_pos == 1 and play_button or discard_button, + } + } + return t + end + + function desc_from_rows(desc_nodes, empty, maxw) + local t = {} + for k, v in ipairs(desc_nodes) do + t[#t+1] = {n=G.UIT.R, config={align = "cm", maxw = maxw}, nodes=v} + end + return {n=G.UIT.R, config={align = "cm", colour = desc_nodes.background_colour or empty and G.C.CLEAR or G.C.UI.BACKGROUND_WHITE, r = 0.1, padding = 0.04, minw = 2, minh = 0.8, emboss = not empty and 0.05 or nil, filler = true}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.03}, nodes=t} + }} + end + + function transparent_multiline_text(desc_nodes) + local t = {} + for k, v in ipairs(desc_nodes) do + t[#t+1] = {n=G.UIT.R, config={align = "cm", maxw = maxw}, nodes=v} + end + return {n=G.UIT.R, config={align = "cm", padding = 0.03}, nodes=t} + end + + function info_tip_from_rows(desc_nodes, name) + local t = {} + for k, v in ipairs(desc_nodes) do + t[#t+1] = {n=G.UIT.R, config={align = "cm"}, nodes=v} + end + return {n=G.UIT.R, config={align = "cm", colour = lighten(G.C.GREY, 0.15), r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "tm", minh = 0.36, padding = 0.03}, nodes={{n=G.UIT.T, config={text = name, scale = 0.32, colour = G.C.UI.TEXT_LIGHT}}}}, + {n=G.UIT.R, config={align = "cm", minw = 1.5, minh = 0.4, r = 0.1, padding = 0.05, colour = desc_nodes.background_colour or G.C.WHITE}, nodes={{n=G.UIT.R, config={align = "cm", padding = 0.03}, nodes=t}}} + }} + end + + function overlay_infotip(text_rows) + local t = {} + if type(text_rows) ~= 'table' then text_rows = {"ERROR"} end + for k, v in ipairs(text_rows) do + t[#t+1] = {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = v,colour = G.C.UI.TEXT_LIGHT, scale = 0.45, juice = true, shadow = true, lang = text_rows.lang}} + }} + end + return {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR, padding = 0.1}, nodes=t} + end + + function name_from_rows(name_nodes, background_colour) + if not name_nodes or (type(name_nodes) ~= 'table') or not next(name_nodes) then return end + return {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = background_colour, emboss = background_colour and 0.05 or nil}, nodes=name_nodes} + end + + function G.UIDEF.card_h_popup(card) + if card.ability_UIBox_table then + local AUT = card.ability_UIBox_table + local debuffed = card.debuff + local card_type_colour = get_type_colour(card.config.center or card.config, card) + local card_type_background = + (AUT.card_type == 'Locked' and G.C.BLACK) or + ((AUT.card_type == 'Undiscovered') and darken(G.C.JOKER_GREY, 0.3)) or + (AUT.card_type == 'Enhanced' or AUT.card_type == 'Default') and darken(G.C.BLACK, 0.1) or + (debuffed and darken(G.C.BLACK, 0.1)) or + (card_type_colour and darken(G.C.BLACK, 0.1)) or + G.C.SET[AUT.card_type] or + {0, 1, 1, 1} + + local outer_padding = 0.05 + local card_type = localize('k_'..string.lower(AUT.card_type)) + + if AUT.card_type == 'Joker' or (AUT.badges and AUT.badges.force_rarity) then card_type = SMODS.Rarity:get_rarity_badge(card.config.center.rarity) end + if AUT.card_type == 'Enhanced' then card_type = localize{type = 'name_text', key = card.config.center.key, set = 'Enhanced'} end + card_type = (debuffed and AUT.card_type ~= 'Enhanced') and localize('k_debuffed') or card_type + + local disp_type, is_playing_card = + (AUT.card_type ~= 'Locked' and AUT.card_type ~= 'Undiscovered' and AUT.card_type ~= 'Default') or debuffed, + AUT.card_type == 'Enhanced' or AUT.card_type == 'Default' + + local info_boxes = {} + local badges = {} + + local obj = card.config.center + if AUT.badges.card_type or AUT.badges.force_rarity then + if obj and (obj.set_card_type_badge or obj.type and obj.type.set_card_type_badge) then + if obj.type and type(obj.type.set_card_type_badge) == 'function' then + obj.type:set_card_type_badge(obj, card, badges) + end + if type(obj.set_card_type_badge) == 'function' then + obj:set_card_type_badge(card, badges) + end + else + badges[#badges + 1] = create_badge(((card.ability.name == 'Pluto' or card.ability.name == 'Ceres' or card.ability.name == 'Eris') and localize('k_dwarf_planet')) or (card.ability.name == 'Planet X' and localize('k_planet_q') or card_type),card_type_colour, nil, 1.2) + end + end + if obj and obj.set_badges and type(obj.set_badges) == 'function' then + obj:set_badges(card, badges) + end + local function is_bad_badge(string) + local bad_badges = {'cry_pinned_booster', 'cry_pinned_voucher', 'cry_pinned_consumeable'} + for i = 1, #bad_badges do + if string == bad_badges[i] then return true end + end + return false + end + if AUT.badges then + for k, v in ipairs(AUT.badges) do + if v == 'negative_consumable' or v == 'negative_playing_card' then v = 'negative' end + if not is_bad_badge(v) then badges[#badges + 1] = create_badge(localize(v, "labels"), get_badge_colour(v)) end + end + end if AUT.card_type ~= 'Locked' and AUT.card_type ~= 'Undiscovered' then + SMODS.create_mod_badges(card.config.center, badges) + if card.base then + SMODS.create_mod_badges(SMODS.Ranks[card.base.value], badges) + SMODS.create_mod_badges(SMODS.Suits[card.base.suit], badges) + end + if card.config and card.config.tag then + SMODS.create_mod_badges(SMODS.Tags[card.config.tag.key], badges) + end + badges.mod_set = nil + end + + -- if AUT.badges then + -- for k, v in ipairs(AUT.badges) do + -- local replaced = false + -- if v == 'cry_pinned_booster' or v == 'cry_pinned_voucher' or v == 'cry_pinned_consumeable' then replaced = true; v = 'pinned_left' end + -- if replaced == true then badges[#badges + 1] = create_badge(localize(v, "labels"), get_badge_colour(v)) end + -- end + --end + if AUT.info then + for k, v in ipairs(AUT.info) do + info_boxes[#info_boxes+1] = + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", colour = lighten(G.C.JOKER_GREY, 0.5), r = 0.1, padding = 0.05, emboss = 0.05}, nodes={ + info_tip_from_rows(v, v.name), + }} + }} + end + end + + local cols + if #info_boxes <= 3 then + cols = 1 + elseif #info_boxes <= 10 then + cols = 2 + elseif #info_boxes <= 24 then + cols = 3 + else + cols = 4 + end + local nodes_per_col = math.ceil(#info_boxes/cols) + local info_cols = {} + for i = 0, cols-1 do + local col = {} + for j = 1, nodes_per_col do + local info_box = info_boxes[i*nodes_per_col+j] + if info_box then + table.insert(col, info_box) + else break end + end + table.insert(info_cols, {n=G.UIT.C, config = {align="cm"}, nodes = col}) + end + info_boxes = {{n=G.UIT.R, config = {align="cm", padding = 0.05, card_pos = card.T.x }, nodes = info_cols}} + return {n=G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR}, nodes={ + {n=G.UIT.C, config={align = "cm", func = 'show_infotip',object = Moveable(),ref_table = next(info_boxes) and info_boxes or nil}, nodes={ + {n=G.UIT.R, config={padding = outer_padding, r = 0.12, colour = lighten(G.C.JOKER_GREY, 0.5), emboss = 0.07}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.07, r = 0.1, colour = adjust_alpha(card_type_background, 0.8)}, nodes={ + name_from_rows(AUT.name, is_playing_card and G.C.WHITE or nil), + desc_from_rows(AUT.main), + badges[1] and {n=G.UIT.R, config={align = "cm", padding = 0.03}, nodes=badges} or nil, + }} + }} + }}, + }} + end + end + + function get_badge_colour(key) + G.BADGE_COL = G.BADGE_COL or { + eternal = G.C.ETERNAL, + perishable = G.C.PERISHABLE, + rental = G.C.RENTAL, + foil = G.C.DARK_EDITION, + holographic = G.C.DARK_EDITION, + polychrome = G.C.DARK_EDITION, + negative = G.C.DARK_EDITION, + gold_seal = G.C.GOLD, + red_seal = G.C.RED, + blue_seal = G.C.BLUE, + purple_seal = G.C.PURPLE, + pinned_left = G.C.ORANGE, + } + for _, v in ipairs(G.P_CENTER_POOLS.Edition) do + G.BADGE_COL[v.key:sub(3)] = v.badge_colour + end + for k, v in pairs(SMODS.Rarity.obj_buffer) do + G.BADGE_COL[k] = G.C.RARITY[v] + end + for k, v in pairs(SMODS.Seals) do + G.BADGE_COL[k:lower()..'_seal'] = v.badge_colour + end + for k, v in pairs(SMODS.Stickers) do + G.BADGE_COL[k] = v.badge_colour + end + return G.BADGE_COL[key] or {1, 0, 0, 1} + end + + function create_badge(_string, _badge_col, _text_col, scaling) + scaling = scaling or 1 + return {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", colour = _badge_col or G.C.GREEN, r = 0.1, minw = 2, minh = 0.4*scaling, emboss = 0.05, padding = 0.03*scaling}, nodes={ + {n=G.UIT.B, config={h=0.1,w=0.03}}, + {n=G.UIT.O, config={object = DynaText({string = _string or 'ERROR', colours = {_text_col or G.C.WHITE},float = true, shadow = true, offset_y = -0.05, silent = true, spacing = 1, scale = 0.33*scaling})}}, + {n=G.UIT.B, config={h=0.1,w=0.03}}, + }} + }} + end + + function create_UIBox_detailed_tooltip(_center) + local full_UI_table = { + main = {}, + info = {}, + type = {}, + name = 'done', + badges = badges or {} + } + local desc = generate_card_ui(_center, full_UI_table, nil, _center.set, nil) + return {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", colour = lighten(G.C.JOKER_GREY, 0.5), r = 0.1, padding = 0.05, emboss = 0.05}, nodes={ + info_tip_from_rows(desc.info[1], desc.info[1].name), + }} + }} + end + + function create_popup_UIBox_tooltip(tooltip) + local title = tooltip.title or nil + local text = tooltip.text or {} + local rows = {} + if title then + local r = {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = title,colour = G.C.UI.TEXT_DARK, scale = 0.4}}}}}} + table.insert(rows, r) + end + for i = 1, #text do + if type(text[i]) == 'table' then + local r = {n=G.UIT.R, config={align = "cm", padding = 0.03}, nodes={ + {n=G.UIT.T, config={ref_table = text[i].ref_table, ref_value = text[i].ref_value,colour = G.C.UI.TEXT_DARK, scale = 0.4}}}} + table.insert(rows, r) + else + local r = {n=G.UIT.R, config={align = "cm", padding = 0.03}, nodes={ + {n=G.UIT.T, config={text = text[i],colour = G.C.UI.TEXT_DARK, scale = 0.4}}}} + table.insert(rows, r) + end + end + if tooltip.filler then + table.insert(rows, tooltip.filler.func(tooltip.filler.args)) + end + local t = { + n=G.UIT.ROOT, config = {align = "cm", padding = 0.05, r=0.1, colour = G.C.RED, emboss = 0.05}, nodes= + {{n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, colour = G.C.WHITE, emboss = 0.05}, nodes=rows}}} + return t + end + +function create_UIBox_HUD_blind() + local scale = 0.4 + local stake_sprite = get_stake_sprite(G.GAME.stake or 1, 0.5) + G.GAME.blind:change_dim(1.5,1.5) + + return {n=G.UIT.ROOT, config={align = "cm", minw = 4.5, r = 0.1, colour = G.C.BLACK, emboss = 0.05, padding = 0.05, func = 'HUD_blind_visible', id = 'HUD_blind'}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.7, r = 0.1, emboss = 0.05, colour = G.C.DYN_UI.MAIN}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = 3}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME.blind, ref_value = 'loc_name'}}, colours = {G.C.UI.TEXT_LIGHT},shadow = true, rotate = true, silent = true, float = true, scale = 1.6*scale, y_offset = -4}),id = 'HUD_blind_name'}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 2.74, r = 0.1,colour = G.C.DYN_UI.DARK}, nodes={ + {n=G.UIT.R, config={align = "cm", id = 'HUD_blind_debuff', func = 'HUD_blind_debuff'}, nodes={}}, + {n=G.UIT.R, config={align = "cm",padding = 0.15}, nodes={ + {n=G.UIT.O, config={object = G.GAME.blind, draw_layer = 1}}, + {n=G.UIT.C, config={align = "cm",r = 0.1, padding = 0.05, emboss = 0.05, minw = 2.9, colour = G.C.BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 2.8}, nodes={ + {n=G.UIT.T, config={text = localize('ph_blind_score_at_least'), scale = 0.3, colour = G.C.WHITE, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.6}, nodes={ + {n=G.UIT.O, config={w=0.5,h=0.5, colour = G.C.BLUE, object = stake_sprite, hover = true, can_collide = false}}, + {n=G.UIT.B, config={h=0.1,w=0.1}}, + {n=G.UIT.T, config={ref_table = G.GAME.blind, ref_value = 'chip_text', scale = 0.001, colour = G.C.RED, shadow = true, id = 'HUD_blind_count', func = 'blind_chip_UI_scale'}} + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.45, maxw = 2.8, func = 'HUD_blind_reward'}, nodes={ + {n=G.UIT.T, config={text = localize('ph_blind_reward'), scale = 0.3, colour = G.C.WHITE}}, + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME.current_round, ref_value = 'dollars_to_be_earned'}}, colours = {G.C.MONEY},shadow = true, rotate = true, bump = true, silent = true, scale = 0.45}),id = 'dollars_to_be_earned'}}, + }}, + }}, + }}, + }}, + }} +end + +function add_tag(_tag) + G.HUD_tags = G.HUD_tags or {} + local tag_sprite_ui = _tag:generate_UI() + G.HUD_tags[#G.HUD_tags+1] = UIBox{ + definition = {n=G.UIT.ROOT, config={align = "cm",padding = 0.05, colour = G.C.CLEAR}, nodes={ + tag_sprite_ui + }}, + config = { + align = G.HUD_tags[1] and 'tm' or 'bri', + offset = G.HUD_tags[1] and {x=0,y=0} or {x=0.7,y=0}, + major = G.HUD_tags[1] and G.HUD_tags[#G.HUD_tags] or G.ROOM_ATTACH} + } + discover_card(G.P_TAGS[_tag.key]) + + for i = 1, #G.GAME.tags do + G.GAME.tags[i]:apply_to_run({type = 'tag_add', tag = _tag}) + end + + G.GAME.tags[#G.GAME.tags+1] = _tag + _tag.HUD_tag = G.HUD_tags[#G.HUD_tags] +end + +function create_UIBox_HUD() + local scale = 0.4 + local stake_sprite = get_stake_sprite(G.GAME.stake or 1, 0.5) + + local contents = {} + + local spacing = 0.13 + local temp_col = G.C.DYN_UI.BOSS_MAIN + local temp_col2 = G.C.DYN_UI.BOSS_DARK + contents.round = { + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={id = 'hud_hands',align = "cm", padding = 0.05, minw = 1.45, colour = temp_col, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.33, maxw = 1.35}, nodes={ + {n=G.UIT.T, config={text = localize('k_hud_hands'), scale = 0.85*scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", r = 0.1, minw = 1.2, colour = temp_col2}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME.current_round, ref_value = 'hands_left'}}, font = G.LANGUAGES['en-us'].font, colours = {G.C.BLUE},shadow = true, rotate = true, scale = 2*scale}),id = 'hand_UI_count'}}, + }} + }}, + {n=G.UIT.C, config={minw = spacing},nodes={}}, + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 1.45, colour = temp_col, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.33, maxw = 1.35}, nodes={ + {n=G.UIT.T, config={text = localize('k_hud_discards'), scale = 0.85*scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1, minw = 1.2, colour = temp_col2}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME.current_round, ref_value = 'discards_left'}}, font = G.LANGUAGES['en-us'].font, colours = {G.C.RED},shadow = true, rotate = true, scale = 2*scale}),id = 'discard_UI_count'}}, + }} + }}, + }}, + }}, + {n=G.UIT.R, config={minh = spacing},nodes={}}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 1.45*2 + spacing, minh = 1.15, colour = temp_col, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", r = 0.1, minw = 1.28*2+spacing, minh = 1, colour = temp_col2}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME, ref_value = 'dollars', prefix = localize('$')}}, + scale_function = function () + return scale_number(G.GAME.dollars, 2.2 * scale, 99999, 1000000) + end, maxw = 1.35, colours = {G.C.MONEY}, font = G.LANGUAGES['en-us'].font, shadow = true,spacing = 2, bump = true, scale = 2.2*scale}), id = 'dollar_text_UI'}} + }}, + }}, + }}, + }}, + {n=G.UIT.R, config={minh = spacing},nodes={}}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={id = 'hud_ante',align = "cm", padding = 0.05, minw = 1.45, minh = 1, colour = temp_col, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.33, maxw = 1.35}, nodes={ + {n=G.UIT.T, config={text = localize('k_ante'), scale = 0.85*scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", r = 0.1, minw = 1.2, colour = temp_col2}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME.round_resets, ref_value = 'ante_disp'}}, colours = {G.C.IMPORTANT},shadow = true, font = G.LANGUAGES['en-us'].font, scale = scale_number(G.GAME.round_resets.ante, 2*scale, 100)}),id = 'ante_UI_count'}},--{n=G.UIT.T, config={text = number_format(G.GAME.round_resets.ante), lang = G.LANGUAGES['en-us'], scale = scale_number(G.GAME.round_resets.ante, 2*scale, 100), colour = G.C.IMPORTANT, shadow = true,id = 'ante_UI_count'}}, + {n=G.UIT.T, config={text = " ", scale = 0.3*scale}}, + {n=G.UIT.T, config={text = "/ ", scale = 0.7*scale, colour = G.C.WHITE, shadow = true}}, + {n=G.UIT.T, config={ref_table = G.GAME, ref_value='win_ante', scale = scale, colour = G.C.WHITE, shadow = true}} + }}, + }}, + {n=G.UIT.C, config={minw = spacing},nodes={}}, + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 1.45, minh = 1, colour = temp_col, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 1.35}, nodes={ + {n=G.UIT.T, config={text = localize('k_round'), minh = 0.33, scale = 0.85*scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", r = 0.1, minw = 1.2, colour = temp_col2, id = 'row_round_text'}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME, ref_value = 'round'}}, colours = {G.C.IMPORTANT},shadow = true, scale = 2*scale}),id = 'round_UI_count'}}, + }}, + }}, + }}, + } + + contents.hand = + {n=G.UIT.R, config={align = "cm", id = 'hand_text_area', colour = darken(G.C.BLACK, 0.1), r = 0.1, emboss = 0.05, padding = 0.03}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 1.1}, nodes={ + {n=G.UIT.O, config={id = 'hand_name', func = 'hand_text_UI_set',object = DynaText({string = {{ref_table = G.GAME.current_round.current_hand, ref_value = "handname_text"}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, float = true, scale = scale*1.4})}}, + {n=G.UIT.O, config={id = 'cry_asc', func = 'cry_asc_UI_set',object = DynaText({string = {{ref_table = G.GAME.current_round.current_hand, ref_value = "cry_asc_num_text"}}, colours = {G.C.GOLD}, shadow = true, float = true, scale = scale*1})}}, + {n=G.UIT.O, config={id = 'hand_chip_total', func = 'hand_chip_total_UI_set',object = DynaText({string = {{ref_table = G.GAME.current_round.current_hand, ref_value = "chip_total_text"}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, float = true, scale = scale*1.4})}}, + {n=G.UIT.T, config={ref_table = G.GAME.current_round.current_hand, ref_value='hand_level', scale = scale, colour = G.C.UI.TEXT_LIGHT, id = 'hand_level', shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", minh = 1, padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cr", minw = 2, minh =1, r = 0.1,colour = G.C.UI_CHIPS, id = 'hand_chip_area', emboss = 0.05}, nodes={ + {n=G.UIT.O, config={func = 'flame_handler',no_role = true, id = 'flame_chips', object = Moveable(0,0,0,0), w = 0, h = 0}}, + {n=G.UIT.O, config={id = 'hand_chips', func = 'hand_chip_UI_set',object = DynaText({string = {{ref_table = G.GAME.current_round.current_hand, ref_value = "chip_text"}}, colours = {G.C.UI.TEXT_LIGHT}, font = G.LANGUAGES['en-us'].font, shadow = true, float = true, scale = scale*2.3})}}, + {n=G.UIT.B, config={w=0.1,h=0.1}}, + }}, + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = "X", lang = G.LANGUAGES['en-us'], scale = scale*2, colour = G.C.UI_MULT, shadow = true}}, + }}, + {n=G.UIT.C, config={align = "cl", minw = 2, minh=1, r = 0.1,colour = G.C.UI_MULT, id = 'hand_mult_area', emboss = 0.05}, nodes={ + {n=G.UIT.O, config={func = 'flame_handler',no_role = true, id = 'flame_mult', object = Moveable(0,0,0,0), w = 0, h = 0}}, + {n=G.UIT.B, config={w=0.1,h=0.1}}, + {n=G.UIT.O, config={id = 'hand_mult', func = 'hand_mult_UI_set',object = DynaText({string = {{ref_table = G.GAME.current_round.current_hand, ref_value = "mult_text"}}, colours = {G.C.UI.TEXT_LIGHT}, font = G.LANGUAGES['en-us'].font, shadow = true, float = true, scale = scale*2.3})}}, + }} + }} + }} + }} + contents.dollars_chips = {n=G.UIT.R, config={align = "cm",r=0.1, padding = 0,colour = G.C.DYN_UI.BOSS_MAIN, emboss = 0.05, id = 'row_dollars_chips'}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = 1.3}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0, maxw = 1.3}, nodes={ + {n=G.UIT.T, config={text = localize('k_round'), scale = 0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0, maxw = 1.3}, nodes={ + {n=G.UIT.T, config={text =localize('k_lower_score'), scale = 0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }} + }}, + {n=G.UIT.C, config={align = "cm", minw = 3.3, minh = 0.7, r = 0.1, colour = G.C.DYN_UI.BOSS_DARK}, nodes={ + {n=G.UIT.O, config={w=0.5,h=0.5 , object = stake_sprite, hover = true, can_collide = false}}, + {n=G.UIT.B, config={w=0.1,h=0.1}}, + {n=G.UIT.T, config={ref_table = G.GAME, ref_value = 'chips_text', lang = G.LANGUAGES['en-us'], scale = 0.85, colour = G.C.WHITE, id = 'chip_UI_count', func = 'chip_UI_set', shadow = true}} + }} + }} + }} + + contents.buttons = { + {n=G.UIT.C, config={align = "cm", r=0.1, colour = G.C.CLEAR, shadow = true, id = 'button_area', padding = 0.2}, nodes={ + {n=G.UIT.R, config={id = 'run_info_button', align = "cm", minh = 1.75, minw = 1.5,padding = 0.05, r = 0.1, hover = true, colour = G.C.RED, button = "run_info", shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0, maxw = 1.4}, nodes={ + {n=G.UIT.T, config={text = localize('b_run_info_1'), scale = 1.2*scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0, maxw = 1.4}, nodes={ + {n=G.UIT.T, config={text = localize('b_run_info_2'), scale = 1*scale, colour = G.C.UI.TEXT_LIGHT, shadow = true, focus_args = {button = G.F_GUIDE and 'guide' or 'back', orientation = 'bm'}, func = 'set_button_pip'}} + }} + }}, + {n=G.UIT.R, config={align = "cm", minh = 1.75, minw = 1.5,padding = 0.05, r = 0.1, hover = true, colour = G.C.ORANGE, button = "options", shadow = true}, nodes={ + {n=G.UIT.C, config={align = "cm", maxw = 1.4, focus_args = {button = 'start', orientation = 'bm'}, func = 'set_button_pip'}, nodes={ + {n=G.UIT.T, config={text = localize('b_options'), scale = scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }}, + }} + }} + } + + return {n=G.UIT.ROOT, config = {align = "cm", padding = 0.03, colour = G.C.UI.TRANSPARENT_DARK}, nodes={ + {n=G.UIT.R, config = {align = "cm", padding= 0.05, colour = G.C.DYN_UI.MAIN, r=0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", colour = G.C.DYN_UI.BOSS_DARK, r=0.1, minh = 30, padding = 0.08}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.3}, nodes={}}, + {n=G.UIT.R, config={align = "cm", id = 'row_blind', minw = 1, minh = 3.75}, nodes={ + {n=G.UIT.B, config={w=0, h=3.64, id = 'row_blind_bottom'}, nodes={}} + }}, + contents.dollars_chips, + contents.hand, + {n=G.UIT.R, config={align = "cm", id = 'row_round'}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes=contents.buttons}, + {n=G.UIT.C, config={align = "cm"}, nodes=contents.round} + }}, + }} + }} + }} +end + +function create_UIBox_blind_select() + G.blind_prompt_box = UIBox{ + definition = + {n=G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR, padding = 0.2}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize('ph_choose_blind_1'), colours = {G.C.WHITE}, shadow = true, bump = true, scale = 0.6, pop_in = 0.5, maxw = 5}), id = 'prompt_dynatext1'}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize('ph_choose_blind_2'), colours = {G.C.WHITE}, shadow = true, bump = true, scale = 0.7, pop_in = 0.5, maxw = 5, silent = true}), id = 'prompt_dynatext2'}} + }}, + (G.GAME.used_vouchers["v_retcon"] or G.GAME.used_vouchers["v_directors_cut"]) and + UIBox_button({label = {localize('b_reroll_boss'), localize('$')..cry_cheapest_boss_reroll()}, button = "reroll_boss", func = 'reroll_boss_button'}) or nil + }}, + config = {align="cm", offset = {x=0,y=-15},major = G.HUD:get_UIE_by_ID('row_blind'), bond = 'Weak'} + } + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + G.blind_prompt_box.alignment.offset.y = 0 + return true + end) + })) + + local width = G.hand.T.w + G.GAME.blind_on_deck = + not (G.GAME.round_resets.blind_states.Small == 'Defeated' or G.GAME.round_resets.blind_states.Small == 'Skipped' or G.GAME.round_resets.blind_states.Small == 'Hide') and 'Small' or + not (G.GAME.round_resets.blind_states.Big == 'Defeated' or G.GAME.round_resets.blind_states.Big == 'Skipped'or G.GAME.round_resets.blind_states.Big == 'Hide') and 'Big' or + 'Boss' + + G.blind_select_opts = {} + G.blind_select_opts.small = G.GAME.round_resets.blind_states['Small'] ~= 'Hide' and UIBox{definition = {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={UIBox_dyn_container({create_UIBox_blind_choice('Small')},false,get_blind_main_colour('Small'))}}, config = {align="bmi", offset = {x=0,y=0}}} or nil + G.blind_select_opts.big = G.GAME.round_resets.blind_states['Big'] ~= 'Hide' and UIBox{definition = {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={UIBox_dyn_container({create_UIBox_blind_choice('Big')},false,get_blind_main_colour('Big'))}}, config = {align="bmi", offset = {x=0,y=0}}} or nil + G.blind_select_opts.boss = G.GAME.round_resets.blind_states['Boss'] ~= 'Hide' and UIBox{definition = {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={UIBox_dyn_container({create_UIBox_blind_choice('Boss')},false,get_blind_main_colour('Boss'), mix_colours(G.C.BLACK, get_blind_main_colour('Boss'), 0.8))}}, config = {align="bmi", offset = {x=0,y=0}}} or nil + + local t = {n=G.UIT.ROOT, config = {align = 'tm',minw = width, r = 0.15, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.5}, nodes={ + G.GAME.round_resets.blind_states['Small'] ~= 'Hide' and {n=G.UIT.O, config={align = "cm", object = G.blind_select_opts.small}} or nil, + G.GAME.round_resets.blind_states['Big'] ~= 'Hide' and {n=G.UIT.O, config={align = "cm", object = G.blind_select_opts.big}} or nil, + G.GAME.round_resets.blind_states['Boss'] ~= 'Hide' and {n=G.UIT.O, config={align = "cm", object = G.blind_select_opts.boss}} or nil, + }} + }} + return t +end + +function create_UIBox_blind_tag(blind_choice, run_info) + G.GAME.round_resets.blind_tags = G.GAME.round_resets.blind_tags or {} + if not G.GAME.round_resets.blind_tags[blind_choice] then return nil end + local _tag = Tag(G.GAME.round_resets.blind_tags[blind_choice], nil, blind_choice) + local _tag_ui, _tag_sprite = _tag:generate_UI() + _tag_sprite.states.collide.can = not not run_info + return + {n=G.UIT.R, config={id = 'tag_container', ref_table = _tag, align = "cm"}, nodes={ + {n=G.UIT.R, config={align = 'tm', minh = 0.65}, nodes={ + {n=G.UIT.T, config={text = localize('k_or'), scale = 0.55, colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.WHITE, shadow = not disabled}}, + }}, + {n=G.UIT.R, config={id = 'tag_'..blind_choice, align = "cm", r = 0.1, padding = 0.1, minw = 1, can_collide = true, ref_table = _tag_sprite}, nodes={ + {n=G.UIT.C, config={id = 'tag_desc', align = "cm", minh = 1}, nodes={ + _tag_ui + }}, + not run_info and {n=G.UIT.C, config={align = "cm", colour = G.C.UI.BACKGROUND_INACTIVE, minh = 0.6, minw = 2, maxw = 2, padding = 0.07, r = 0.1, shadow = true, hover = true, one_press = true, button = 'skip_blind', func = 'hover_tag_proxy', ref_table = _tag}, nodes={ + {n=G.UIT.T, config={text = localize('b_skip_blind'), scale = 0.4, colour = G.C.UI.TEXT_INACTIVE}} + }} or {n=G.UIT.C, config={align = "cm", padding = 0.1, emboss = 0.05, colour = mix_colours(G.C.BLUE, G.C.BLACK, 0.4), r = 0.1, maxw = 2}, nodes={ + {n=G.UIT.T, config={text = localize('b_skip_reward'), scale = 0.35, colour = G.C.WHITE}}, + }}, + }} + }} +end + +function create_UIBox_blind_choice(type, run_info) + if not G.GAME.blind_on_deck then + G.GAME.blind_on_deck = G.GAME.modifiers.cry_no_small_blind and 'Big' or 'Small' + end + if not run_info then G.GAME.round_resets.blind_states[G.GAME.blind_on_deck] = 'Select' end + + local disabled = false + type = type or 'Small' + + local blind_choice = { + config = G.P_BLINDS[G.GAME.round_resets.blind_choices[type]], + } + + blind_choice.animation = AnimatedSprite(0,0, 1.4, 1.4, G.ANIMATION_ATLAS[blind_choice.config.atlas] or G.ANIMATION_ATLAS['blind_chips'], blind_choice.config.pos) + blind_choice.animation:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'} + }) + local extras = nil + local stake_sprite = get_stake_sprite(G.GAME.stake or 1, 0.5) + + G.GAME.orbital_choices = G.GAME.orbital_choices or {} + G.GAME.orbital_choices[G.GAME.round_resets.ante] = G.GAME.orbital_choices[G.GAME.round_resets.ante] or {} + + if not G.GAME.orbital_choices[G.GAME.round_resets.ante][type] then + local _poker_hands = {} + for k, v in pairs(G.GAME.hands) do + if v.visible then _poker_hands[#_poker_hands+1] = k end + end + + G.GAME.orbital_choices[G.GAME.round_resets.ante][type] = pseudorandom_element(_poker_hands, pseudoseed('orbital')) + end + + + + if type == 'Small' and not G.GAME.modifiers.cry_no_tags then + extras = create_UIBox_blind_tag(type, run_info) + elseif type == 'Big' and not G.GAME.modifiers.cry_no_tags then + extras = create_UIBox_blind_tag(type, run_info) + elseif type == 'Boss' and not run_info then + local dt1 = DynaText({string = {{string = localize('ph_up_ante_1'), colour = G.C.FILTER}}, colours = {G.C.BLACK}, scale = 0.55, silent = true, pop_delay = 4.5, shadow = true, bump = true, maxw = 3}) + local dt2 = DynaText({string = {{string = localize('ph_up_ante_2'), colour = G.C.WHITE}},colours = {G.C.CHANCE}, scale = 0.35, silent = true, pop_delay = 4.5, shadow = true, maxw = 3}) + local dt3 = DynaText({string = {{string = localize('ph_up_ante_3'), colour = G.C.WHITE}},colours = {G.C.CHANCE}, scale = 0.35, silent = true, pop_delay = 4.5, shadow = true, maxw = 3}) + extras = + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.07, r = 0.1, colour = {0,0,0,0.12}, minw = 2.9}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = dt1}}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = dt2}}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = dt3}}, + }}, + }}, + }} + end + G.GAME.round_resets.blind_ante = G.GAME.round_resets.blind_ante or G.GAME.round_resets.ante + local loc_vars = nil + if blind_choice.config.name == 'The Ox' then + loc_vars = {localize(G.GAME.current_round.most_played_poker_hand, 'poker_hands')} + end + local obj = blind_choice.config + if obj.loc_vars and _G['type'](obj.loc_vars) == 'function' then + local res = obj:loc_vars() or {} + loc_vars = res.vars or {} + end + local loc_target = localize{type = 'raw_descriptions', key = blind_choice.config.key, set = 'Blind', vars = loc_vars or blind_choice.config.vars} + local loc_name = localize{type = 'name_text', key = blind_choice.config.key, set = 'Blind'} + local text_table = loc_target + local blind_col = get_blind_main_colour(type) + local blind_amt = get_blind_amount(G.GAME.round_resets.blind_ante)*blind_choice.config.mult*G.GAME.starting_params.ante_scaling + + local blind_state = G.GAME.round_resets.blind_states[type] + local _reward = true + if G.GAME.modifiers.no_blind_reward and G.GAME.modifiers.no_blind_reward[type] then _reward = nil end + if blind_state == 'Select' then blind_state = 'Current' end + local blind_desc_nodes = {} + for k, v in ipairs(text_table) do + blind_desc_nodes[#blind_desc_nodes+1] = {n=G.UIT.R, config={align = "cm", maxw = 2.8}, nodes={ + {n=G.UIT.T, config={text = v or '-', scale = 0.32, colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.WHITE, shadow = not disabled}} + }} + end + local run_info_colour = run_info and (blind_state == 'Defeated' and G.C.GREY or blind_state == 'Skipped' and G.C.BLUE or blind_state == 'Upcoming' and G.C.ORANGE or blind_state == 'Current' and G.C.RED or G.C.GOLD) + local t = + {n=G.UIT.R, config={id = type, align = "tm", func = 'blind_choice_handler', minh = not run_info and 10 or nil, ref_table = {deck = nil, run_info = run_info}, r = 0.1, padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", colour = mix_colours(G.C.BLACK, G.C.L_BLACK, 0.5), r = 0.1, outline = 1, outline_colour = G.C.L_BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.2}, nodes={ + not run_info and {n=G.UIT.R, config={id = 'select_blind_button', align = "cm", ref_table = blind_choice.config, colour = disabled and G.C.UI.BACKGROUND_INACTIVE or G.C.ORANGE, minh = 0.6, minw = 2.7, padding = 0.07, r = 0.1, shadow = true, hover = true, one_press = true, button = 'select_blind'}, nodes={ + {n=G.UIT.T, config={ref_table = G.GAME.round_resets.loc_blind_states, ref_value = type, scale = 0.45, colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.UI.TEXT_LIGHT, shadow = not disabled}} + }} or + {n=G.UIT.R, config={id = 'select_blind_button', align = "cm", ref_table = blind_choice.config, colour = run_info_colour, minh = 0.6, minw = 2.7, padding = 0.07, r = 0.1, emboss = 0.08}, nodes={ + {n=G.UIT.T, config={text = localize(blind_state, 'blind_states'), scale = 0.45, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }} + }}, + {n=G.UIT.R, config={id = 'blind_name',align = "cm", padding = 0.07}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1, outline = 1, outline_colour = blind_col, colour = darken(blind_col, 0.3), minw = 2.9, emboss = 0.1, padding = 0.07, line_emboss = 1}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = loc_name, colours = {disabled and G.C.UI.TEXT_INACTIVE or G.C.WHITE}, shadow = not disabled, float = not disabled, y_offset = -4, scale = 0.45, maxw =2.8})}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={id = 'blind_desc', align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 1.5}, nodes={ + {n=G.UIT.O, config={object = blind_choice.animation}}, + }}, + text_table[1] and {n=G.UIT.R, config={align = "cm", minh = 0.7, padding = 0.05, minw = 2.9}, nodes = blind_desc_nodes} or nil, + }}, + {n=G.UIT.R, config={align = "cm",r = 0.1, padding = 0.05, minw = 3.1, colour = G.C.BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 3}, nodes={ + {n=G.UIT.T, config={text = localize('ph_blind_score_at_least'), scale = 0.3, colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.WHITE, shadow = not disabled}} + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.6}, nodes={ + {n=G.UIT.O, config={w=0.5,h=0.5, colour = G.C.BLUE, object = stake_sprite, hover = true, can_collide = false}}, + {n=G.UIT.B, config={h=0.1,w=0.1}}, + {n=G.UIT.T, config={text = number_format(blind_amt), scale = score_number_scale(0.9, blind_amt), colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.RED, shadow = not disabled}} + }}, + _reward and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = localize('ph_blind_reward'), scale = 0.35, colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.WHITE, shadow = not disabled}}, + {n=G.UIT.T, config={text = string.rep(localize("$"), blind_choice.config.dollars)..'+', scale = 0.35, colour = disabled and G.C.UI.TEXT_INACTIVE or G.C.MONEY, shadow = not disabled}} + }} or nil, + }}, + }}, + }}, + }}, + {n=G.UIT.R, config={id = 'blind_extras', align = "cm"}, nodes={ + extras, + }} + + }} + return t +end + +function create_UIBox_round_evaluation() + local width = G.hand.T.w-2 + local t = {n=G.UIT.ROOT, config = {align = 'tm',colour = G.C.CLEAR}, nodes={ + UIBox_dyn_container( + { + {n=G.UIT.R, config={align = "tm", minw = width, minh = 3, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", minw = width, minh = 1.4}, nodes={}}, + {n=G.UIT.R, config={align = "cm", minw = width, id = 'base_round_eval'}, nodes={}}, + {n=G.UIT.R, config={align = "cm", minw = width, id = 'bonus_round_eval'}, nodes={}} + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={}}, + {n=G.UIT.R, config={align = "cm", minw = width, id = 'eval_bottom'}, nodes={}} + },false) + }} +return t +end + +function create_UIBox_arcana_pack() + local _size = G.GAME.pack_size + G.pack_cards = CardArea( + G.ROOM.T.x + 9 + G.hand.T.x, G.hand.T.y, + math.max(1,math.min(_size,5))*G.CARD_W, + 1.05*G.CARD_H, + {card_limit = _size, type = 'consumeable', highlight_limit = 1}) + + local t = {n=G.UIT.ROOT, config = {align = 'tm', r = 0.15, colour = G.C.CLEAR, padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cl", colour = G.C.CLEAR,r=0.15, padding = 0.1, minh = 2, shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", r=0.2, colour = G.C.CLEAR, shadow = true}, nodes={ + {n=G.UIT.O, config={object = G.pack_cards}}, + }} + }} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + }}, + {n=G.UIT.R, config={align = "tm"}, nodes={ + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={}}, + {n=G.UIT.C,config={align = "tm", padding = 0.05}, nodes={ + UIBox_dyn_container({ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 4}, nodes={ + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize('k_arcana_pack'), colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.7, maxw = 4, pop_in = 0.5})}} + }}, + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('k_choose')..' '}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}}, + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME, ref_value = 'pack_choices'}}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}} + }}, + }} + }), + }}, + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={ + {n=G.UIT.R,config={minh =0.2}, nodes={}}, + {n=G.UIT.R,config={align = "tm",padding = 0.2, minh = 1.2, minw = 1.8, r=0.15,colour = G.C.GREY, one_press = true, button = 'skip_booster', hover = true,shadow = true, func = 'can_skip_booster'}, nodes = { + {n=G.UIT.T, config={text = localize('b_skip'), scale = 0.5, colour = G.C.WHITE, shadow = true, focus_args = {button = 'y', orientation = 'bm'}, func = 'set_button_pip'}} + }} + }} + }} + }} + }} + return t +end + +function create_UIBox_spectral_pack() + local _size = G.GAME.pack_size + G.pack_cards = CardArea( + G.ROOM.T.x + 9 + G.hand.T.x, G.hand.T.y, + math.max(1,math.min(_size,5))*G.CARD_W, + 1.05*G.CARD_H, + {card_limit = _size, type = 'consumeable', highlight_limit = 1}) + + local t = {n=G.UIT.ROOT, config = {align = 'tm', r = 0.15, colour = G.C.CLEAR, padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cl", colour = G.C.CLEAR,r=0.15, padding = 0.1, minh = 2, shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", r=0.2, colour = G.C.CLEAR, shadow = true}, nodes={ + {n=G.UIT.O, config={object = G.pack_cards}}, + }} + }} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + }}, + {n=G.UIT.R, config={align = "tm"}, nodes={ + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={}}, + {n=G.UIT.C,config={align = "tm", padding = 0.05}, nodes={ + UIBox_dyn_container({ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 4}, nodes={ + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize('k_spectral_pack'), colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.7, maxw = 4, pop_in = 0.5})}} + }}, + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('k_choose')..' '}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}}, + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME, ref_value = 'pack_choices'}}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}} + }}, + }} + }), + }}, + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={ + {n=G.UIT.R,config={minh =0.2}, nodes={}}, + {n=G.UIT.R,config={align = "tm",padding = 0.2, minh = 1.2, minw = 1.8, r=0.15,colour = G.C.GREY, one_press = true, button = 'skip_booster', hover = true,shadow = true, func = 'can_skip_booster'}, nodes = { + {n=G.UIT.T, config={text = localize('b_skip'), scale = 0.5, colour = G.C.WHITE, shadow = true, focus_args = {button = 'y', orientation = 'bm'}, func = 'set_button_pip'}} + }} + }} + }} + }} + }} + return t +end + +function create_UIBox_standard_pack() + local _size = G.GAME.pack_size + G.pack_cards = CardArea( + G.ROOM.T.x + 9 + G.hand.T.x, G.hand.T.y, + math.max(1,math.min(_size,5))*G.CARD_W*1.1, + 1.05*G.CARD_H, + {card_limit = _size, type = 'consumeable', highlight_limit = 1}) + + local t = {n=G.UIT.ROOT, config = {align = 'tm', r = 0.15, colour = G.C.CLEAR, padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cl", colour = G.C.CLEAR,r=0.15, padding = 0.1, minh = 2, shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", r=0.2, colour = G.C.CLEAR, shadow = true}, nodes={ + {n=G.UIT.O, config={object = G.pack_cards}}, + }} + }} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + }}, + {n=G.UIT.R, config={align = "tm"}, nodes={ + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={}}, + {n=G.UIT.C,config={align = "tm", padding = 0.05}, nodes={ + UIBox_dyn_container({ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 4}, nodes={ + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize('k_standard_pack'), colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.7, maxw = 4, pop_in = 0.5})}} + }}, + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('k_choose')..' '}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}}, + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME, ref_value = 'pack_choices'}}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}} + }}, + }} + }), + }}, + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={ + {n=G.UIT.R,config={minh =0.2}, nodes={}}, + {n=G.UIT.R,config={align = "tm",padding = 0.2, minh = 1.2, minw = 1.8, r=0.15,colour = G.C.GREY, one_press = true, button = 'skip_booster', hover = true,shadow = true, func = 'can_skip_booster'}, nodes = { + {n=G.UIT.T, config={text = localize('b_skip'), scale = 0.5, colour = G.C.WHITE, shadow = true, focus_args = {button = 'y', orientation = 'bm'}, func = 'set_button_pip'}} + }} + }} + }} + }} + }} + return t +end + +function create_UIBox_buffoon_pack() + local _size = G.GAME.pack_size + G.pack_cards = CardArea( + G.ROOM.T.x + 9 + G.hand.T.x, G.hand.T.y, + math.max(1,math.min(_size,5))*G.CARD_W*1.1, + 1.05*G.CARD_H, + {card_limit = _size, type = 'consumeable', highlight_limit = 1}) + + local t = {n=G.UIT.ROOT, config = {align = 'tm', r = 0.15, colour = G.C.CLEAR, padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cl", colour = G.C.CLEAR,r=0.15, padding = 0.1, minh = 2, shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", r=0.2, colour = G.C.CLEAR, shadow = true}, nodes={ + {n=G.UIT.O, config={object = G.pack_cards}}, + }} + }} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + }}, + {n=G.UIT.R, config={align = "tm"}, nodes={ + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={}}, + {n=G.UIT.C,config={align = "tm", padding = 0.05}, nodes={ + UIBox_dyn_container({ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 4}, nodes={ + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize('k_buffoon_pack'), colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.7, maxw = 4, pop_in = 0.5})}} + }}, + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('k_choose')..' '}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}}, + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME, ref_value = 'pack_choices'}}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}} + }}, + }} + }), + }}, + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={ + {n=G.UIT.R,config={minh =0.2}, nodes={}}, + {n=G.UIT.R,config={align = "tm",padding = 0.2, minh = 1.2, minw = 1.8, r=0.15,colour = G.C.GREY, one_press = true, button = 'skip_booster', hover = true,shadow = true, func = 'can_skip_booster'}, nodes = { + {n=G.UIT.T, config={text = localize('b_skip'), scale = 0.5, colour = G.C.WHITE, shadow = true, focus_args = {button = 'y', orientation = 'bm'}, func = 'set_button_pip'}} + }} + }} + }} + }} + }} + return t +end + +function create_UIBox_celestial_pack() + local _size = G.GAME.pack_size + G.pack_cards = CardArea( + G.ROOM.T.x + 9 + G.hand.T.x, G.hand.T.y, + math.max(1,math.min(_size,5))*G.CARD_W*1.1 + 0.5, + 1.05*G.CARD_H, + {card_limit = _size, type = 'consumeable', highlight_limit = 1}) + + local t = {n=G.UIT.ROOT, config = {align = 'tm', r = 0.15, colour = G.C.CLEAR, padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cl", colour = G.C.CLEAR,r=0.15, padding = 0.1, minh = 2, shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", r=0.2, colour = G.C.CLEAR, shadow = true}, nodes={ + {n=G.UIT.O, config={object = G.pack_cards}}, + }} + }} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + }}, + {n=G.UIT.R, config={align = "tm"}, nodes={ + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={}}, + {n=G.UIT.C,config={align = "tm", padding = 0.05}, nodes={ + UIBox_dyn_container({ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 4}, nodes={ + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize('k_celestial_pack'), colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.7, maxw = 4, pop_in = 0.5})}} + }}, + {n=G.UIT.R,config={align = "bm", padding = 0.05}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('k_choose')..' '}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}}, + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.GAME, ref_value = 'pack_choices'}}, colours = {G.C.WHITE},shadow = true, rotate = true, bump = true, spacing =2, scale = 0.5, pop_in = 0.7})}} + }}, + }} + }), + }}, + {n=G.UIT.C,config={align = "tm", padding = 0.05, minw = 2.4}, nodes={ + {n=G.UIT.R,config={minh =0.2}, nodes={}}, + {n=G.UIT.R,config={align = "tm",padding = 0.2, minh = 1.2, minw = 1.8, r=0.15,colour = G.C.GREY, one_press = true, button = 'skip_booster', hover = true,shadow = true, func = 'can_skip_booster'}, nodes = { + {n=G.UIT.T, config={text = localize('b_skip'), scale = 0.5, colour = G.C.WHITE, shadow = true, focus_args = {button = 'y', orientation = 'bm'}, func = 'set_button_pip'}} + }} + }} + }} + }} + }} + return t +end + +function create_UIBox_card_alert(args) + args = args or {} + return {n=G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR, refresh_movement = true}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.15, minw = 0.42, minh = 0.42, colour = args.no_bg and G.C.CLEAR or args.bg_col or (args.red_bad and darken(G.C.RED, 0.1) or G.C.RED), draw_layer = 1, emboss = 0.05, refresh_movement = true}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = args.text or '!', colours = {G.C.WHITE},shadow = true, rotate = true,H_offset = args.y_offset or 0,bump_rate = args.text and 3 or 7, bump_amount = args.bump_amount or 3, bump = true,maxw = args.maxw, text_rot = args.text_rot or 0.2, spacing = 3*(args.scale or 1), scale = args.scale or 0.48})}} + }}, + }} +end + +function create_slider(args) + args = args or {} + args.colour = args.colour or G.C.RED + args.w = args.w or 1 + args.h = args.h or 0.5 + args.label_scale = args.label_scale or 0.5 + args.text_scale = args.text_scale or 0.3 + args.min = args.min or 0 + args.max = args.max or 1 + args.decimal_places = args.decimal_places or 0 + args.text = string.format("%."..tostring(args.decimal_places).."f", args.ref_table[args.ref_value]) + local startval = args.w * (args.ref_table[args.ref_value] - args.min)/(args.max - args.min) + + local t = + {n=G.UIT.C, config={align = "cm", minw = args.w, min_h = args.h, padding = 0.1, r = 0.1, colour = G.C.CLEAR, focus_args = {type = 'slider'}}, nodes={ + {n=G.UIT.C, config={align = "cl", minw = args.w, r = 0.1,min_h = args.h,collideable = true, hover = true, colour = G.C.BLACK,emboss = 0.05,func = 'slider', refresh_movement = true}, nodes={ + {n=G.UIT.B, config={id = args.id, w=startval,h=args.h, r = 0.1, colour = args.colour, ref_table = args, refresh_movement = true}}, + }}, + not args.hide_val and {n=G.UIT.C, config={align = "cm", minh = args.h,r = 0.1, minw = 0.8, colour = args.colour,shadow = true}, nodes={ + {n=G.UIT.T, config={ref_table = args, ref_value = 'text', scale = args.text_scale, colour = G.C.UI.TEXT_LIGHT, decimal_places = args.decimal_places}} + }} or nil + }} + if args.label then + t = {n=G.UIT.R, config={align = "cm", minh = 1, minw = 1, padding = 0.1*args.label_scale, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = args.label, scale = args.label_scale, colour = G.C.UI.TEXT_LIGHT}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + t + }} + }} + end + return t +end + +function create_toggle(args) + args = args or {} + args.active_colour = args.active_colour or G.C.RED + args.inactive_colour = args.inactive_colour or G.C.BLACK + args.w = args.w or 3 + args.h = args.h or 0.5 + args.scale = args.scale or 1 + args.label = args.label or 'TEST?' + args.label_scale = args.label_scale or 0.4 + args.ref_table = args.ref_table or {} + args.ref_value = args.ref_value or 'test' + + local check = Sprite(0,0,0.5*args.scale,0.5*args.scale,G.ASSET_ATLAS["icons"], {x=1, y=0}) + check.states.drag.can = false + check.states.visible = false + + local info = nil + if args.info then + info = {} + for k, v in ipairs(args.info) do + table.insert(info, {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + {n=G.UIT.T, config={text = v, scale = 0.25, colour = G.C.UI.TEXT_LIGHT}} + }}) + end + info = {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes=info} + end + + local t = + {n=args.col and G.UIT.C or G.UIT.R, config={align = "cm", padding = 0.1, r = 0.1, colour = G.C.CLEAR, focus_args = {funnel_from = true}}, nodes={ + {n=G.UIT.C, config={align = "cr", minw = args.w}, nodes={ + {n=G.UIT.T, config={text = args.label, scale = args.label_scale, colour = G.C.UI.TEXT_LIGHT}}, + {n=G.UIT.B, config={w = 0.1, h = 0.1}}, + }}, + {n=G.UIT.C, config={align = "cl", minw = 0.3*args.w}, nodes={ + {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.BLACK}, nodes={ + {n=G.UIT.C, config={align = "cm", r = 0.1, padding = 0.03, minw = 0.4*args.scale, minh = 0.4*args.scale, outline_colour = G.C.WHITE, outline = 1.2*args.scale, line_emboss = 0.5*args.scale, ref_table = args, + colour = args.inactive_colour, + button = 'toggle_button', button_dist = 0.2, hover = true, toggle_callback = args.callback, func = 'toggle', focus_args = {funnel_to = true}}, nodes={ + {n=G.UIT.O, config={object = check}}, + }}, + }} + }}, + }} + if args.info then + t = {n=args.col and G.UIT.C or G.UIT.R, config={align = "cm"}, nodes={ + t, + info, + }} + end + return t +end + +function create_option_cycle(args) + args = args or {} + args.colour = args.colour or G.C.RED + args.options = args.options or { + 'Option 1', + 'Option 2' + } + args.current_option = args.current_option or 1 + args.current_option_val = args.options[args.current_option] + args.opt_callback = args.opt_callback or nil + args.scale = args.scale or 1 + args.ref_table = args.ref_table or nil + args.ref_value = args.ref_value or nil + args.w = (args.w or 2.5)*args.scale + args.h = (args.h or 0.8)*args.scale + args.text_scale = (args.text_scale or 0.5)*args.scale + args.l = '<' + args.r = '>' + args.focus_args = args.focus_args or {} + args.focus_args.type = 'cycle' + + local info = nil + if args.info then + info = {} + for k, v in ipairs(args.info) do + table.insert(info, {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + {n=G.UIT.T, config={text = v, scale = 0.3*args.scale, colour = G.C.UI.TEXT_LIGHT}} + }}) + end + info = {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes=info} + end + + local disabled = #args.options < 2 + local pips = {} + for i = 1, #args.options do + pips[#pips+1] = {n=G.UIT.B, config={w = 0.1*args.scale, h = 0.1*args.scale, r = 0.05, id = 'pip_'..i, colour = args.current_option == i and G.C.WHITE or G.C.BLACK}} + end + + local choice_pips = not args.no_pips and {n=G.UIT.R, config={align = "cm", padding = (0.05 - (#args.options > 15 and 0.03 or 0))*args.scale}, nodes=pips} or nil + + local t = + {n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, colour = G.C.CLEAR, id = args.id and (not args.label and args.id or nil) or nil, focus_args = args.focus_args}, nodes={ + {n=G.UIT.C, config={align = "cm",r = 0.1, minw = 0.6*args.scale, hover = not disabled, colour = not disabled and args.colour or G.C.BLACK,shadow = not disabled, button = not disabled and 'option_cycle' or nil, ref_table = args, ref_value = 'l', focus_args = {type = 'none'}}, nodes={ + {n=G.UIT.T, config={ref_table = args, ref_value = 'l', scale = args.text_scale, colour = not disabled and G.C.UI.TEXT_LIGHT or G.C.UI.TEXT_INACTIVE}} + }}, + args.mid and + {n=G.UIT.C, config={id = 'cycle_main'}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + args.mid + }}, + not disabled and choice_pips or nil + }} + or {n=G.UIT.C, config={id = 'cycle_main', align = "cm", minw = args.w, minh = args.h, r = 0.1, padding = 0.05, colour = args.colour,emboss = 0.1, hover = true, can_collide = true, on_demand_tooltip = args.on_demand_tooltip}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = args, ref_value = "current_option_val"}}, colours = {G.C.UI.TEXT_LIGHT},pop_in = 0, pop_in_rate = 8, reset_pop_in = true,shadow = true, float = true, silent = true, bump = true, scale = args.text_scale, non_recalc = true})}}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.05}, nodes={ + }}, + not disabled and choice_pips or nil + }} + }}, + {n=G.UIT.C, config={align = "cm",r = 0.1, minw = 0.6*args.scale, hover = not disabled, colour = not disabled and args.colour or G.C.BLACK,shadow = not disabled, button = not disabled and 'option_cycle' or nil, ref_table = args, ref_value = 'r', focus_args = {type = 'none'}}, nodes={ + {n=G.UIT.T, config={ref_table = args, ref_value = 'r', scale = args.text_scale, colour = not disabled and G.C.UI.TEXT_LIGHT or G.C.UI.TEXT_INACTIVE}} + }}, + }} + + if args.cycle_shoulders then + t = + {n=G.UIT.R, config={align = "cm", colour = G.C.CLEAR}, nodes = { + {n=G.UIT.C, config={minw = 0.7,align = "cm", colour = G.C.CLEAR,func = 'set_button_pip', focus_args = {button = 'leftshoulder', type = 'none', orientation = 'cm', scale = 0.7, offset = {x = -0.1, y = 0}}}, nodes = {}}, + {n=G.UIT.C, config={id = 'cycle_shoulders', padding = 0.1}, nodes={t}}, + {n=G.UIT.C, config={minw = 0.7,align = "cm", colour = G.C.CLEAR,func = 'set_button_pip', focus_args = {button = 'rightshoulder', type = 'none', orientation = 'cm', scale = 0.7, offset = {x = 0.1, y = 0}}}, nodes = {}}, + }} + else + t = + {n=G.UIT.R, config={align = "cm", colour = G.C.CLEAR, padding = 0.0}, nodes = { + t + }} + end + if args.label or args.info then + t = {n=G.UIT.R, config={align = "cm", padding = 0.05, id = args.id or nil}, nodes={ + args.label and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = args.label, scale = 0.5*args.scale, colour = G.C.UI.TEXT_LIGHT}} + }} or nil, + t, + info, + }} + end + return t +end + +function create_tabs(args) + args = args or {} + args.colour = args.colour or G.C.RED + args.tab_alignment = args.tab_alignment or 'cm' + args.opt_callback = args.opt_callback or nil + args.scale = args.scale or 1 + args.tab_w = args.tab_w or 0 + args.tab_h = args.tab_h or 0 + args.text_scale = (args.text_scale or 0.5) + args.tabs = args.tabs or { + { + label = 'tab 1', + chosen = true, + func = nil, + tab_definition_function = function() return {n=G.UIT.ROOT, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = 'A', scale = 1, colour = G.C.UI.TEXT_LIGHT}} + }} end + }, + { + label = 'tab 2', + chosen = false, + tab_definition_function = function() return {n=G.UIT.ROOT, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = 'B', scale = 1, colour = G.C.UI.TEXT_LIGHT}} + }} end + }, + { + label = 'tab 3', + chosen = false, + tab_definition_function = function() return {n=G.UIT.ROOT, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = 'C', scale = 1, colour = G.C.UI.TEXT_LIGHT}} + }} end + } + } + + local tab_buttons = {} + + for k, v in ipairs(args.tabs) do + if v.chosen then args.current = {k = k, v = v} end + tab_buttons[#tab_buttons+1] = UIBox_button({id = 'tab_but_'..(v.label or ''), ref_table = v, button = 'change_tab', label = {v.label}, minh = 0.8*args.scale, minw = 2.5*args.scale, col = true, choice = true, scale = args.text_scale, chosen = v.chosen, func = v.func, colour = args.colour, focus_args = {type = 'none'}}) + end + + local t = + {n=G.UIT.R, config={padding = 0.0, align = "cm", colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", colour = G.C.CLEAR}, nodes = { + (#args.tabs > 1 and not args.no_shoulders) and {n=G.UIT.C, config={minw = 0.7,align = "cm", colour = G.C.CLEAR,func = 'set_button_pip', focus_args = {button = 'leftshoulder', type = 'none', orientation = 'cm', scale = 0.7, offset = {x = -0.1, y = 0}}}, nodes = {}} or nil, + {n=G.UIT.C, config={id = args.no_shoulders and 'no_shoulders' or 'tab_shoulders', ref_table = args, align = "cm", padding = 0.15, group = 1, collideable = true, focus_args = #args.tabs > 1 and {type = 'tab', nav = 'wide',snap_to = args.snap_to_nav, no_loop = args.no_loop} or nil}, nodes=tab_buttons}, + (#args.tabs > 1 and not args.no_shoulders) and {n=G.UIT.C, config={minw = 0.7,align = "cm", colour = G.C.CLEAR,func = 'set_button_pip', focus_args = {button = 'rightshoulder', type = 'none', orientation = 'cm', scale = 0.7, offset = {x = 0.1, y = 0}}}, nodes = {}} or nil, + }}, + {n=G.UIT.R, config={align = args.tab_alignment, padding = args.padding or 0.1, no_fill = true, minh = args.tab_h, minw = args.tab_w}, nodes={ + {n=G.UIT.O, config={id = 'tab_contents', object = UIBox{definition = args.current.v.tab_definition_function(args.current.v.tab_definition_function_args), config = {offset = {x=0,y=0}}}}} + }}, + }} + + return t +end + +function create_text_input(args) + args = args or {} + args.colour = copy_table(args.colour) or copy_table(G.C.BLUE) + args.hooked_colour = copy_table(args.hooked_colour) or darken(copy_table(G.C.BLUE), 0.3) + args.w = args.w or 2.5 + args.h = args.h or 0.7 + args.text_scale = args.text_scale or 0.4 + args.max_length = args.max_length or 16 + args.all_caps = args.all_caps or false + args.prompt_text = args.prompt_text or localize('k_enter_text') + args.current_prompt_text = '' + args.id = args.id or "text_input" + + local text = {ref_table = args.ref_table, ref_value = args.ref_value, letters = {}, current_position = string.len(args.ref_table[args.ref_value])} + local ui_letters = {} + for i = 1, args.max_length do + text.letters[i] = (args.ref_table[args.ref_value] and (string.sub(args.ref_table[args.ref_value], i, i) or '')) or '' + ui_letters[i] = {n=G.UIT.T, config={ref_table = text.letters, ref_value = i, scale = args.text_scale, colour = G.C.UI.TEXT_LIGHT, id = args.id..'_letter_'..i}} + end + args.text = text + + local position_text_colour = lighten(copy_table(G.C.BLUE), 0.4) + + ui_letters[#ui_letters+1] = {n=G.UIT.T, config={ref_table = args, ref_value = 'current_prompt_text', scale = args.text_scale, colour = lighten(copy_table(args.colour), 0.4), id = args.id..'_prompt'}} + ui_letters[#ui_letters+1] = {n=G.UIT.B, config={r = 0.03,w=0.1, h=0.4, colour = position_text_colour, id = args.id..'_position', func = 'flash'}} + + local t = + {n=G.UIT.C, config={align = "cm", colour = G.C.CLEAR}, nodes = { + {n=G.UIT.C, config={id = args.id, align = "cm", padding = 0.05, r = 0.1, hover = true, colour = args.colour,minw = args.w, min_h = args.h, button = 'select_text_input', shadow = true}, nodes={ + {n=G.UIT.R, config={ref_table = args, padding = 0.05, align = "cm", r = 0.1, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={ref_table = args, align = "cm", r = 0.1, colour = G.C.CLEAR, func = 'text_input'}, nodes= + ui_letters + } + }} + }} + }} + return t +end + +function create_keyboard_input(args) + local keyboard_rows = { + '1234567890', + 'QWERTYUIOP', + 'ASDFGHJKL', + 'ZXCVBNM', + args.space_key and ' ' or nil + } + local keyboard_button_rows = { + {},{},{},{},{} + } + for k, v in ipairs(keyboard_rows) do + for i = 1, #v do + local c = v:sub(i,i) + keyboard_button_rows[k][#keyboard_button_rows[k] +1] = create_keyboard_button(c, c == ' ' and 'y' or nil) + end + end + return {n=G.UIT.ROOT, config={align = "cm", padding = 15, r = 0.1, colour = {G.C.GREY[1], G.C.GREY[2], G.C.GREY[3],0.7}}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes = { + {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.BLACK, emboss = 0.05, r = 0.1, mid = true}, nodes = { + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes = { + {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes = { + {n=G.UIT.R, config={align = "cm", padding = 0.07, colour = G.C.CLEAR}, nodes=keyboard_button_rows[1]}, + {n=G.UIT.R, config={align = "cm", padding = 0.07, colour = G.C.CLEAR}, nodes=keyboard_button_rows[2]}, + {n=G.UIT.R, config={align = "cm", padding = 0.07, colour = G.C.CLEAR}, nodes=keyboard_button_rows[3]}, + {n=G.UIT.R, config={align = "cm", padding = 0.07, colour = G.C.CLEAR}, nodes=keyboard_button_rows[4]}, + {n=G.UIT.R, config={align = "cm", padding = 0.07, colour = G.C.CLEAR}, nodes=keyboard_button_rows[5]} + }}, + (args.backspace_key or args.return_key) and {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes = { + args.backspace_key and {n=G.UIT.R, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={create_keyboard_button('backspace', 'x')}} or nil, + args.return_key and {n=G.UIT.R, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={create_keyboard_button('return', 'start')}} or nil, + {n=G.UIT.R, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={create_keyboard_button('back', 'b')}} + }} or nil + }}, + }} + }}, + + }} +end + +function create_keyboard_button(key, binding) + local key_label = (key == 'backspace' and 'Backspace') or (key == ' ' and 'Space') or (key == 'back' and 'Back') or (key == 'return' and 'Enter') or key + return UIBox_button{ label = {key_label}, button = "key_button", ref_table = {key = key == 'back' and 'return' or key}, + minw = key == ' ' and 6 or key == 'return' and 2.5 or key == 'backspace' and 2.5 or key == 'back' and 2.5 or 0.8, + minh = key == 'return' and 1.5 or key == 'backspace' and 1.5 or key == 'back' and 0.8 or 0.7, + col = true, colour = G.C.GREY, focus_args = binding and {button = binding, orientation = (key == ' ' or key == 'back') and 'cr' or 'bm', set_button_pip= true} or nil} +end + +function create_dynatext_pips(args) + args = args or {} + + args.active_colour = copy_table(args.colour) or G.C.RED + args.inactive_colour = copy_table(args.inactive_colour) or {0,0,0,0.08} + args.w = args.w or 0.07 + args.h = args.h or 0.07 + + if not (args.dynatext) or not (#args.dynatext.strings > 1) then return end + + local pips = {} + + for i = 1, #args.dynatext.strings do + pips[i] = {n=G.UIT.C, config={ref_table = args.dynatext, minw = args.w, minh = args.h, colour = G.C.UI.TEXT_INACTIVE, r = 0.1, id = 'pip_'..i, pipcol1 = args.active_colour, pipcol2 = args.inactive_colour, func = 'pip_dynatext'}} + end + + return {n=G.UIT.R, config={padding = 0.05, align = "cm"}, nodes=pips} +end + +function create_UIBox_options() + local current_seed = nil + local restart = nil + local main_menu = nil + local mods = nil + local your_collection = nil + local credits = nil + local customize = nil + + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.REFRESH_ALERTS = true + return true + end + })) + + if G.STAGE == G.STAGES.RUN then + restart = UIBox_button{id = 'restart_button', label = {localize('b_start_new_run')}, button = "setup_run", minw = 5} + main_menu = UIBox_button{ label = {localize('b_main_menu')}, button = "go_to_menu", minw = 5} + mods = UIBox_button{ id = "mods_button", label = {localize('b_mods')}, button = "mods_button", minw = 5} + your_collection = UIBox_button{ label = {localize('b_collection')}, button = "your_collection", minw = 5, id = 'your_collection'} + current_seed = {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize('b_seed')..": ", scale = 0.4, colour = G.C.WHITE}} + }}, + {n=G.UIT.C, config={align = "cm", padding = 0, minh = 0.8}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0, minh = 0.8}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1, colour = G.GAME.seeded and G.C.RED or G.C.BLACK, minw = 1.8, minh = 0.5, padding = 0.1, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={ text = tostring(G.GAME.pseudorandom.seed), scale = 0.43, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }} + }} + }} + }}, + UIBox_button({col = true, button = 'copy_seed', label = {localize('b_copy')}, colour = G.C.BLUE, scale = 0.3, minw = 1.3, minh = 0.5,}), + }} + end + if G.STAGE == G.STAGES.MAIN_MENU then + credits = UIBox_button{ label = {localize('b_credits')}, button = "show_credits", minw = 5} + end + + local settings = UIBox_button({button = 'settings', label = {localize('b_settings')}, minw = 5, focus_args = {snap_to = true}}) + local high_scores = UIBox_button{ label = {localize('b_stats')}, button = "high_scores", minw = 5} + local customize = UIBox_button{ label = {localize('b_customize_deck')}, button = "customize_deck", minw = 5} + + local t = create_UIBox_generic_options({ contents = { + settings, + G.GAME.seeded and current_seed or nil, + restart, + main_menu, + mods, + high_scores, + your_collection, + customize, + credits + }}) + return t +end + +function create_UIBox_settings() + local tabs = {} + tabs[#tabs+1] = { + label = localize('b_set_game'), + chosen = true, + tab_definition_function = G.UIDEF.settings_tab, + tab_definition_function_args = 'Game' + } + if G.F_VIDEO_SETTINGS then tabs[#tabs+1] = { + label = localize('b_set_video'), + tab_definition_function = G.UIDEF.settings_tab, + tab_definition_function_args = 'Video' + } + end + tabs[#tabs+1] = { + label = localize('b_set_graphics'), + tab_definition_function = G.UIDEF.settings_tab, + tab_definition_function_args = 'Graphics' + } + tabs[#tabs+1] = { + label = localize('b_set_audio'), + tab_definition_function = G.UIDEF.settings_tab, + tab_definition_function_args = 'Audio' + } + + if not require("debugplus.config").SMODSLoaded then + tabs[#tabs+1] = { + label = "DebugPlus", + tab_definition_function = require("debugplus.config").fakeConfigTab, + } + end + local settings_icon = Cartomancer.add_settings_icon() + if settings_icon then + tabs[#tabs+1] = { + colour = G.C.MONEY, + custom_button = {settings_icon}, + tab_definition_function = Cartomancer.config_tab, + tab_definition_function_args = '' + } + end + local t = create_UIBox_generic_options({back_func = 'options',contents = {create_tabs( + {tabs = tabs, + tab_h = 7.05, + tab_alignment = 'tm', + snap_to_nav = true} + )}}) +return t +end + +function G.UIDEF.settings_tab(tab) + if tab == 'Game' then + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={ + create_option_cycle({label = localize('b_set_gamespeed'),scale = 0.8, options = {0.5, 1, 2, 4}, opt_callback = 'change_gamespeed', current_option = (G.SETTINGS.GAMESPEED == 0.5 and 1 or G.SETTINGS.GAMESPEED == 4 and 4 or G.SETTINGS.GAMESPEED + 1)}), + create_option_cycle({w = 5, label = localize('b_set_play_discard_pos'),scale = 0.8, options = localize('ml_play_discard_pos_opt'), opt_callback = 'change_play_discard_position', current_option = (G.SETTINGS.play_button_pos)}), + G.F_RUMBLE and create_toggle({label = localize('b_set_rumble'), ref_table = G.SETTINGS, ref_value = 'rumble'}) or nil, + create_slider({label = localize('b_set_screenshake'),w = 4, h = 0.4, ref_table = G.SETTINGS, ref_value = 'screenshake', min = 0, max = 100}), + create_toggle({label = localize('ph_display_stickers'), ref_table = G.SETTINGS, ref_value = 'run_stake_stickers'}), + --create_toggle({label = localize('b_high_contrast_cards'), ref_table = G.SETTINGS, ref_value = 'colourblind_option', callback = G.FUNCS.refresh_contrast_mode}), + create_toggle({label = localize('b_reduced_motion'), ref_table = G.SETTINGS, ref_value = 'reduced_motion'}), + G.F_CRASH_REPORTS and create_toggle({label = localize('b_set_crash_reports'), ref_table = G.SETTINGS, ref_value = 'crashreports', info = localize('ml_crash_report_info')}) or nil, + }} + elseif tab == 'Video' then + --Reset the queue so there are no pending changes + G.SETTINGS.QUEUED_CHANGE = {} + + --Refresh the display information for all displays based on the screenmode selected + local res_option = GET_DISPLAYINFO(G.SETTINGS.WINDOW.screenmode, G.SETTINGS.WINDOW.selected_display) + + return + {n=G.UIT.ROOT, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={ + create_option_cycle({w = 4,scale = 0.8, label = localize('b_set_monitor'), options = G.SETTINGS.WINDOW.display_names, opt_callback = 'change_display', current_option = (G.SETTINGS.WINDOW.selected_display)}), + create_option_cycle({w = 4,scale = 0.8, label = localize('b_set_windowmode'), options = localize('ml_windowmode_opt'), opt_callback = 'change_screenmode', current_option = (({Windowed = 1, Fullscreen = 2, Borderless = 3})[G.SETTINGS.WINDOW.screenmode] or 1)}), + {n=G.UIT.R, config={align = "cm", id = 'resolution_cycle'}, nodes={create_option_cycle({w = 4,scale = 0.8, options = G.SETTINGS.WINDOW.DISPLAYS[G.SETTINGS.WINDOW.selected_display].screen_resolutions.strings, opt_callback = 'change_screen_resolution',current_option = res_option or 1})}}, + {n=G.UIT.R, config={align = "cm"}, nodes={create_option_cycle({w = 4,scale = 0.8, options = localize('ml_vsync_opt'), opt_callback = 'change_vsync',current_option = G.SETTINGS.WINDOW.vsync == 0 and 2 or 1})}}, + UIBox_button({button = 'apply_window_changes', label = {localize('b_set_apply')}, minw = 3, func = 'can_apply_window_changes'}), + }} + elseif tab == 'Audio' then + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={ + create_slider({label = localize('b_set_master_vol'), w = 5, h = 0.4, ref_table = G.SETTINGS.SOUND, ref_value = 'volume', min = 0, max = 100}), + create_slider({label = localize('b_set_music_vol'), w = 5, h = 0.4, ref_table = G.SETTINGS.SOUND, ref_value = 'music_volume', min = 0, max = 100}), + create_slider({label = localize('b_set_game_vol'), w = 5, h = 0.4, ref_table = G.SETTINGS.SOUND, ref_value = 'game_sounds_volume', min = 0, max = 100}), + }} + elseif tab == 'Graphics' then + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={ + create_option_cycle({w = 4,scale = 0.8, label = localize("b_set_shadows"),options = localize('ml_shadow_opt'), opt_callback = 'change_shadows', current_option = (G.SETTINGS.GRAPHICS.shadows == 'On' and 1 or 2)}), + create_option_cycle({w = 4,scale = 0.8, label = localize("b_set_pixel_smoothing"),options = localize('ml_smoothing_opt'), opt_callback = 'change_pixel_smoothing', current_option = G.SETTINGS.GRAPHICS.texture_scaling}), + create_slider({label = localize('b_set_CRT'),w = 4, h = 0.4, ref_table = G.SETTINGS.GRAPHICS, ref_value = 'crt', min = 0, max = 100}), + create_option_cycle({w = 4,scale = 0.8, label = localize("b_set_CRT_bloom"),options = localize('ml_bloom_opt'), opt_callback = 'change_crt_bloom', current_option = G.SETTINGS.GRAPHICS.bloom}), + create_option_cycle({label = localize('b_graphics_mipmap_level'),scale = 0.8, options = SMODS.config.graphics_mipmap_level_options, opt_callback = 'SMODS_change_mipmap', current_option = SMODS.config.graphics_mipmap_level}), + }} + end + + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.05, colour = G.C.CLEAR, minh = 5, minw = 5}, nodes={}} +end + +function create_UIBox_test_framework(variables) + variables = variables or {}; + local nodes = {} + for k, v in pairs(variables) do + if v.type == 'cycle' then + table.insert(nodes, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = v.label or '', scale = 0.5, colour = G.C.UI.TEXT_DARK}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + create_option_cycle({options = v.options, opt_callback = 'test_framework_cycle_callback', ref_table = v.ref_table, ref_value = v.ref_value}) + }}, + }}) + elseif v.type == 'slider' then + table.insert(nodes, + {n=G.UIT.R, config={align = "cm", minh = 1, minw = 1, padding = 0.05, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = v.label, scale = 0.5, colour = G.C.UI.TEXT_DARK}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + create_slider({w = 5, h = 0.4, ref_table = v.ref_table, ref_value = v.ref_value, min = v.min or 0, max = v.max or 1}), + }}, + }}) + elseif v.type == 'text_input' then + table.insert(nodes, + {n=G.UIT.R, config={align = "cm", minh = 1, minw = 1, padding = 0.05, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = v.label, scale = 0.5, colour = G.C.UI.TEXT_DARK}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + create_text_input(v.options), + }}, + }}) + end + end + local t = {n=G.UIT.ROOT, config = {align = "cm", minw = G.ROOM.T.w*5, minh = G.ROOM.T.h*5,padding = 0.15, r = 0.1, colour = {G.C.BLACK[1], G.C.BLACK[2], G.C.BLACK[3],0.6}}, nodes={ + {n=G.UIT.C, config={align = "cm", minh = 1,r = 0.3, padding = 0.1, minw = 1, colour = G.C.WHITE, shadow = true}, nodes={ + {n=G.UIT.C, config={align = "cm", minh = 1,r = 0.2, padding = 0.2, minw = 1, colour = G.C.CLEAR, outline = 1, outline_colour = G.C.BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes= + nodes + }, + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, hover = true, colour = G.C.ORANGE, button = "exit_overlay_menu", shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.T, config={text = "Back", scale = 0.5, colour = G.C.UI.TEXT_LIGHT}} + }} + }}, + }} + }} + }} +return t +end + +function G.UIDEF.usage_tabs() + return create_UIBox_generic_options({back_func = 'high_scores', contents ={create_tabs( + {tabs = { + { + label = localize('b_stat_jokers'), + chosen = true, + tab_definition_function = create_UIBox_usage, + tab_definition_function_args = {'joker_usage'}, + }, + { + label = localize('b_stat_consumables'), + tab_definition_function = create_UIBox_usage, + tab_definition_function_args = {'consumeable_usage'}, + }, + { + label = localize('b_stat_tarots'), + tab_definition_function = create_UIBox_usage, + tab_definition_function_args = {'consumeable_usage', 'Tarot'}, + }, + { + label = localize('b_stat_planets'), + tab_definition_function = create_UIBox_usage, + tab_definition_function_args = {'consumeable_usage', 'Planet'}, + }, + { + label = localize('b_stat_spectrals'), + tab_definition_function = create_UIBox_usage, + tab_definition_function_args = {'consumeable_usage', 'Spectral'}, + }, + { + label = localize('b_stat_vouchers'), + tab_definition_function = create_UIBox_usage, + tab_definition_function_args = {'voucher_usage', 'Voucher'}, + }, + }, + tab_h = 8, + snap_to_nav = true})}}) +end + +function create_UIBox_usage(args) + args = args or {} + _type, _set = args[1], args[2] + local used_cards = {} + local max_amt = 0 + for k, v in pairs(G.PROFILES[G.SETTINGS.profile][_type]) do + if G.P_CENTERS[k] and (not _set or G.P_CENTERS[k].set == _set) and G.P_CENTERS[k].discovered then + used_cards[#used_cards + 1] = {count = v.count, key = k} + if v.count > max_amt then max_amt = v.count end + end + end + + local _col = G.C.SECONDARY_SET[_set] or G.C.RED + + table.sort(used_cards, function (a, b) return a.count > b.count end ) + + local histograms = {} + + for i = 1, 10 do + local v = used_cards[i] + if v then + local card = Card(0,0, 0.7*G.CARD_W, 0.7*G.CARD_H, nil, G.P_CENTERS[v.key]) + card.ambient_tilt = 0.8 + local cardarea = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + G.CARD_W*0.7, + G.CARD_H*0.7, + {card_limit = 2, type = 'title', highlight_limit = 0}) + cardarea:emplace(card) + + histograms[#histograms +1] = + {n=G.UIT.C, config={align = "bm",minh = 6.2, colour = G.C.UI.TRANSPARENT_DARK, r = 0.1}, nodes={ + + {n=G.UIT.R, config={align = "bm"}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.7*G.CARD_H+0.1} , nodes={ + {n=G.UIT.O, config={object = cardarea}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.T, config={text = v.count, scale = 0.35, colour = mix_colours(G.C.FILTER, G.C.WHITE, 0.8), shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = v.count/max_amt*3.6, minw = 0.8, colour = G.C.SECONDARY_SET[G.P_CENTERS[v.key].set] or G.C.RED, res = 0.15, r = 0.001}, nodes={}}, + }}, + }}, + }} + else + histograms[#histograms +1] = + {n=G.UIT.C, config={align = "bm",minh = 6.2, colour = G.C.UI.TRANSPARENT_DARK, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "bm"}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.7*G.CARD_H+0.1, minw = 0.7*G.CARD_W} , nodes={ + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.T, config={text = '-', scale = 0.35, colour = mix_colours(G.C.FILTER, G.C.WHITE, 0.8), shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.2, minw = 0.8, colour = G.C.UI.TRANSPARENT_LIGHT, res = 0.15, r = 0.001}, nodes={}}, + }}, + }}, + }} + end + end + + local t = {n=G.UIT.ROOT, config={align = "cm", minw = 3, padding = 0.1, r = 0.1, colour = G.C.UI.TRANSPARENT_DARK}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.B, config={w=0.2,h=0.2,r =0.1,colour = G.C.FILTER}}, + {n=G.UIT.T, config={text = + _type == 'joker_usage' and localize('ph_stat_joker') or + _type == 'consumeable_usage' and localize('ph_stat_consumable') or + _type == 'voucher_usage' and localize('ph_stat_voucher'), + scale = 0.35, colour = G.C.WHITE}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes=histograms}, + }} + + return t +end + +function create_UIBox_customize_deck() + local suitTabs = {} + + local index = 1 + for i, suit in ipairs(SMODS.Suit:obj_list(true)) do + if G.COLLABS.options[suit.key] then + suitTabs[index] = { + label = localize(suit.key, 'suits_plural'), + tab_definition_function = G.UIDEF.custom_deck_tab, + tab_definition_function_args = suit.key + } + index = index + 1 + end + end + + if suitTabs[1] then + suitTabs[1].chosen = true + end + + local t = create_UIBox_generic_options({ back_func = 'options', snap_back = nil, contents = { + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + create_tabs( + {tabs = suitTabs, snap_to_nav = true, no_shoulders = true} + )}}} + }) + + return t +end + + +function G.UIDEF.custom_deck_tab(_suit) + local t = {} + + local rankCount = 0 +local lookup = {} +local options = G.COLLABS.options[_suit] +for i = 2, #options do + local skin = SMODS.DeckSkins[options[i]] + for j = 1, #skin.ranks do + if not lookup[skin.ranks[j]] then + lookup[skin.ranks[j]] = true + rankCount = rankCount + 1 + end + end +end + +local face_cards = CardArea( + 0,0, + math.min(math.max(rankCount*G.CARD_W*0.6, 4*G.CARD_W), 10*G.CARD_W), + 1.4*G.CARD_H, + {card_limit = rankCount, type = 'title', highlight_limit = 0}) + + + table.insert(t, + {n=G.UIT.R, config={align = "cm", colour = G.C.BLACK, r = 0.1, padding = 0.07, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = face_cards}} + }} + ) + + local loc_options = localize(_suit, 'collabs') + local conv_loc_options = {} + for k, v in pairs(loc_options) do + conv_loc_options[tonumber(k)] = v + end + + loc_options = conv_loc_options + + local current_option = 1 + for k, v in pairs(G.COLLABS.options[_suit]) do + if G.SETTINGS.CUSTOM_DECK.Collabs[_suit] == v then current_option = k end + end + + table.insert(t, + {n=G.UIT.R, config={align = "cm"}, nodes={ + create_option_cycle({options = loc_options, w = 5.5, cycle_shoulders = true, curr_suit = _suit, opt_callback = 'change_collab', current_option = current_option, colour = G.C.RED, focus_args = {snap_to = true, nav = 'wide'}}), + }} + ) + table.insert(t, create_toggle({label = localize('b_high_contrast_cards'), ref_table = G.SETTINGS, ref_value = 'colourblind_option', callback = G.FUNCS.refresh_contrast_mode})) + + local faces = {'K','Q','J'} + local rank = SMODS.Ranks['2'] +local cards = {} +local smodSuit = SMODS.Suits[_suit] +repeat + if lookup[rank.key] then + local card_code = smodSuit.card_key .. '_' .. rank.card_key + local card = Card(0,0, G.CARD_W*1.2, G.CARD_H*1.2, G.P_CARDS[card_code], G.P_CENTERS.c_base) + card.no_ui = true + + cards[#cards + 1] = card + end + rank = SMODS.Ranks[rank.next[1]] +until rank == SMODS.Ranks['2'] + +for i = #cards, 1, -1 do + face_cards:emplace(cards[i]) +end + + + return {n=G.UIT.ROOT, config={align = "cm", padding = 0, colour = G.C.CLEAR, r = 0.1, minw = 7, minh = 4.2}, nodes=t} +end + +function create_UIBox_high_scores() + fetch_achievements() + set_profile_progress() + set_discover_tallies() + + local scores = { + create_UIBox_high_scores_row("hand"), + create_UIBox_high_scores_row("furthest_round"), + create_UIBox_high_scores_row("furthest_ante"), + create_UIBox_high_scores_row("poker_hand"), + create_UIBox_high_scores_row("most_money"), + create_UIBox_high_scores_row("win_streak"), + } + G.focused_profile = G.SETTINGS.profile + local cheevs = {} + + local t = create_UIBox_generic_options({ back_func = 'options', snap_back = true, contents = { + {n=G.UIT.C, config={align = "cm", minw = 3, padding = 0.2, r = 0.1, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes= + scores + }, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, colour = G.C.CLEAR}, nodes={ + create_progress_box(), + UIBox_button({button = 'usage', label = {localize('k_card_stats')}, minw = 7.5, minh =1, focus_args = {nav = 'wide'}}), + }}, + not G.F_NO_ACHIEVEMENTS and {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.CLEAR}, nodes=cheevs} or nil + }}) + + return t +end + +function create_progress_box(_profile_progress, smaller) + local rows, protos = {}, {'collection', 'challenges', 'joker_stickers', 'deck_stake_wins'} + _profile_progress = _profile_progress or G.PROFILES[G.SETTINGS.profile].progress + + + _profile_progress.overall_tally, _profile_progress.overall_of = + _profile_progress.discovered.tally/_profile_progress.discovered.of + + _profile_progress.deck_stakes.tally/_profile_progress.deck_stakes.of + + _profile_progress.joker_stickers.tally/_profile_progress.joker_stickers.of + + _profile_progress.challenges.tally/_profile_progress.challenges.of, + 4 + + local text_scale = smaller and 0.7 or 1 + local bar_colour = G.PROFILES[G.focused_profile].all_unlocked and G.C.RED or G.C.BLUE + + for _, v in ipairs(protos) do + local tab, val, max = nil,nil,nil + if v == 'collection' then + tab, val, max = _profile_progress.discovered, 'tally', _profile_progress.discovered.of + elseif v == 'deck_stake_wins' then + tab, val, max = _profile_progress.deck_stakes, 'tally', _profile_progress.deck_stakes.of + elseif v == 'joker_stickers' then + tab, val, max = _profile_progress.joker_stickers, 'tally', _profile_progress.joker_stickers.of + elseif v == 'challenges' then + tab, val, max = _profile_progress.challenges, 'tally', _profile_progress.challenges.of + end + local filling = v == 'collection' and { + {n=G.UIT.O, config={object = DynaText({string = {math.floor(0.01+100*_profile_progress.discovered.tally/_profile_progress.discovered.of)..'%'}, colours = {G.C.WHITE},shadow = true, float = true, scale = 0.55*text_scale})}}, + {n=G.UIT.T, config={text = " (".._profile_progress.discovered.tally..'/'.._profile_progress.discovered.of..")", scale = 0.35, colour = G.C.JOKER_GREY}} + } or v == 'deck_stake_wins' and { + {n=G.UIT.O, config={object = DynaText({string = {math.floor(0.01+100*_profile_progress.deck_stakes.tally/_profile_progress.deck_stakes.of)..'%'}, colours = {G.C.WHITE},shadow = true, float = true, scale = 0.55*text_scale})}}, + {n=G.UIT.T, config={text = " (".._profile_progress.deck_stakes.tally..'/'.._profile_progress.deck_stakes.of..")", scale = 0.35, colour = G.C.JOKER_GREY}} + } or v == 'joker_stickers' and { + {n=G.UIT.O, config={object = DynaText({string = {math.floor(0.01+100*_profile_progress.joker_stickers.tally/_profile_progress.joker_stickers.of)..'%'}, colours = {G.C.WHITE},shadow = true, float = true, scale = 0.55*text_scale})}}, + {n=G.UIT.T, config={text = " (".._profile_progress.joker_stickers.tally..'/'.._profile_progress.joker_stickers.of..")", scale = 0.35, colour = G.C.JOKER_GREY}} + } or v == 'challenges' and { + {n=G.UIT.O, config={object = DynaText({string = {math.floor(0.01+100*_profile_progress.challenges.tally/_profile_progress.challenges.of)..'%'}, colours = {G.C.WHITE},shadow = true, float = true, scale = 0.55*text_scale})}}, + {n=G.UIT.T, config={text = " (".._profile_progress.challenges.tally..'/'.._profile_progress.challenges.of..")", scale = 0.35, colour = G.C.JOKER_GREY}} + } + + rows[#rows+1] = {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = darken(G.C.JOKER_GREY, 0.1), emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 3.5*text_scale, maxw = 3.5*text_scale}, nodes={ + {n=G.UIT.T, config={text = localize('k_'..v), scale = 0.5*text_scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.C, config={align = "cl", minh =smaller and 0.5 or 0.8, r = 0.1, minw = 3.5*text_scale, colour = G.C.BLACK, emboss = 0.05, + progress_bar = { + max = max, ref_table = tab, ref_value = val, empty_col = G.C.BLACK, filled_col = adjust_alpha(bar_colour, 0.5) + }}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, minw = 3.5*text_scale}, nodes=filling}, + }}, + }} + end + + return {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = 3.5*text_scale, maxw = 3.5*text_scale}, nodes={ + {n=G.UIT.T, config={text = localize('k_progress'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.C, config={align = "cl", minh = 0.6, r = 0.1, minw = 3.5*text_scale, colour = G.C.BLACK, emboss = 0.05, + progress_bar = { + max = _profile_progress.overall_of, ref_table = _profile_progress, ref_value = 'overall_tally', empty_col = G.C.BLACK, filled_col = adjust_alpha(bar_colour, 0.5) + }}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, minw = 3.5*text_scale}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {math.floor(0.01+100*_profile_progress.overall_tally/_profile_progress.overall_of)..'%'}, colours = {G.C.WHITE},shadow = true, float = true, scale = 0.55})}}, + }}, + }} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes=rows}, + }} +end + +function create_UIBox_high_scores_row(score) + if not G.PROFILES[G.SETTINGS.profile].high_scores[score] then return nil end + local label_scale = 0.65 - 0.025*math.max(string.len(G.PROFILES[G.SETTINGS.profile].high_scores[score].label)-8, 0) + local label_w, score_w, h = 3.5, 4, 0.8 + local score_tab = {} + + if score == 'poker_hand' then + local handname, amount = localize('k_none'), 0 + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].hand_usage) do if v.count > amount then handname = v.order; amount = v.count end end + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {amount < 1 and handname or localize(handname,'poker_hands')}, colours = {G.C.WHITE},shadow = true, float = true, scale = 0.55})}}, + {n=G.UIT.T, config={text = " ("..amount..")", scale = 0.45, colour = G.C.JOKER_GREY}} + } + elseif score == 'most_money' then + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {localize('$')..number_format(G.PROFILES[G.SETTINGS.profile].high_scores[score].amt)}, colours = {G.C.MONEY},shadow = true, float = true, scale = score_number_scale(0.85, G.PROFILES[G.SETTINGS.profile].high_scores[score].amt)})}}, + } + elseif score == 'win_streak' then + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {number_format(G.PROFILES[G.SETTINGS.profile].high_scores[score].amt)}, colours = {G.C.WHITE},shadow = true, float = true, scale = score_number_scale(0.85, G.PROFILES[G.SETTINGS.profile].high_scores[score].amt)})}}, + {n=G.UIT.T, config={text = " ("..G.PROFILES[G.SETTINGS.profile].high_scores["current_streak"].amt..")", scale = 0.45, colour = G.C.JOKER_GREY}} + } + elseif score == 'hand' then + local chip_sprite = Sprite(0,0,0.4,0.4,G.ASSET_ATLAS["ui_"..(G.SETTINGS.colourblind_option and 2 or 1)], {x=0, y=0}) + chip_sprite.states.drag.can = false + score_tab = { + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={w=0.4,h=0.4 , object = chip_sprite}} + }}, + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {number_format(G.PROFILES[G.SETTINGS.profile].high_scores[score].amt)}, colours = {G.C.RED},shadow = true, float = true, scale = math.min(0.75, score_number_scale(1.5, G.PROFILES[G.SETTINGS.profile].high_scores[score].amt))})}}, + }}, + } + elseif score == 'collection' then + score_tab = { + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {'%'..math.floor(0.01+100*G.PROFILES[G.SETTINGS.profile].high_scores[score].amt/G.PROFILES[G.SETTINGS.profile].high_scores[score].tot)}, colours = {G.C.WHITE},shadow = true, float = true, scale = math.min(0.75, score_number_scale(1.5, G.PROFILES[G.SETTINGS.profile].high_scores[score].amt))})}}, + {n=G.UIT.T, config={text = " ("..G.PROFILES[G.SETTINGS.profile].high_scores[score].amt..'/'..G.PROFILES[G.SETTINGS.profile].high_scores[score].tot..")", scale = 0.45, colour = G.C.JOKER_GREY}} + }}, + } + else + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {number_format(G.PROFILES[G.SETTINGS.profile].high_scores[score].amt)}, colours = {G.C.FILTER},shadow = true, float = true, scale = score_number_scale(0.85, G.PROFILES[G.SETTINGS.profile].high_scores[score].amt)})}}, + } + end + return {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = darken(G.C.JOKER_GREY, 0.1), emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, minw = label_w, maxw = label_w}, nodes={ + {n=G.UIT.T, config={text = localize(score, 'high_scores'), scale = label_scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.C, config={align = "cl", minh = h, r = 0.1, minw = score_w, colour = G.C.BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, minw = score_w, maxw = score_w}, nodes=score_tab}, + }}, + }} +end + +function create_UIBox_win() + local show_lose_cta = false + local eased_green = copy_table(G.C.GREEN) + eased_green[4] = 0 + ease_value(eased_green, 4, 0.5, nil, nil, true) + local t = create_UIBox_generic_options({ padding = 0, bg_colour = eased_green , colour = G.C.BLACK, outline_colour = G.C.EDITION, no_back = true, no_esc = true, contents = { + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('ph_you_win')}, colours = {G.C.EDITION},shadow = true, float = true, spacing = 10, rotate = true, scale = 1.5, pop_in = 0.4, maxw = 6.5})}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.15}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.08}, nodes={ + create_UIBox_round_scores_row('hand'), + create_UIBox_round_scores_row('poker_hand'), + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.08}, nodes={ + create_UIBox_round_scores_row('cards_played', G.C.BLUE), + create_UIBox_round_scores_row('cards_discarded', G.C.RED), + create_UIBox_round_scores_row('cards_purchased', G.C.MONEY), + create_UIBox_round_scores_row('times_rerolled', G.C.GREEN), + create_UIBox_round_scores_row('new_collection', G.C.WHITE), + create_UIBox_round_scores_row('seed', G.C.WHITE), + UIBox_button({button = 'copy_seed', label = {localize('b_copy')}, colour = G.C.BLUE, scale = 0.3, minw = 2.3, minh = 0.4,}), + }}, + {n=G.UIT.C, config={align = "tr", padding = 0.08}, nodes={ + create_UIBox_round_scores_row('furthest_ante', G.C.FILTER), + create_UIBox_round_scores_row('furthest_round', G.C.FILTER), + {n=G.UIT.R, config={align = "cm", minh = 0.4, minw = 0.1}, nodes={}}, + show_win_cta and UIBox_button({id = 'win_cta', button = 'show_main_cta', label = {localize('b_next')}, colour = G.C.GREEN, scale = 0.8, minw = 2.5, minh = 2.5, focus_args = {nav = 'wide', snap_to = true}}) or nil, + not show_win_cta and UIBox_button({id = 'from_game_won', button = 'notify_then_setup_run', label = {localize('b_start_new_run')}, minw = 2.5, maxw = 2.5, minh = 1, focus_args = {nav = 'wide', snap_to = true}}) or nil, + not show_win_cta and {n=G.UIT.R, config={align = "cm", minh = 0.2, minw = 0.1}, nodes={}} or nil, + not show_win_cta and UIBox_button({button = 'go_to_menu', label = {localize('b_main_menu')}, minw = 2.5, maxw = 2.5, minh = 1, focus_args = {nav = 'wide'}}) or nil, + }} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.08}, nodes={ + UIBox_button({button = 'exit_overlay_menu', label = {localize('b_endless')}, minw = 6.5, maxw = 5, minh = 1.2, scale = 0.7, shadow = true, colour = G.C.BLUE, focus_args = {nav = 'wide', button = 'x',set_button_pip = true}}), + }}, + }} + }} + }}) + t.nodes[1] = {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 2}, nodes={ + {n=G.UIT.O, config={padding = 0, id = 'jimbo_spot', object = Moveable(0,0,G.CARD_W*1.1, G.CARD_H*1.1)}}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={t.nodes[1]} + }} + } + --t.nodes[1].config.mid = true + t.config.id = 'you_win_UI' + return t +end + +function create_UIBox_exit_CTA() + + local t = create_UIBox_generic_options({ back_label = 'Quit Game', back_func = 'quit' , colour = G.C.BLACK, back_colour = G.C.RED, padding = 0, contents = { + {n=G.UIT.C, config={align = "tm", padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('ph_demo_thanks_1')}, colours = {G.C.WHITE},shadow = true, float = true, scale = 0.9})}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('ph_demo_thanks_2')}, colours = {G.C.WHITE},shadow = true, bump = true, rotate = true, pop_in = 0.2, scale = 1.4})}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0.12, emboss = 0.1, colour = G.C.L_BLACK, r = 0.1}, nodes={ + simple_text_container('ml_demo_thanks_message',{colour = G.C.UI.TEXT_LIGHT, scale = 0.55, shadow = true}), + {n=G.UIT.R, config={align = "cm", padding = 0.2}, nodes={ + UIBox_button({button = 'wishlist_steam', label = {localize('b_wishlist')}, minw = 5.9, minh = 1.1, scale = 0.5, shadow = true, colour = G.C.GREEN, focus_args = {nav = 'wide', button = 'x',set_button_pip = true, snap_to = true}}), + UIBox_button({button = 'go_to_playbalatro', label = {localize('b_playbalatro')}, minw = 4.9, minh = 0.8, scale = 0.4, shadow = true, colour = G.C.BLUE, focus_args = {nav = 'wide', button = 'y',set_button_pip = true}}), + }}, + }}, + }} + }}) + t.nodes[2] = t.nodes[1] + t.nodes[1] = {n=G.UIT.C, config={align = "cm", padding = 2}, nodes={ + {n=G.UIT.O, config={padding = 0, id = 'jimbo_spot', object = Moveable(0,0,G.CARD_W*1.1, G.CARD_H*1.1)}}, + }} + --t.nodes[1].config.mid = true + return t +end + +function create_UIBox_small_cta() + return {n=G.UIT.ROOT, config={align = "cm", minw = 4, minh = 3}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('ph_demo_thanks_1')}, colours = {G.C.WHITE},shadow = true, float = true, scale = 0.3})}}, + }} +end + +function create_UIBox_demo_video_CTA() + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 21.7, + blockable = false, + blocking = false, + func = (function() + G.exception_queue = 'other' + G.FUNCS.go_to_demo_cta() + G.exception_queue = nil + return true + end)}), 'other') + + RESTART_MUSIC() + + local video_file = love.graphics.newVideo('resources/democta.ogv') + local vid_sprite = Sprite(0,0,11*16/9,11,G.ASSET_ATLAS["ui_"..(G.SETTINGS.colourblind_option and 2 or 1)], {x=0, y=0}) + video_file:getSource():setVolume(G.SETTINGS.SOUND.volume*G.SETTINGS.SOUND.game_sounds_volume/(100*100)) + vid_sprite.video = video_file + video_file:play() + + local t = create_UIBox_generic_options({ back_delay = 7, back_label = localize('b_skip'), back_func = 'go_to_demo_cta' , colour = G.C.BLACK, padding = 0, contents = { + {n=G.UIT.O, config={object = vid_sprite}}, + }}) + return t +end + +function create_UIBox_game_over() + local show_lose_cta = false + + + local eased_red = copy_table(G.GAME.round_resets.ante <= G.GAME.win_ante and G.C.RED or G.C.BLUE) + eased_red[4] = 0 + ease_value(eased_red, 4, 0.8, nil, nil, true) + local t = create_UIBox_generic_options({ bg_colour = eased_red ,no_back = true, padding = 0, contents = { + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('ph_game_over')}, colours = {G.C.RED},shadow = true, float = true, scale = 1.5, pop_in = 0.4, maxw = 6.5})}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.15}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05, colour = G.C.BLACK, emboss = 0.05, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.08}, nodes={ + create_UIBox_round_scores_row('hand'), + create_UIBox_round_scores_row('poker_hand'), + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.08}, nodes={ + create_UIBox_round_scores_row('cards_played', G.C.BLUE), + create_UIBox_round_scores_row('cards_discarded', G.C.RED), + create_UIBox_round_scores_row('cards_purchased', G.C.MONEY), + create_UIBox_round_scores_row('times_rerolled', G.C.GREEN), + create_UIBox_round_scores_row('new_collection', G.C.WHITE), + create_UIBox_round_scores_row('seed', G.C.WHITE), + UIBox_button({button = 'copy_seed', label = {localize('b_copy')}, colour = G.C.BLUE, scale = 0.3, minw = 2.3, minh = 0.4, focus_args = {nav = 'wide'}}), + }}, + {n=G.UIT.C, config={align = "tr", padding = 0.08}, nodes={ + create_UIBox_round_scores_row('furthest_ante', G.C.FILTER), + create_UIBox_round_scores_row('furthest_round', G.C.FILTER), + create_UIBox_round_scores_row('defeated_by'), + }} + }} + }}, + show_lose_cta and + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={id = 'lose_cta', align = "cm", minw = 5, padding = 0.1, r = 0.1, hover = true, colour = G.C.GREEN, button = "show_main_cta", shadow = true}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.T, config={text = localize('b_next'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT, focus_args = {nav = 'wide', snap_to = true}}} + }} + }} + }} or + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.R, config={id = 'from_game_over', align = "cm", minw = 5, padding = 0.1, r = 0.1, hover = true, colour = G.C.RED, button = "notify_then_setup_run", shadow = true, focus_args = {nav = 'wide', snap_to = true}}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true, maxw = 4.8}, nodes={ + {n=G.UIT.T, config={text = localize('b_start_new_run'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT}} + }} + }}, + {n=G.UIT.R, config={align = "cm", minw = 5, padding = 0.1, r = 0.1, hover = true, colour = G.C.RED, button = "go_to_menu", shadow = true, focus_args = {nav = 'wide'}}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true, maxw = 4.8}, nodes={ + {n=G.UIT.T, config={text = localize('b_main_menu'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT}} + }} + }} + }} + }}, + }} +}}) + t.nodes[1] = {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 2}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={padding = 0, id = 'jimbo_spot', object = Moveable(0,0,G.CARD_W*1.1, G.CARD_H*1.1)}}, + }}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={t.nodes[1]}}} +} + + --t.nodes[1].config.mid = true + return t +end + +function create_UIBox_round_scores_row(score, text_colour) + local label = G.GAME.round_scores[score] and localize('ph_score_'..score) or '' + local check_high_score = false + local score_tab = {} + local label_w, score_w, h = ({hand=true,poker_hand=true})[score] and 3.5 or 2.9, ({hand=true,poker_hand=true})[score] and 3.5 or 1, 0.5 + + if score == 'furthest_ante' then + label_w = 1.9 + check_high_score = true + label = localize('k_ante') + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {number_format(G.GAME.round_resets.ante)}, colours = {text_colour or G.C.FILTER},shadow = true, float = true, scale = 0.45})}}, + } + end + if score == 'furthest_round' then + label_w = 1.9 + check_high_score = true + label = localize('k_round') + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {number_format(G.GAME.round)}, colours = {text_colour or G.C.FILTER},shadow = true, float = true, scale = 0.45})}}, + } + end + if score == 'seed' then + label_w = 1.9 + score_w = 1.9 + label = localize('k_seed') + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {G.GAME.pseudorandom.seed}, colours = {text_colour or G.C.WHITE},shadow = true, float = true, scale = 0.45})}}, + } + end + if score == 'defeated_by' then + label = localize('k_defeated_by') + local blind_choice = {config = G.GAME.blind.config.blind or G.P_BLINDS.bl_small} + blind_choice.animation = AnimatedSprite(0,0, 1.4, 1.4, G.ANIMATION_ATLAS[blind_choice.config.atlas] or G.ANIMATION_ATLAS['blind_chips'], blind_choice.config.pos) + blind_choice.animation:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'} + }) + + score_tab = { + {n=G.UIT.R, config={align = "cm", minh = 0.6}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = localize{type ='name_text', key = blind_choice.config.key, set = 'Blind'}, colours = {G.C.WHITE},shadow = true, float = true,maxw = 2.2, scale = 0.45})}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.O, config={object = blind_choice.animation}} + }}, + } + end + + local label_scale = 0.5 + + if score == 'poker_hand' then + local handname, amount = localize('k_none'), 0 + for k, v in pairs(G.GAME.hand_usage) do if v.count > amount then handname = v.order; amount = v.count end end + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {amount <1 and handname or localize(handname,'poker_hands')}, colours = {text_colour or G.C.WHITE},shadow = true, float = true, scale = 0.45, maxw = 2.5})}}, + {n=G.UIT.T, config={text = " ("..amount..")", scale = 0.35, colour = G.C.JOKER_GREY}} + } + elseif score == 'hand' then + check_high_score = true + local chip_sprite = Sprite(0,0,0.3,0.3,G.ASSET_ATLAS["ui_"..(G.SETTINGS.colourblind_option and 2 or 1)], {x=0, y=0}) + chip_sprite.states.drag.can = false + score_tab = { + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={w=0.3,h=0.3 , object = chip_sprite}} + }}, + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {number_format(G.GAME.round_scores[score].amt)}, colours = {text_colour or G.C.RED},shadow = true, float = true, scale = math.min(0.6, score_number_scale(1.2, G.GAME.round_scores[score].amt))})}}, + }}, + } + elseif G.GAME.round_scores[score] and not score_tab[1] then + score_tab = { + {n=G.UIT.O, config={object = DynaText({string = {number_format(G.GAME.round_scores[score].amt)}, colours = {text_colour or G.C.FILTER},shadow = true, float = true, scale = score_number_scale(0.6, G.GAME.round_scores[score].amt)})}}, + } + end + return {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = darken(G.C.JOKER_GREY, 0.1), emboss = 0.05, func = check_high_score and 'high_score_alert' or nil, id = score}, nodes={ + {n=score=='defeated_by' and G.UIT.R or G.UIT.C, config={align = "cm", padding = 0.02, minw = label_w, maxw = label_w}, nodes={ + {n=G.UIT.T, config={text = label, scale = label_scale, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=score=='defeated_by' and G.UIT.R or G.UIT.C, config={align = "cr"}, nodes={ + {n=G.UIT.C, config={align = "cm", minh = h, r = 0.1, minw = score=='defeated_by' and label_w or score_w, colour = (score == 'seed' and G.GAME.seeded) and G.C.RED or G.C.BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, minw = score_w}, nodes=score_tab}, + }} + }}, + }} +end + +function create_UIBox_hand_tip(handname) + if not G.GAME.hands[handname].example then return {n=G.UIT.R, config={align = "cm"},nodes = {}} end + local cardarea = CardArea( + 2,2, + 3.5*G.CARD_W, + 0.75*G.CARD_H, + {card_limit = 5, type = 'title', highlight_limit = 0}) + for k, v in ipairs(G.GAME.hands[handname].example) do + local card = Card(0,0, 0.5*G.CARD_W, 0.5*G.CARD_H, G.P_CARDS[v[1]], G.P_CENTERS[v[3] or 'c_base']) + if v[2] then card:juice_up(0.3, 0.2) end + if k == 1 then play_sound('paper1',0.95 + math.random()*0.1, 0.3) end + ease_value(card.T, 'scale',v[2] and 0.25 or -0.15,nil,'REAL',true,0.2) + cardarea:emplace(card) + end + + return {n=G.UIT.R, config={align = "cm", colour = G.C.WHITE, r = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = cardarea}} + }} + }} +end + +function create_UIBox_current_hand_row(handname, simple) + return (G.GAME.hands[handname].visible) and + (not simple and + {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = darken(G.C.JOKER_GREY, 0.1), emboss = 0.05, hover = true, force_focus = true, on_demand_tooltip = {text = localize(handname, 'poker_hand_descriptions'), filler = {func = create_UIBox_hand_tip, args = handname}}}, nodes={ + {n=G.UIT.C, config={align = "cl", padding = 0, minw = 5}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.01, r = 0.1, colour = G.C.HAND_LEVELS[math.min(7, G.GAME.hands[handname].level)], minw = 1.5, outline = 0.8, outline_colour = G.C.WHITE}, nodes={ + {n=G.UIT.T, config={text = localize('k_level_prefix')..G.GAME.hands[handname].level, scale = 0.5, colour = G.C.UI.TEXT_DARK}} + }}, + {n=G.UIT.C, config={align = "cm", minw = 4.5, maxw = 4.5}, nodes={ + {n=G.UIT.T, config={text = ' '..localize(handname,'poker_hands'), scale = 0.45, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }} + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.BLACK,r = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cr", padding = 0.01, r = 0.1, colour = G.C.CHIPS, minw = 1.1}, nodes={ + {n=G.UIT.T, config={text = number_format(G.GAME.hands[handname].chips, 1000000), scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}, + {n=G.UIT.B, config={w = 0.08, h = 0.01}} + }}, + {n=G.UIT.T, config={text = "X", scale = 0.45, colour = G.C.MULT}}, + {n=G.UIT.C, config={align = "cl", padding = 0.01, r = 0.1, colour = G.C.MULT, minw = 1.1}, nodes={ + {n=G.UIT.B, config={w = 0.08,h = 0.01}}, + {n=G.UIT.T, config={text = number_format(G.GAME.hands[handname].mult, 1000000), scale = 0.45, colour = G.C.UI.TEXT_LIGHT}} + }} + }}, + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = ' #', scale = 0.45, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.L_BLACK,r = 0.1, minw = 0.9}, nodes={ + {n=G.UIT.T, config={text = G.GAME.hands[handname].played, scale = 0.45, colour = G.C.FILTER, shadow = true}}, + }} + }} + or {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = darken(G.C.JOKER_GREY, 0.1), force_focus = true, emboss = 0.05, hover = true, on_demand_tooltip = {text = localize(handname, 'poker_hand_descriptions'), filler = {func = create_UIBox_hand_tip, args = handname}}, focus_args = {snap_to = (simple and handname == 'Straight Flush')}}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0, minw = 5}, nodes={ + {n=G.UIT.T, config={text = localize(handname,'poker_hands'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }} + }}) + or nil +end + +function create_UIBox_current_hands(simple) + local hands = { + create_UIBox_current_hand_row("Flush Five", simple), + create_UIBox_current_hand_row("Flush House", simple), + create_UIBox_current_hand_row("Five of a Kind", simple), + create_UIBox_current_hand_row("Straight Flush", simple), + create_UIBox_current_hand_row("Four of a Kind", simple), + create_UIBox_current_hand_row("Full House", simple), + create_UIBox_current_hand_row("Flush", simple), + create_UIBox_current_hand_row("Straight", simple), + create_UIBox_current_hand_row("Three of a Kind", simple), + create_UIBox_current_hand_row("Two Pair", simple), + create_UIBox_current_hand_row("Pair", simple), + create_UIBox_current_hand_row("High Card", simple) + } + + local t = {n=G.UIT.ROOT, config={align = "cm", minw = 3, padding = 0.1, r = 0.1, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.04}, nodes= + hands + }, + }} + + return t +end + +function G.UIDEF.deck_info(_show_remaining) + return create_UIBox_generic_options({contents ={create_tabs( + {tabs = _show_remaining and { + { + label = localize('b_remaining'), + chosen = true, + tab_definition_function = G.UIDEF.view_deck, + tab_definition_function_args = true, + }, + { + label = localize('b_full_deck'), + tab_definition_function = G.UIDEF.view_deck + }, + } or { + { + label = localize('b_full_deck'), + chosen = true, + tab_definition_function = G.UIDEF.view_deck + }, + }, + tab_h = 8, + snap_to_nav = true})}}) +end + +function G.UIDEF.run_info() + return create_UIBox_generic_options({contents ={create_tabs( + {tabs = { + { + label = localize('b_poker_hands'), + chosen = true, + tab_definition_function = create_UIBox_current_hands, + }, + { + label = localize('b_blinds'), + tab_definition_function = G.UIDEF.current_blinds, + }, + { + label = localize('b_vouchers'), + tab_definition_function = G.UIDEF.used_vouchers, + }, + G.GAME.stake > 1 and { + label = localize('b_stake'), + tab_definition_function = G.UIDEF.current_stake, + } or nil, + }, + tab_h = 8, + snap_to_nav = true})}}) +end + +function G.UIDEF.current_blinds() + return {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR, padding = 0.2}, nodes={ + G.GAME.round_resets.blind_states['Small'] ~= 'Hide' and {n=G.UIT.C, config={align = "tm", padding = 0.1, outline = 2, r = 0.1, line_emboss = 0.2, outline_colour = G.C.BLACK}, nodes={ + create_UIBox_blind_choice('Small', true) + }} or nil, + G.GAME.round_resets.blind_states['Big'] ~= 'Hide' and {n=G.UIT.C, config={align = "tm", padding = 0.1, outline = 2, r = 0.1, line_emboss = 0.2, outline_colour = G.C.BLACK}, nodes={ + create_UIBox_blind_choice('Big', true) + }} or nil, + G.GAME.round_resets.blind_states['Boss'] ~= 'Hide' and {n=G.UIT.C, config={align = "tm", padding = 0.1, outline = 2, r = 0.1, line_emboss = 0.2, outline_colour = G.C.BLACK}, nodes={ + create_UIBox_blind_choice('Boss', true) + }} or nil + }} +end + +function G.UIDEF.deck_stake_column(_deck_key) + local deck_usage = G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key] + local stake_col = {} + local valid_option = nil + for i = #G.P_CENTER_POOLS['Stake'], 1, -1 do + local _wins = deck_usage and deck_usage.wins[i] or 0 + if (deck_usage and deck_usage.wins[i-1]) or i == 1 or G.PROFILES[G.SETTINGS.profile].all_unlocked then valid_option = true end + stake_col[#stake_col+1] = {n=G.UIT.R, config={id = i, align = "cm", colour = _wins > 0 and G.C.GREY or G.C.CLEAR, outline = 0, outline_colour = G.C.WHITE, r = 0.1, minh = 0.25, minw = valid_option and 0.45 or 0.25, func = 'RUN_SETUP_check_back_stake_highlight'}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = valid_option and 0.17 or 0.13, minw = valid_option and 0.37 or 0.13, colour = _wins > 0 and get_stake_col(i) or G.C.UI.TRANSPARENT_LIGHT, r = 0.1},nodes={}} + }} + if i > 1 then stake_col[#stake_col+1] = {n=G.UIT.R, config={align = "cm", minh = 0.1, minw = 0.04},nodes={}} end + end + return {n=G.UIT.ROOT, config={align = 'cm', colour = G.C.CLEAR}, nodes =stake_col} +end + +function G.UIDEF.current_stake() + local other_col = nil + if G.GAME.stake > 2 then + local stake_desc_rows = {{n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = localize('k_also_applied'), scale = 0.4, colour = G.C.WHITE}} + }} + } + if false then + for i = G.GAME.stake-1, 2, -1 do + local _stake_desc = {} + local _stake_center = G.P_CENTER_POOLS.Stake[i] + localize{type = 'descriptions', key = _stake_center.key, set = _stake_center.set, nodes = _stake_desc} + local _full_desc = {} + for k, v in ipairs(_stake_desc) do + _full_desc[#_full_desc+1] = {n=G.UIT.R, config={align = "cm"}, nodes=v} + end + _full_desc[#_full_desc] = nil + stake_desc_rows[#stake_desc_rows+1] = {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = 'cm'}, nodes ={{n=G.UIT.C, config={align = "cm", colour = get_stake_col(i), r = 0.1, minh = 0.35, minw = 0.35, emboss = 0.05}, nodes={}}, {n=G.UIT.B, config={w=0.1,h=0.1}}}}, + {n=G.UIT.C, config={align = "cm", padding = 0.03, colour = G.C.WHITE, r = 0.1, minh = 0.7, minw = 4.8}, nodes=_full_desc}, + }} + end + end SMODS.applied_stakes_UI(G.GAME.stake, stake_desc_rows) + other_col = {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, colour = G.C.L_BLACK}, nodes=stake_desc_rows} + end + + local stake_sprite = get_stake_sprite(G.GAME.stake, 0.8) + local _stake_desc = {} + local _stake_center = G.P_CENTER_POOLS.Stake[G.GAME.stake] + localize{type = 'descriptions', key = _stake_center.key, set = _stake_center.set, nodes = _stake_desc} + local _full_desc = {} + for k, v in ipairs(_stake_desc) do + _full_desc[#_full_desc+1] = {n=G.UIT.R, config={align = "cm"}, nodes=v} + end + _full_desc[#_full_desc] = nil + local current_col = {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1, minw = 4}, nodes={ + {n=G.UIT.O, config={colour = G.C.BLUE, object = stake_sprite, hover = true, can_collide = false}}, + {n=G.UIT.T, config={text = localize{type = 'name_text', key = G.P_CENTER_POOLS.Stake[G.GAME.stake].key, set = 'Stake'}, scale = 0.45, colour = G.C.WHITE}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05, colour = get_stake_col(G.GAME.stake), r = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05, colour = G.C.WHITE, r = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.03, minh = 0.7, minw = 3.8}, nodes=_full_desc} + }} + }} + }} + + return {n=G.UIT.ROOT, config={align = "cm", colour = G.C.BLACK, r = 0.1, padding = 0.1}, nodes={ + current_col,other_col + }} +end + +function G.UIDEF.view_deck(unplayed_only) + local deck_tables = {} + remove_nils(G.playing_cards) + G.VIEWING_DECK = true + table.sort(G.playing_cards, function (a, b) return a:get_nominal('suit') > b:get_nominal('suit') end ) + local SUITS = { + Spades = {}, + Hearts = {}, + Clubs = {}, + Diamonds = {}, + } + local suit_map = {'Spades', 'Hearts', 'Clubs', 'Diamonds'} + local SUITS_SORTED = Cartomancer.tablecopy(SUITS) + for k, v in ipairs(G.playing_cards) do + local greyed + if unplayed_only and not ((v.area and v.area == G.deck) or v.ability.wheel_flipped) then + greyed = true + end + local card_string = v:cart_to_string() + if greyed then + card_string = card_string .. "Greyed" + end + if greyed and Cartomancer.SETTINGS.deck_view_hide_drawn_cards then + -- Ignore this card. + elseif not SUITS[v.base.suit][card_string] then + table.insert(SUITS_SORTED[v.base.suit], card_string) + + local _scale = 0.7 + local copy = copy_card(v, nil, _scale) + + copy.greyed = greyed + copy.stacked_quantity = 1 + + SUITS[v.base.suit][card_string] = copy + else + local stacked_card = SUITS[v.base.suit][card_string] + stacked_card.stacked_quantity = stacked_card.stacked_quantity + 1 + end + end + for j = 1, 4 do + if SUITS_SORTED[suit_map[j]][1] then + local view_deck = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + 6.5*G.CARD_W, + 0.6*G.CARD_H, + {card_limit = #SUITS_SORTED[suit_map[j]], type = 'title', view_deck = true, highlight_limit = 0, card_w = G.CARD_W*0.7, draw_layers = {'card'}}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = view_deck}} + }} + ) + + for i = 1, #SUITS_SORTED[suit_map[j]] do + local card_string = SUITS_SORTED[suit_map[j]][i] + local card = SUITS[suit_map[j]][card_string] + + card.T.x = view_deck.T.x + view_deck.T.w/2 + card.T.y = view_deck.T.y + card:create_quantity_display() + + card:hard_set_T() + view_deck:emplace(card) + + end + + end + end + + local flip_col = G.C.WHITE + + local suit_tallies = {['Spades'] = 0, ['Hearts'] = 0, ['Clubs'] = 0, ['Diamonds'] = 0} + local mod_suit_tallies = {['Spades'] = 0, ['Hearts'] = 0, ['Clubs'] = 0, ['Diamonds'] = 0} + local rank_tallies = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + local mod_rank_tallies = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} + local rank_name_mapping = {2, 3, 4, 5, 6, 7, 8, 9, 10, 'J', 'Q', 'K', 'A'} + local face_tally = 0 + local mod_face_tally = 0 + local num_tally = 0 + local mod_num_tally = 0 + local ace_tally = 0 + local mod_ace_tally = 0 + local wheel_flipped = 0 + + for k, v in ipairs(G.playing_cards) do + if v.ability.name ~= 'Stone Card' and (not unplayed_only or ((v.area and v.area == G.deck) or v.ability.wheel_flipped)) then + if v.ability.wheel_flipped and unplayed_only then wheel_flipped = wheel_flipped + 1 end + --For the suits + suit_tallies[v.base.suit] = (suit_tallies[v.base.suit] or 0) + 1 + mod_suit_tallies['Spades'] = (mod_suit_tallies['Spades'] or 0) + (v:is_suit('Spades') and 1 or 0) + mod_suit_tallies['Hearts'] = (mod_suit_tallies['Hearts'] or 0) + (v:is_suit('Hearts') and 1 or 0) + mod_suit_tallies['Clubs'] = (mod_suit_tallies['Clubs'] or 0) + (v:is_suit('Clubs') and 1 or 0) + mod_suit_tallies['Diamonds'] = (mod_suit_tallies['Diamonds'] or 0) + (v:is_suit('Diamonds') and 1 or 0) + + --for face cards/numbered cards/aces + local card_id = v:get_id() + face_tally = face_tally + ((card_id ==11 or card_id ==12 or card_id ==13) and 1 or 0) + mod_face_tally = mod_face_tally + (v:is_face() and 1 or 0) + if card_id > 1 and card_id < 11 then + num_tally = num_tally + 1 + if not v.debuff then mod_num_tally = mod_num_tally + 1 end + end + if card_id == 14 then + ace_tally = ace_tally + 1 + if not v.debuff then mod_ace_tally = mod_ace_tally + 1 end + end + + --ranks + rank_tallies[card_id - 1] = rank_tallies[card_id - 1] + 1 + if not v.debuff then mod_rank_tallies[card_id - 1] = mod_rank_tallies[card_id - 1] + 1 end + end + end + + local modded = (face_tally ~= mod_face_tally) or + (mod_suit_tallies['Spades'] ~= suit_tallies['Spades']) or + (mod_suit_tallies['Hearts'] ~= suit_tallies['Hearts']) or + (mod_suit_tallies['Clubs'] ~= suit_tallies['Clubs']) or + (mod_suit_tallies['Diamonds'] ~= suit_tallies['Diamonds']) + + if wheel_flipped > 0 then flip_col = mix_colours(G.C.FILTER, G.C.WHITE,0.7) end + + local rank_cols = {} + for i = 13, 1, -1 do + local mod_delta = mod_rank_tallies[i] ~= rank_tallies[i] + rank_cols[#rank_cols+1] = {n=G.UIT.R, config={align = "cm", padding = 0.07}, nodes={ + {n=G.UIT.C, config={align = "cm", r = 0.1, padding = 0.04, emboss = 0.04, minw = 0.5, colour = G.C.L_BLACK}, nodes={ + {n=G.UIT.T, config={text = rank_name_mapping[i],colour = G.C.JOKER_GREY, scale = 0.35, shadow = true}}, + }}, + {n=G.UIT.C, config={align = "cr", minw = 0.4}, nodes={ + mod_delta and {n=G.UIT.O, config={object = DynaText({string = {{string = ''..rank_tallies[i], colour = flip_col},{string =''..mod_rank_tallies[i], colour = G.C.BLUE}}, colours = {G.C.RED}, scale = 0.4, y_offset = -2, silent = true, shadow = true, pop_in_rate = 10, pop_delay = 4})}} or + {n=G.UIT.T, config={text = rank_tallies[i] or 'NIL',colour = flip_col, scale = 0.45, shadow = true}}, + }} + }} + end + + + local t = + {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={}}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = 1.5, minh = 2, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1, colour = G.C.L_BLACK, emboss = 0.05, padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = G.GAME.selected_back.loc_name, colours = {G.C.WHITE}, bump = true, rotate = true, shadow = true, scale = 0.6 - string.len(G.GAME.selected_back.loc_name)*0.01})}}, + }}, + {n=G.UIT.R, config={align = "cm", r = 0.1, padding = 0.1, minw = 2.5, minh = 1.3, colour = G.C.WHITE, emboss = 0.05}, nodes={ + {n=G.UIT.O, config={object = UIBox{ + definition = G.GAME.selected_back:generate_UI(nil,0.7, 0.5, G.GAME.challenge), + config = {offset = {x=0,y=0}} + }}} + }} + }}, + {n=G.UIT.R, config={align = "cm", r = 0.1, outline_colour = G.C.L_BLACK, line_emboss = 0.05, outline = 1.5}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.05, padding = 0.07}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{string = localize('k_base_cards'), colour = G.C.RED}, modded and {string = localize('k_effective'), colour = G.C.BLUE} or nil}, colours = {G.C.RED}, silent = true,scale = 0.4,pop_in_rate = 10, pop_delay = 4})}} + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.05, padding = 0.1}, nodes={ + tally_sprite({x=1,y=0},{{string = ''..ace_tally, colour = flip_col},{string =''..mod_ace_tally, colour = G.C.BLUE}}, {localize('k_aces')}),--Aces + tally_sprite({x=2,y=0},{{string = ''..face_tally, colour = flip_col},{string =''..mod_face_tally, colour = G.C.BLUE}}, {localize('k_face_cards')}),--Face + tally_sprite({x=3,y=0},{{string = ''..num_tally, colour = flip_col},{string =''..mod_num_tally, colour = G.C.BLUE}}, {localize('k_numbered_cards')}),--Numbers + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.05, padding = 0.1}, nodes={ + tally_sprite({x=3,y=1}, {{string = ''..suit_tallies['Spades'], colour = flip_col},{string =''..mod_suit_tallies['Spades'], colour = G.C.BLUE}}, {localize('Spades', 'suits_plural')}), + tally_sprite({x=0,y=1}, {{string = ''..suit_tallies['Hearts'], colour = flip_col},{string =''..mod_suit_tallies['Hearts'], colour = G.C.BLUE}}, {localize('Hearts', 'suits_plural')}), + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.05, padding = 0.1}, nodes={ + tally_sprite({x=2,y=1}, {{string = ''..suit_tallies['Clubs'], colour = flip_col},{string =''..mod_suit_tallies['Clubs'], colour = G.C.BLUE}}, {localize('Clubs', 'suits_plural')}), + tally_sprite({x=1,y=1}, {{string = ''..suit_tallies['Diamonds'], colour = flip_col},{string =''..mod_suit_tallies['Diamonds'], colour = G.C.BLUE}}, {localize('Diamonds', 'suits_plural')}), + }}, + }} + }}, + {n=G.UIT.C, config={align = "cm"}, nodes=rank_cols}, + {n=G.UIT.B, config={w = 0.1, h = 0.1}}, + }}, + {n=G.UIT.B, config={w = 0.2, h = 0.1}}, + {n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables} + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.8, padding = 0.05}, nodes={ + not unplayed_only and Cartomancer.add_unique_count() or nil, + modded and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={padding = 0.3, r = 0.1, colour = mix_colours(G.C.BLUE, G.C.WHITE,0.7)}, nodes = {}}, + {n=G.UIT.T, config={text =' '..localize('ph_deck_preview_effective'),colour = G.C.WHITE, scale =0.3}}, + }} or nil, + wheel_flipped > 0 and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={padding = 0.3, r = 0.1, colour = flip_col}, nodes = {}}, + {n=G.UIT.T, config={text =' '..(wheel_flipped > 1 and + localize{type = 'variable', key = 'deck_preview_wheel_plural', vars = {wheel_flipped}} or + localize{type = 'variable', key = 'deck_preview_wheel_singular', vars = {wheel_flipped}}),colour = G.C.WHITE, scale =0.3}}, + }} or nil, + }} + }} + return t +end + +function tally_sprite(pos, value, tooltip, suit) + local text_colour = G.C.BLACK + if type(value) == "table" and value[1].string==value[2].string then + text_colour = value[1].colour or G.C.WHITE + value = value[1].string + end + local t_s = Sprite(0,0,0.5,0.5, G.ASSET_ATLAS[suit and SMODS.Suits[suit][G.SETTINGS.colourblind_option and "hc_ui_atlas" or "lc_ui_atlas"]] or G.ASSET_ATLAS[("ui_"..(G.SETTINGS.colourblind_option and "2" or "1"))], {x=pos.x or 0, y=pos.y or 0}) + t_s.states.drag.can = false + t_s.states.hover.can = false + t_s.states.collide.can = false + return + {n=G.UIT.C, config={align = "cm", padding = 0.07,force_focus = true, focus_args = {type = 'tally_sprite'}, tooltip = {text = tooltip}}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1, padding = 0.04, emboss = 0.05, colour = G.C.JOKER_GREY}, nodes={ + {n=G.UIT.O, config={w=0.5,h=0.5 ,can_collide = false, object = t_s, tooltip = {text = tooltip}}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + type(value) == "table" and {n=G.UIT.O, config={object = DynaText({string = value, colours = {G.C.RED}, scale = 0.4, silent = true, shadow = true, pop_in_rate = 10, pop_delay = 4})}} or + {n=G.UIT.T, config={text = value or 'NIL',colour = text_colour, scale = 0.4, shadow = true}}, + }}, + }} +end + +function G.UIDEF.used_vouchers() + + local silent = false + local keys_used = {} + local area_count = 0 + local voucher_areas = {} + local voucher_tables = {} + local voucher_table_rows = {} + for k, v in ipairs(G.P_CENTER_POOLS["Voucher"]) do + local key = 1 + math.floor((k-0.1)/2) + keys_used[key] = keys_used[key] or {} + if G.GAME.used_vouchers[v.key] then + if not G.GAME.cry_owned_vouchers[v.key] then + G.GAME.cry_owned_vouchers[v.key] = G.GAME.used_vouchers[v.key] + end + end + if G.GAME.cry_owned_vouchers[v.key] then + keys_used[key][#keys_used[key]+1] = v + end + end + for k, v in ipairs(keys_used) do + if next(v) then + area_count = area_count + 1 + end + end + for k, v in ipairs(keys_used) do + if next(v) then + if #voucher_areas == 5 or #voucher_areas == 10 then + table.insert(voucher_table_rows, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes=voucher_tables} + ) + voucher_tables = {} + end + voucher_areas[#voucher_areas + 1] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + (#v == 1 and 1 or 1.33)*G.CARD_W, + (area_count >=10 and 0.75 or 1.07)*G.CARD_H, + {card_limit = 2, type = 'voucher', highlight_limit = 0}) + for kk, vv in ipairs(v) do + local center = G.P_CENTERS[vv.key] + local card = Card(voucher_areas[#voucher_areas].T.x + voucher_areas[#voucher_areas].T.w/2, voucher_areas[#voucher_areas].T.y, G.CARD_W, G.CARD_H, nil, center, {bypass_discovery_center=true,bypass_discovery_ui=true,bypass_lock=true}) + card.ability.order = vv.order + card:start_materialize(nil, silent) + silent = true + if G.GAME.voucher_edition_index[card.ability.name] then -- i just made it a function so i can look at it less + local edition = cry_edition_to_table(G.GAME.voucher_edition_index[card.ability.name]) + if edition then + card:set_edition(edition, true, true) + end + end + + if G.GAME.voucher_sticker_index.eternal[card.ability.name] then + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.voucher_sticker_index.perishable[card.ability.name] then + card:set_perishable(true) + card.ability.perish_tally = G.GAME.voucher_sticker_index.perishable[card.ability.name] + card.ability.perishable = true + if G.GAME.voucher_sticker_index.perishable[card.ability.name] == 0 then + card.debuff = true + end + end + if G.GAME.voucher_sticker_index.rental[card.ability.name] then + card:set_rental(true) + card.ability.rental = true + end + if G.GAME.voucher_sticker_index.pinned[card.ability.name] then + card.pinned = true + end + if G.GAME.voucher_sticker_index.banana[card.ability.name] then + card.ability.banana = true + end + card.ability.extra = G.GAME.cry_voucher_centers[card.config.center_key].config.extra + if card.ability.extra_disp then card.ability.extra_disp = G.GAME.cry_voucher_centers[card.config.center_key].config.extra_disp end + voucher_areas[#voucher_areas]:emplace(card) + end + table.insert(voucher_tables, + {n=G.UIT.C, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = voucher_areas[#voucher_areas]}} + }} + ) + end + end + table.insert(voucher_table_rows, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes=voucher_tables} + ) + + + local t = silent and {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('ph_vouchers_redeemed')}, colours = {G.C.UI.TEXT_LIGHT}, bump = true, scale = 0.6})}} + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.5}, nodes={ + }}, + {n=G.UIT.R, config={align = "cm", colour = G.C.BLACK, r = 1, padding = 0.15, emboss = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes=voucher_table_rows}, + }} + }} or + {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('ph_no_vouchers')}, colours = {G.C.UI.TEXT_LIGHT}, bump = true, scale = 0.6})}} + }} + return t +end + + + +function create_UIBox_your_collection() + set_discover_tallies() + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + G.REFRESH_ALERTS = true + return true + end + })) + local consumable_nodes = {} + if #SMODS.ConsumableType.ctype_buffer <= 3 then + for _, key in ipairs(SMODS.ConsumableType.ctype_buffer) do + local id = 'your_collection_'..key:lower()..'s' + consumable_nodes[#consumable_nodes+1] = UIBox_button({button = id, label = {localize('b_'..key:lower()..'_cards')}, count = G.DISCOVER_TALLIES[key:lower()..'s'], minw = 4, id = id, colour = G.C.SECONDARY_SET[key]}) + end + else + consumable_nodes[#consumable_nodes+1] = UIBox_button({ button = 'your_collection_consumables', label = {localize('b_stat_consumables'), localize{ type = 'variable', key = 'c_types', vars = {#SMODS.ConsumableType.ctype_buffer} } }, count = G.DISCOVER_TALLIES['consumeables'], minw = 4, minh = 4, id = 'your_collection_consumables', colour = G.C.FILTER }) + end + local t = create_UIBox_generic_options({ back_func = G.STAGE == G.STAGES.RUN and 'options' or 'exit_overlay_menu', contents = { + {n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes={ + UIBox_button({button = 'your_collection_jokers', label = {localize('b_jokers')}, count = G.DISCOVER_TALLIES.jokers, minw = 5, minh = 1.7, scale = 0.6, id = 'your_collection_jokers'}), + UIBox_button({button = 'your_collection_decks', label = {localize('b_decks')}, count = G.DISCOVER_TALLIES.backs, minw = 5}), + UIBox_button({button = 'your_collection_vouchers', label = {localize('b_vouchers')}, count = G.DISCOVER_TALLIES.vouchers, minw = 5, id = 'your_collection_vouchers'}), + {n=G.UIT.R, config={align = "cm", padding = 0.1, r=0.2, colour = G.C.BLACK}, nodes={ + {n=G.UIT.C, config={align = "cm", maxh=2.9}, nodes={ + {n=G.UIT.T, config={text = localize('k_cap_consumables'), scale = 0.45, colour = G.C.L_BLACK, vert = true, maxh=2.2}}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes = consumable_nodes} + }}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.15}, nodes={ + UIBox_button({button = 'your_collection_enhancements', label = {localize('b_enhanced_cards')}, minw = 5}), + UIBox_button({button = 'your_collection_seals', label = {localize('b_seals')}, minw = 5, id = 'your_collection_seals'}), + UIBox_button({button = 'your_collection_editions', label = {localize('b_editions')}, count = G.DISCOVER_TALLIES.editions, minw = 5, id = 'your_collection_editions'}), + UIBox_button({button = 'your_collection_boosters', label = {localize('b_booster_packs')}, count = G.DISCOVER_TALLIES.boosters, minw = 5, id = 'your_collection_boosters'}), + UIBox_button({button = 'your_collection_tags', label = {localize('b_tags')}, count = G.DISCOVER_TALLIES.tags, minw = 5, id = 'your_collection_tags'}), + UIBox_button({button = 'your_collection_blinds', label = {localize('b_blinds')}, count = G.DISCOVER_TALLIES.blinds, minw = 5, minh = 2.0, id = 'your_collection_blinds', focus_args = {snap_to = true}}),UIBox_button({button = 'your_collection_other_gameobjects', label = {localize('k_other')}, minw = 5, id = 'your_collection_other_gameobjects', focus_args = {snap_to = true}}), + }}, + + }}) + return t +end + +function create_UIBox_your_collection_jokers() + local deck_tables = {} + + G.your_collection = {} + for j = 1, 3 do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + 5*G.CARD_W, + 0.95*G.CARD_H, + {card_limit = 5, type = 'title', highlight_limit = 0, collection = true}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0.07, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection[j]}} + }} + ) + end + + local joker_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker) + local joker_options = {} + for i = 1, math.ceil(#joker_pool/(5*#G.your_collection)) do + table.insert(joker_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#joker_pool/(5*#G.your_collection)))) + end + + for i = 1, 5 do + for j = 1, #G.your_collection do + local center = joker_pool[i+(j-1)*5] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, nil, center) + card.sticker = get_joker_win_sticker(center) + G.your_collection[j]:emplace(card) + end + end + + INIT_COLLECTION_CARD_ALERTS() + + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = { + {n=G.UIT.R, config={align = "cm", r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + create_option_cycle({options = joker_options, w = 4.5, cycle_shoulders = true, opt_callback = 'your_collection_joker_page', current_option = 1, colour = G.C.RED, no_pips = true, focus_args = {snap_to = true, nav = 'wide'}}) + }} + }}) + return t +end + +function create_UIBox_your_collection_tarots() + local deck_tables = {} + + G.your_collection = {} + for j = 1, 2 do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + (4.25+j)*G.CARD_W, + 1*G.CARD_H, + {card_limit = 4 + j, type = 'title', highlight_limit = 0, collection = true}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection[j]}} + }} + ) + end + + local tarot_options = {} + for i = 1, math.floor(#G.P_CENTER_POOLS.Tarot/11) do + table.insert(tarot_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.floor(#G.P_CENTER_POOLS.Tarot/11))) + end + + for j = 1, #G.your_collection do + for i = 1, 4+j do + local center = G.P_CENTER_POOLS["Tarot"][i+(j-1)*(5)] + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, nil, center) + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + + INIT_COLLECTION_CARD_ALERTS() + + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = { + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + create_option_cycle({options = tarot_options, w = 4.5, cycle_shoulders = true, opt_callback = 'your_collection_tarot_page', focus_args = {snap_to = true, nav = 'wide'},current_option = 1, colour = G.C.RED, no_pips = true}) + }} + }}) + return t +end + +function create_UIBox_your_collection_boosters() + local deck_tables = {} + + G.your_collection = {} + for j = 1, 2 do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + (5.25)*G.CARD_W, + 1.3*G.CARD_H, + {card_limit = 4, type = 'title', highlight_limit = 0, collection = true}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection[j]}} + }} + ) + end + + local booster_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Booster) + local booster_options = {} + for i = 1, math.ceil(#booster_pool/8) do + table.insert(booster_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#booster_pool/8))) + end + + for j = 1, #G.your_collection do + for i = 1, 4 do + local center = booster_pool[i+(j-1)*4] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W*1.27, G.CARD_H*1.27, nil, center) + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + + INIT_COLLECTION_CARD_ALERTS() + + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = { + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + create_option_cycle({options = booster_options, w = 4.5, cycle_shoulders = true, opt_callback = 'your_collection_booster_page', focus_args = {snap_to = true, nav = 'wide'},current_option = 1, colour = G.C.RED, no_pips = true}) + }} + }}) + return t +end + +function create_UIBox_your_collection_planets() + local deck_tables = {} + + G.your_collection = {} + for j = 1, 2 do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + (6.25)*G.CARD_W, + 1*G.CARD_H, + {card_limit = 6, type = 'title', highlight_limit = 0, collection = true}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection[j]}} + }} + ) + end + + for j = 1, #G.your_collection do + for i = 1, 6 do + local center = G.P_CENTER_POOLS["Planet"][i+(j-1)*(6)] + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, nil, center) + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + + INIT_COLLECTION_CARD_ALERTS() + + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = { + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, + {n=G.UIT.R, config={align = "cm", padding = 0.7}, nodes={}}, + }}) + return t +end + +function create_UIBox_your_collection_spectrals() + local deck_tables = {} + + G.your_collection = {} + for j = 1, 2 do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + (3.25+j)*G.CARD_W, + 1*G.CARD_H, + {card_limit = 3+j, type = 'title', highlight_limit = 0, collection = true}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection[j]}} + }} + ) + end + + for j = 1, #G.your_collection do + for i = 1, 3+j do + local center = G.P_CENTER_POOLS["Spectral"][i+(j-1)*3 + j - 1] + + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, nil, center) + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + + local spectral_options = {} + for i = 1, math.floor(#G.P_CENTER_POOLS.Tarot/9) do + table.insert(spectral_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.floor(#G.P_CENTER_POOLS.Spectral/9))) + end + + INIT_COLLECTION_CARD_ALERTS() + + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = { + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + create_option_cycle({options = spectral_options, w = 4.5, cycle_shoulders = true, opt_callback = 'your_collection_spectral_page', focus_args = {snap_to = true, nav = 'wide'},current_option = 1, colour = G.C.RED, no_pips = true}) + }}, + }}) + return t +end + +function create_UIBox_your_collection_vouchers(exit) + local deck_tables = {} + + G.your_collection = {} + for j = 1, 2 do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + 4.25*G.CARD_W, + 1*G.CARD_H, + {card_limit = 4, type = 'voucher', highlight_limit = 0, collection = true}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection[j]}} + }} + ) + end + + local voucher_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Voucher) + local voucher_options = {} + for i = 1, math.ceil(#voucher_pool/(4*#G.your_collection)) do + table.insert(voucher_options, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#voucher_pool/(4*#G.your_collection)))) + end + + for i = 1, 4 do + for j = 1, #G.your_collection do + local center = voucher_pool[i+(j-1)*4] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, nil, center) + card.ability.order = i+(j-1)*4 + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + + INIT_COLLECTION_CARD_ALERTS() + + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', contents = { + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + create_option_cycle({options = voucher_options, w = 4.5, cycle_shoulders = true, opt_callback = 'your_collection_voucher_page', focus_args = {snap_to = true, nav = 'wide'}, current_option = 1, colour = G.C.RED, no_pips = true}) + }} + }}) + return t +end + +function create_UIBox_your_collection_seals(exit) + local deck_tables = {} + + G.your_collection = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + 4.25*G.CARD_W, + 1.03*G.CARD_H, + {card_limit = 4, type = 'title', highlight_limit = 0, collection = true}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection}} + }} + ) + + for k, v in ipairs(G.P_CENTER_POOLS['Seal']) do + local center = G.P_CENTERS.c_base + local card = Card(G.your_collection.T.x + G.your_collection.T.w/2, G.your_collection.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + card:set_seal(v.key, true) + G.your_collection:emplace(card) + end + + local t = create_UIBox_generic_options({ infotip = localize('ml_edition_seal_enhancement_explanation'), back_func = exit or 'your_collection', snap_back = true, contents = { + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, + }}) + return t +end + +function create_UIBox_your_collection_enhancements(exit) + local deck_tables = {} + + G.your_collection = {} + for j = 1, 2 do + G.your_collection[j] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + 4.25*G.CARD_W, + 1.03*G.CARD_H, + {card_limit = 4, type = 'title', highlight_limit = 0, collection = true}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection[j]}} + }} + ) + end + + for i = 1, 4 do + for j = 1, #G.your_collection do + local center = G.P_CENTER_POOLS["Enhanced"][i+(j-1)*4] + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + G.your_collection[j]:emplace(card) + end + end + + local t = create_UIBox_generic_options({ infotip = localize('ml_edition_seal_enhancement_explanation'), back_func = exit or 'your_collection', snap_back = true, contents = { + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes=deck_tables}, + }}) + return t +end + +function create_UIBox_your_collection_editions() + G.your_collection = {} + G.your_collection[1] = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + 5.3*G.CARD_W, + 1.03*G.CARD_H, + {card_limit = 5, type = 'title', highlight_limit = 0, collection = true}) + local deck_tables = + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.O, config={object = G.your_collection[1]}} + }} + + local editions = {'base', 'foil','holo','polychrome','negative'} + + for i = 1, 5 do + local card = Card(G.your_collection[1].T.x + G.your_collection[1].T.w/2, G.your_collection[1].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['e_'..editions[i]]) + card:start_materialize() + if G.P_CENTERS['e_'..editions[i]].discovered then card:set_edition({[editions[i]] = true}, true, true) end + G.your_collection[1]:emplace(card) + end + + INIT_COLLECTION_CARD_ALERTS() + + local t = create_UIBox_generic_options({ infotip = localize('ml_edition_seal_enhancement_explanation'), back_func = 'your_collection', snap_back = true, contents = { + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes={deck_tables}}, + }}) + return t +end + +function create_UIBox_your_collection_decks() + local deck_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Back) + G.GAME.viewed_back = Back(G.ACTIVE_MOD_UI and deck_pool[1] or G.P_CENTERS.b_red) + + local area = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + 1.2*G.CARD_W, + 1.2*G.CARD_H, + {card_limit = 52, type = 'deck', highlight_limit = 0}) + + for i = 1, 52 do + local card = Card(G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, G.CARD_W*1.2, G.CARD_H*1.2, pseudorandom_element(G.P_CARDS), G.P_CENTERS.c_base, {playing_card = i, viewed_back = true}) + card.sprite_facing = 'back' + card.facing = 'back' + area:emplace(card) + if i == 52 then G.sticker_card = card; card.sticker = get_deck_win_sticker(G.GAME.viewed_back.effect.center) end + end + + local ordered_names = {} + for k, v in ipairs(deck_pool) do + ordered_names[#ordered_names+1] = v.key + end + + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or 'your_collection', contents = { + create_option_cycle({options = ordered_names, opt_callback = 'change_viewed_back', current_option = 1, colour = G.C.RED, w = 4.5, focus_args = {snap_to = true}, mid = + {n=G.UIT.R, config={align = "cm", minw = 2.5, padding = 0.1, r = 0.1, colour = G.C.BLACK, emboss = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.2, colour = G.C.BLACK, r = 0.2}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = area}} + }}, + {n=G.UIT.C, config={align = "tm", minw = 3.7, minh = 2.1, r = 0.1, colour = G.C.L_BLACK, padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", emboss = 0.1, r = 0.1, minw = 4, maxw = 4, minh = 0.6}, nodes={ + {n=G.UIT.O, config={id = nil, func = 'RUN_SETUP_check_back_name', object = Moveable()}}, + }}, + {n=G.UIT.R, config={align = "cm", colour = G.C.WHITE, emboss = 0.1, minh = 2.2, r = 0.1}, nodes={ + {n=G.UIT.O, config={id = G.GAME.viewed_back.name, func = 'RUN_SETUP_check_back', object = UIBox{definition = G.GAME.viewed_back:generate_UI(), config = {offset = {x=0,y=0}}}}} + }} + }}, + }}, + }}}), + }}) + return t +end + +function create_UIBox_your_collection_tags() + local tag_matrix = {} + local counter = 0 + local tag_tab = {} + local tag_pool = {} + if G.ACTIVE_MOD_UI then + for k, v in pairs(G.P_TAGS) do + if v.mod and G.ACTIVE_MOD_UI.id == v.mod.id then tag_pool[k] = v end + end + else + tag_pool = G.P_TAGS + end + for k, v in pairs(tag_pool) do + counter = counter + 1 + tag_tab[#tag_tab+1] = v + end + for i = 1, math.ceil(counter / 6) do + table.insert(tag_matrix, {}) + end + + table.sort(tag_tab, function (a, b) return a.order < b.order end) + + local tags_to_be_alerted = {} + for k, v in ipairs(tag_tab) do + local discovered = v.discovered + local temp_tag = Tag(v.key, true) + if not v.discovered then temp_tag.hide_ability = true end + local temp_tag_ui, temp_tag_sprite = temp_tag:generate_UI() + tag_matrix[math.ceil((k-1)/6+0.001)][1+((k-1)%6)] = {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + temp_tag_ui, + }} + if discovered and not v.alerted then + tags_to_be_alerted[#tags_to_be_alerted+1] = temp_tag_sprite + end + end + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + for _, v in ipairs(tags_to_be_alerted) do + v.children.alert = UIBox{ + definition = create_UIBox_card_alert(), + config = { align="tri", offset = {x = 0.1, y = 0.1}, parent = v} + } + v.children.alert.states.collide.can = false + end + return true + end) + })) + + + local table_nodes = {} + for i = 1, math.ceil(counter / 6) do + table.insert(table_nodes, {n=G.UIT.R, config={align = "cm"}, nodes=tag_matrix[i]}) + end local t = create_UIBox_generic_options({ back_func = 'your_collection', contents = { + {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.BLACK, padding = 0.1, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes=table_nodes} + }} + }} + }}) + return t +end + +function create_UIBox_your_collection_blinds(exit) + local blind_matrix = { + {},{},{}, {}, {}, {} + } + local blind_tab = {} + for k, v in pairs(G.P_BLINDS) do + blind_tab[#blind_tab+1] = v + end + + table.sort(blind_tab, function(a, b) return a.order + (a.boss and a.boss.showdown and 1000 or 0) < b.order + (b.boss and b.boss.showdown and 1000 or 0) end) + + local blinds_to_be_alerted = {} + for k, v in ipairs(blind_tab) do + local discovered = v.discovered + + local s = 1.3 + if math.ceil(#blind_tab/6) > 6 then + s = s * 6/math.ceil(#blind_tab/6) + end + local temp_blind = AnimatedSprite(0,0,s,s, G.ANIMATION_ATLAS[discovered and v.atlas or 'blind_chips'], discovered and v.pos or G.b_undiscovered.pos) + temp_blind:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'} + }) + if k == 1 then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + G.CONTROLLER:snap_to{node = temp_blind} + return true + end) + })) + end + temp_blind.float = true + temp_blind.states.hover.can = true + temp_blind.states.drag.can = false + temp_blind.states.collide.can = true + temp_blind.config = {blind = v, force_focus = true} + if discovered and not v.alerted then + blinds_to_be_alerted[#blinds_to_be_alerted+1] = temp_blind + end + temp_blind.hover = function() + if not G.CONTROLLER.dragging.target or G.CONTROLLER.using_touch then + if not temp_blind.hovering and temp_blind.states.visible then + temp_blind.hovering = true + temp_blind.hover_tilt = 3 + temp_blind:juice_up(0.05, 0.02) + play_sound('chips1', math.random()*0.1 + 0.55, 0.12) + temp_blind.config.h_popup = create_UIBox_blind_popup(v, discovered) + temp_blind.config.h_popup_config ={align = 'cl', offset = {x=-0.1,y=0},parent = temp_blind} + Node.hover(temp_blind) + if temp_blind.children.alert then + temp_blind.children.alert:remove() + temp_blind.children.alert = nil + temp_blind.config.blind.alerted = true + G:save_progress() + end + end + end + temp_blind.stop_hover = function() temp_blind.hovering = false; Node.stop_hover(temp_blind); temp_blind.hover_tilt = 0 end + end + local blinds_per_row = math.ceil(#blind_tab / 6) + local row = math.ceil((k - 1) / blinds_per_row + 0.001) + table.insert(blind_matrix[row], { + n = G.UIT.C, + config = { align = "cm", padding = 0.1 }, + nodes = { + ((k - blinds_per_row) % (2 * blinds_per_row) == 1) and { n = G.UIT.B, config = { h = 0.2, w = 0.5 } } or nil, + { n = G.UIT.O, config = { object = temp_blind, focus_with_object = true } }, + ((k - blinds_per_row) % (2 * blinds_per_row) == 0) and { n = G.UIT.B, config = { h = 0.2, w = 0.5 } } or nil, + } + }) + + end + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + for _, v in ipairs(blinds_to_be_alerted) do + v.children.alert = UIBox{ + definition = create_UIBox_card_alert(), + config = { align="tri", offset = {x = 0.1, y = 0.1}, parent = v} + } + v.children.alert.states.collide.can = false + end + return true + end) + })) + + local min_ante = 1 + local max_ante = 16 + local spacing = 1 - 15*0.06 + if G.GAME and G.GAME.round_resets and G.GAME.round_resets.ante then + local current_ante = G.GAME.round_resets.ante + + if current_ante > 8 then + min_ante = current_ante - 8 + 1 + max_ante = current_ante + 8 + end + end + local ante_amounts = {} + for i = min_ante, max_ante do + -- :3 + if spacing > 0 and i > 1 then + ante_amounts[#ante_amounts+1] = {n=G.UIT.R, config={minh = spacing}, nodes={}} + end + local blind_chip = Sprite(0,0,0.2,0.2,G.ASSET_ATLAS["ui_"..(G.SETTINGS.colourblind_option and 2 or 1)], {x=0, y=0}) + blind_chip.states.drag.can = false + ante_amounts[#ante_amounts+1] = {n=G.UIT.R, config={align = "cm", padding = 0.03}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = 0.7}, nodes={ + {n=G.UIT.T, config={text = i, scale = 0.4, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.C, config={align = "cr", minw = 2.8}, nodes={ + {n=G.UIT.O, config={object = blind_chip}}, + {n=G.UIT.C, config={align = "cm", minw = 0.03, minh = 0.01}, nodes={}}, + {n=G.UIT.T, config={text =number_format(get_blind_amount(i)), scale = 0.4, colour = i <= G.PROFILES[G.SETTINGS.profile].high_scores.furthest_ante.amt and G.C.RED or G.C.JOKER_GREY, shadow = true}}, + }} + }} + end + + local extras = nil + local t = create_UIBox_generic_options({ back_func = G.ACTIVE_MOD_UI and "openModUI_"..G.ACTIVE_MOD_UI.id or exit or 'your_collection', contents = { + {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.BLACK, padding = 0.1, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.L_BLACK, padding = 0.1, force_focus = true, focus_args = {nav = 'tall'}}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = 0.7}, nodes={ + {n=G.UIT.T, config={text = localize('k_ante_cap'), scale = 0.4, colour = lighten(G.C.FILTER, 0.2), shadow = true}}, + }}, + {n=G.UIT.C, config={align = "cr", minw = 2.8}, nodes={ + {n=G.UIT.T, config={text = localize('k_base_cap'), scale = 0.4, colour = lighten(G.C.RED, 0.2), shadow = true}}, + }} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes=ante_amounts} + }}, + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes=blind_matrix[1]}, + {n=G.UIT.R, config={align = "cm"}, nodes=blind_matrix[2]}, + {n=G.UIT.R, config={align = "cm"}, nodes=blind_matrix[3]}, + {n=G.UIT.R, config={align = "cm"}, nodes=blind_matrix[4]}, + {n=G.UIT.R, config={align = "cm"}, nodes=blind_matrix[5]}, + {n=G.UIT.R, config={align = "cm"}, nodes=blind_matrix[6]}, + }} + }} + }} + }}) + return t +end + +function create_UIBox_blind_popup(blind, discovered, vars) + local blind_text = {} + + local _dollars = blind.dollars + local loc_vars = nil + if blind.collection_loc_vars and type(blind.collection_loc_vars) == 'function' then + local res = blind:collection_loc_vars() or {} + loc_vars = res.vars + end + local loc_target = localize{type = 'raw_descriptions', key = blind.key, set = 'Blind', vars = loc_vars or vars or blind.vars} + local loc_name = localize{type = 'name_text', key = blind.key, set = 'Blind'} + + if discovered then + local ability_text = {} + if loc_target then + for k, v in ipairs(loc_target) do + ability_text[#ability_text + 1] = {n=G.UIT.R, config={align = "cm"}, nodes={{n=G.UIT.T, config={text = v, scale = 0.35, shadow = true, colour = G.C.WHITE}}}} + end + end + local stake_sprite = get_stake_sprite(G.GAME.stake or 1, 0.4) + blind_text[#blind_text + 1] = + {n=G.UIT.R, config={align = "cm", emboss = 0.05, r = 0.1, minw = 2.5, padding = 0.07, colour = G.C.WHITE}, nodes={ + {n=G.UIT.R, config={align = "cm", maxw = 2.4}, nodes={ + {n=G.UIT.T, config={text = localize('ph_blind_score_at_least'), scale = 0.35, colour = G.C.UI.TEXT_DARK}}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = stake_sprite}}, + {n=G.UIT.T, config={text = blind.mult..localize('k_x_base'), scale = 0.4, colour = G.C.RED}}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = localize('ph_blind_reward'), scale = 0.35, colour = G.C.UI.TEXT_DARK}}, + {n=G.UIT.O, config={object = DynaText({string = {_dollars and string.rep(localize('$'),_dollars) or '-'}, colours = {G.C.MONEY}, rotate = true, bump = true, silent = true, scale = 0.45})}}, + }}, + ability_text[1] and {n=G.UIT.R, config={align = "cm", padding = 0.08, colour = mix_colours(blind.boss_colour, G.C.GREY, 0.4), r = 0.1, emboss = 0.05, minw = 2.5, minh = 0.9}, nodes=ability_text} or nil + }} + else + blind_text[#blind_text + 1] = + {n=G.UIT.R, config={align = "cm", emboss = 0.05, r = 0.1, minw = 2.5, padding = 0.1, colour = G.C.WHITE}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = localize('ph_defeat_this_blind_1'), scale = 0.4, colour = G.C.UI.TEXT_DARK}}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = localize('ph_defeat_this_blind_2'), scale = 0.4, colour = G.C.UI.TEXT_DARK}}, + }}, + }} + end + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.05, colour = lighten(G.C.JOKER_GREY, 0.5), r = 0.1, emboss = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cm", emboss = 0.05, r = 0.1, minw = 2.5, padding = 0.1, colour = not discovered and G.C.JOKER_GREY or blind.boss_colour or G.C.GREY}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = discovered and loc_name or localize('k_not_discovered'), colours = {G.C.UI.TEXT_LIGHT}, shadow = true, rotate = not discovered, spacing = discovered and 2 or 0, bump = true, scale = 0.4})}}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes=blind_text}, + }} +end + + +function create_UIBox_card_unlock(card_center) + G.your_collection = CardArea( + G.ROOM.T.x + G.ROOM.T.w/2,G.ROOM.T.h, + 1*G.CARD_W, + 1*G.CARD_H, + {card_limit = 2, type = 'consumeable', highlight_limit = 0}) + + local card = Card(G.your_collection.T.x + G.your_collection.T.w/2 - G.CARD_W/2, G.your_collection.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, card_center, {bypass_discovery_center = true, bypass_discovery_ui = true}) + local locked_card = Card(G.your_collection.T.x + G.your_collection.T.w/2 - G.CARD_W/2, G.your_collection.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, card_center.set == 'Voucher' and G.v_locked or G.j_locked) + locked_card:remove_UI() + locked_card.ID = card.ID + card.states.click.can = false + locked_card.states.click.can = false + card.states.visible = false + card.no_ui = true + + G.E_MANAGER:add_event(Event({timer = 'REAL',blockable = false,blocking = false, + func = (function() G.OVERLAY_MENU.joker_unlock_table = card.ID return true end) })) + G.E_MANAGER:add_event(Event({timer = 'REAL',blockable = false,blocking = false, trigger = 'after', delay = 0.6, + func = (function() if G.OVERLAY_MENU and G.OVERLAY_MENU.joker_unlock_table == card.ID then locked_card:juice_up(0.3, 0.2); play_sound('cancel', 0.8) end; return true end) })) + G.E_MANAGER:add_event(Event({timer = 'REAL',blockable = false,blocking = false, trigger = 'after', delay = 1.15, + func = (function() if G.OVERLAY_MENU and G.OVERLAY_MENU.joker_unlock_table == card.ID then locked_card:juice_up(0.45, 0.3); play_sound('cancel', 0.92) end; return true end) })) + G.E_MANAGER:add_event(Event({timer = 'REAL',blockable = false,blocking = false, trigger = 'after', delay = 1.8, + func = (function() if G.OVERLAY_MENU and G.OVERLAY_MENU.joker_unlock_table == card.ID then locked_card:juice_up(0.6, 0.4); play_sound('cancel', 1.03) end; return true end) })) + + G.E_MANAGER:add_event(Event({ + timer = 'REAL', + blockable = false, + blocking = false, + trigger = 'after', + delay = 2.3, + func = (function() + if G.OVERLAY_MENU and G.OVERLAY_MENU.joker_unlock_table == card.ID then locked_card:start_dissolve({G.C.BLACK}) end + return true end) + })) + G.E_MANAGER:add_event(Event({ + timer = 'REAL', + blockable = false, + blocking = false, + trigger = 'after', + delay = 2.7, + func = (function() + if G.OVERLAY_MENU and G.OVERLAY_MENU.joker_unlock_table == card.ID then + card:start_materialize({G.C.BLUE}, true) + play_sound('crumple1', 0.8, 1); + end + return true end) + })) + G.E_MANAGER:add_event(Event({timer = 'REAL',blockable = false,blocking = false, trigger = 'after', delay = 2.78, + func = (function() if G.OVERLAY_MENU and G.OVERLAY_MENU.joker_unlock_table == card.ID then card.no_ui = nil; play_sound('timpani', 0.8, 1.8) end return true end) })) + G.E_MANAGER:add_event(Event({timer = 'REAL',blockable = false,blocking = false, trigger = 'after', delay = 2.95, + func = (function() if G.OVERLAY_MENU and G.OVERLAY_MENU.joker_unlock_table == card.ID then play_sound('timpani', 1, 1.8) end return true end) })) + + G.your_collection:emplace(card) + G.your_collection:emplace(locked_card) + + local t = create_UIBox_generic_options({padding = 0,back_label = localize('b_continue'), no_pip = true, snap_back = true, back_func = 'continue_unlock', minw = 4.5, contents = { + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {card_center.set == 'Voucher' and localize('k_voucher') or localize('k_joker')}, colours = {G.C.BLUE},shadow = true, rotate = true, bump = true, pop_in = 0.3, pop_in_rate = 2, scale = 1.2})}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('k_unlocked_ex')}, colours = {G.C.RED},shadow = true, rotate = true, bump = true, pop_in = 0.6, pop_in_rate = 2, scale = 0.8})}} + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0, draw_layer = 1}, nodes={ + {n=G.UIT.O, config={object = G.your_collection}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.2}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05, emboss = 0.05, colour = G.C.WHITE, r = 0.1}, nodes={ + desc_from_rows(card:generate_UIBox_unlock_table(true).main) + }} + }} + }} + }} + }}) + return t +end + +function create_UIBox_deck_unlock(deck_center) + G.GAME.viewed_back = Back(deck_center) + local area = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + 1.2*G.CARD_W, + 1.2*G.CARD_H, + {card_limit = 52, type = 'deck', highlight_limit = 0}) + + for i = 1, 52 do + local card = Card(G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, G.CARD_W*1.2, G.CARD_H*1.2, pseudorandom_element(G.P_CARDS), G.P_CENTERS.c_base, {bypass_back = deck_center.pos, playing_card = i, viewed_back = true}) + area:emplace(card) + card[deck_center.key] = true + card.sprite_facing = 'back' + card.facing = 'back' + end + local deck_criteria = {} + if deck_center.unlock_condition.type == 'win_deck' then + local other_name = localize{type = 'name_text', set = 'Back', key = deck_center.unlock_condition.deck} + localize{type = 'descriptions', key = 'deck_locked_win', set = "Other", nodes = deck_criteria, vars = {other_name}, default_col = G.C.WHITE, shadow = true} + elseif deck_center.unlock_condition.type == 'win_stake' then + local other_name = localize{type = 'name_text', set = 'Stake', key = G.P_CENTER_POOLS.Stake[deck_center.unlock_condition.stake].key} + localize{type = 'descriptions', key = 'deck_locked_stake', set = "Other", nodes = deck_criteria, vars = {other_name, colours = {get_stake_col(deck_center.unlock_condition.stake)}}, default_col = G.C.WHITE, shadow = true} + elseif deck_center.unlock_condition.type == 'discover_amount' then + localize{type = 'descriptions', key = 'deck_locked_discover', set = "Other", nodes = deck_criteria, vars = {deck_center.unlock_condition.amount}, default_col = G.C.WHITE, shadow = true} + end + + local deck_criteria_cols = {} + for k, v in ipairs(deck_criteria) do + if k > 1 then deck_criteria_cols[#deck_criteria_cols+1] = {n=G.UIT.C, config={align = "cm", padding = 0, minw = 0.1}, nodes={}} end + deck_criteria_cols[#deck_criteria_cols+1] = {n=G.UIT.C, config={align = "cm", padding = 0}, nodes=v} + end + + local t = create_UIBox_generic_options({ back_label = localize('b_continue'), no_pip = true, snap_back = true, back_func = 'continue_unlock', minw = 7, contents = { + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {{string = localize{type = 'name_text', set = 'Back', key = deck_center.key}, suffix = ' '..localize('k_unlocked_ex'), outer_colour = G.C.UI.TEXT_LIGHT}}, colours = {G.C.BLUE},shadow = true, rotate = true, float = true, scale = 0.7, pop_in = 0.1})}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes=deck_criteria_cols}, + {n=G.UIT.R, config={align = "cm", padding = 0.2, colour = G.C.BLACK, r = 0.2}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = area}} + }}, + {n=G.UIT.C, config={align = "cm", r = 0.2, colour = G.C.WHITE, emboss = 0.05, padding = 0.2, minw = 4}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes=G.GAME.viewed_back:generate_UI(deck_center).nodes} + }} + }} + }}) + return t +end + +function G.UIDEF.credits() + local text_scale = 0.75 + local t = create_UIBox_generic_options({contents ={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + create_tabs( + {tabs = { + { + label = "Music", + chosen = true, + tab_definition_function = function() return + {n=G.UIT.ROOT, config={align = "cm", padding = 0.2, colour = G.C.BLACK, r = 0.1, emboss = 0.05, minh = 6, minw = 6}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Original Soundtrack", scale = text_scale*0.8, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "composed by ", scale = text_scale*0.8, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = "LouisF", scale = text_scale*0.8, colour = G.C.BLUE, shadow = true}} + }}, + G.F_EXTERNAL_LINKS and {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + UIBox_button({label = {'Instagram'}, button = 'louisf_insta'}) + }} or nil, + {n=G.UIT.R, config={align = "cm", padding = 0.2}, nodes={ + {n=G.UIT.T, config={text = "Modified with their permission", scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }} + end, + }, + { + label = "Publishing", + tab_definition_function = function() return + {n=G.UIT.ROOT, config={align = "cm", padding = 0.2, colour = G.C.BLACK, r = 0.1, emboss = 0.05, minh = 6, minw = 10}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Playstack", scale = text_scale*0.6, colour = G.C.RED, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Harvey Elliott', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Kevin Shrapnell', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Rob Crossley', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Liz Cheng-Moore', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Will Newell', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Charlotte Riley', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Alexander Saunders', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Naman Budhwar', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Tomasz Wisniowski', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Patrick Johnson', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Tom Verney', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Wouter van Halderen', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Shawn Cotter', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.C, config={align = "tl", padding = 0.093}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'CEO', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'COO', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'VP of Publishing', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Lead Marketing Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Producer', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Producer', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Producer', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Lead Visual Marketing Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Producer', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Senior Discovery Scout', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Discovery Scout', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'PR Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Marketing Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + }}, + {n=G.UIT.B, config={align = "tl", w=0.25, h=0}, nodes={}}, + {n=G.UIT.C, config={align = "tl", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Marta Matyjewicz', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Rebecca Bell', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Alex Flynn', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Justas Pugaciauskas', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Jessica Chu', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Millicent Su', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Carla Malavasi', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Pawel Kwietniak', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Ela Müller', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Edgar Khoo', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Dami Ajiboye', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Aaron Ludlow', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Jenny Quintero', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.C, config={align = "tl", padding = 0.093}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Marketing Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Finance', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Creative Director', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Graphic Designer', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Lead Video Artist', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Senior User Acquisition Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Director of Publishing Services', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Front-end Developer', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Graphic Designer', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Video Editor', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Data Analyst', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Product Director', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Senior Partnerships Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + }}, + {n=G.UIT.B, config={align = "tl", w=0.25, h=0}, nodes={}}, + {n=G.UIT.C, config={align = "tl", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Stephanie Marti', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Emma Smith-Bodie', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Moe Abrams', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Piotr Kowalik', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Carmen Martino', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Rong Lin', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Bea Gomez', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Jose Olivares', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Joanna Kieronska', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Zuzanna Dawidowska', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Jean-Claude Vidanes ', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'AJ Purnell', scale = text_scale*0.42, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.C, config={align = "tl", padding = 0.093}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Senior Video Artist', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Community Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Customer Support', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Software Engineer', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'HR & Office Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Finance Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Video Editor', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'QA & Localisation Manager', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'QA Tester', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'QA Tester', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'QA Tester', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'QA Tester', scale = text_scale*0.35, colour = G.C.GOLD, shadow = true}}, + }}, + }}, + }}, + }} + }} + end, + }, + { + label = "Production", + tab_definition_function = function() return + {n=G.UIT.ROOT, config={align = "cm", padding = 0.2, colour = G.C.BLACK, r = 0.1, emboss = 0.05, minh = 6, minw = 10}, nodes={ + {n=G.UIT.C, config={align = "tm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Porting", scale = text_scale*0.6, colour = G.C.WHITE, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "PlayStation", scale = text_scale*0.45, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.03}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Maarten De Meyer', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Xbox", scale = text_scale*0.45, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.03}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Maarten De Meyer', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Android", scale = text_scale*0.45, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.03}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Maarten De Meyer', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Apple Platforms", scale = text_scale*0.45, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.03}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Maarten De Meyer', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Mac (Steam)", scale = text_scale*0.45, colour = G.C.GOLD, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.03}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'william341', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + }}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Localization", scale = text_scale*0.6, colour = G.C.WHITE, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Universally Speaking", scale = text_scale*0.6, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'German', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Spanish Latam', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'French', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Indonesian', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Italian', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Japanese', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Korean', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Dutch', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Polish', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Portuguese Brasilian', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Russian', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Simplified Chinese', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Traditional Chinese', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Project managers', scale = text_scale*0.35, colour = G.C.FILTER, shadow = true}}, + }}, + }}, + {n=G.UIT.C, config={align = "tl", padding = 0.05}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Dominik May, Lisa-Marie Beck', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Román René Orozco, Javier Gómez', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Romain Vervaecke, Claire Gérard', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Yopi Jalu Paksi, Sutarto Mohammad', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Oliver Cozzio, Giulia Benassi', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Takashi Fujimoto, Ai Parlow', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Haejung Lee, Sanghyun Bae', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Ellis Jongsma, Erik Nusselder', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Mariusz Wlodarczyk, Bartosz Klofik', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Samuel Modesto, R. Cali', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Yuliia Tatsenko, Natalia Rudane', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Shuai Fang, Liqi Ye', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Pauline Lin, AngelRabbitBB', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Ruoyang Yuan, Tania Carè', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + }}, + }}, + {n=G.UIT.C, config={align = "tm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Testing/QA", scale = text_scale*0.6, colour = G.C.WHITE, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.03}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Vishwak Kondapalli', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Basha Syed', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'CampfireCollective', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'drspectred', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'TheRealEvab', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Brightqwerty', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'MrWizzrd', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'mcpower', scale = text_scale*0.35, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + + }}, + }}, + + }} + end, + }, + { + label = "Sounds", + tab_definition_function = function() return + {n=G.UIT.ROOT, config={align = "cm", padding = 0.2, colour = G.C.BLACK, r = 0.1, emboss = 0.05, minh = 6, minw = 10}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "All sounds not listed here fall under ", scale = text_scale*0.6, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = "Creative Commons - CC0", scale = text_scale*0.6, colour = G.C.BLUE, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = '"chamber_choir_chord_o.wav" (Used for Polychrome sound) by uair01 (https://freesound.org/people/uair01/sounds/65195/)', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'is licensed under ', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = "Attribution 3.0 License", scale = text_scale*0.5, colour = G.C.GOLD, shadow = true}}, + {n=G.UIT.T, config={text = '. This work has been modified from its original state', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = '"Coffee1.wav" (Used for Tarot card sound) by Nathan Gibson (https://nathangibson.myportfolio.com)', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'is licensed under ', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = "Attribution 4.0 License", scale = text_scale*0.5, colour = G.C.ORANGE, shadow = true}}, + {n=G.UIT.T, config={text = '.', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = '"Wood Block1.wav" (Used for Tarot card sound) by Nathan Gibson (https://nathangibson.myportfolio.com)', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'is licensed under ', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = "Attribution 4.0 License", scale = text_scale*0.5, colour = G.C.ORANGE, shadow = true}}, + {n=G.UIT.T, config={text = '.', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = '"Toy records#06-E3-02.wav" (Used for Mult sounds) by poissonmort (https://freesound.org/people/poissonmort/sounds/253249/)', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'is licensed under ', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = "Attribution 4.0 License", scale = text_scale*0.5, colour = G.C.ORANGE, shadow = true}}, + {n=G.UIT.T, config={text = '. This work has been modified from its original state', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + }} + end, + }, + { + label = "Imagery", + tab_definition_function = function() return + {n=G.UIT.ROOT, config={align = "cm", padding = 0.2, colour = G.C.BLACK, r = 0.1, emboss = 0.05, minh = 6, minw = 10}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'The font "m6x11.ttf" by Daniel Linssen (https://managore.itch.io/m6x11)', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'is licensed under an ', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = "Attribution License", scale = text_scale*0.5, colour = G.C.GOLD, shadow = true}}, + {n=G.UIT.T, config={text = '.', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + not G.F_BASIC_CREDITS and + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Check out his itch.io profile, he has made an incredible catalogue of games.', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }} or nil, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'The Joker "Vagabond" was created by Lumpy Touch', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'The Collab art for Slay the Spire and The Binding of Isaac was', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'created by ', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = "Neato", scale = text_scale*0.5, colour = G.C.GOLD, shadow = true}}, + {n=G.UIT.T, config={text = ' (twitch.tv/neato)', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "All sprites, shaders, and any other visual assets", scale = text_scale*0.6, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "not listed here were created by LocalThunk.", scale = text_scale*0.6, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "©2024 - All rights reserved", scale = text_scale*0.6, colour = G.C.BLUE, shadow = true}}, + }}, + }} + end, + }, + { + label = "Misc", + tab_definition_function = function() return + {n=G.UIT.ROOT, config={align = "cm", padding = 0.2, colour = G.C.BLACK, r = 0.1, emboss = 0.05, minh = 6, minw = 6}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "For Marshal", scale = text_scale*0.6, colour = G.C.WHITE, shadow = true}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1,outline_colour = G.C.JOKER_GREY, r = 0.1, outline = 1}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = "Special Thanks", scale = text_scale*0.6, colour = G.C.GREEN, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "tm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "tl", padding = 0.05, minw = 2.5}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Nicole', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Josh', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Jeremy', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Dylan', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Justin', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Daniel', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Colby', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Thomas', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Mom & Dad', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Luc & Donna', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + {n=G.UIT.C, config={align = "tl", padding = 0.05, minw = 2.5}, nodes={ + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'GothicLordUK', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Big Simple', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'MALF', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Northernlion', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Purple Moss Collectors', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Dan Gheesling', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Fabian Fischer', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'newobject', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'MurphyObv', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", padding = 0}, nodes={ + {n=G.UIT.T, config={text = 'Love2D', scale = text_scale*0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + }}, + }}, + }} + }} + end, + }, + }, + snap_to_nav = true}), + }} + }}) + return t +end + +function G.UIDEF.challenges(from_game_over) + + if G.PROFILES[G.SETTINGS.profile].all_unlocked then G.PROFILES[G.SETTINGS.profile].challenges_unlocked = #G.CHALLENGES end + + if not G.PROFILES[G.SETTINGS.profile].challenges_unlocked then + local deck_wins = 0 + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage) do + if v.wins and v.wins[1] then + deck_wins = deck_wins + 1 + end + end + local loc_nodes = {} + localize{type = 'descriptions', key = 'challenge_locked', set = 'Other', nodes = loc_nodes, vars = {G.CHALLENGE_WINS, deck_wins}, default_col = G.C.WHITE} + + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.1, colour = G.C.CLEAR, minh = 8.02, minw = 7}, nodes={ + transparent_multiline_text(loc_nodes) + }} + end + + G.run_setup_seed = nil + if G.OVERLAY_MENU then + local seed_toggle = G.OVERLAY_MENU:get_UIE_by_ID('run_setup_seed') + if seed_toggle then seed_toggle.states.visible = false end + end + + + local _ch_comp, _ch_tot = 0,#G.CHALLENGES + for k, v in ipairs(G.CHALLENGES) do + if v.id and G.PROFILES[G.SETTINGS.profile].challenge_progress.completed[v.id or ''] then + _ch_comp = _ch_comp + 1 + end + end + + local _ch_tab = {comp = _ch_comp, unlocked = G.PROFILES[G.SETTINGS.profile].challenges_unlocked} + + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.1, colour = G.C.CLEAR, minh = 8, minw = 7}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1, r = 0.1 ,colour = G.C.BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.T, config={text = localize('k_challenge_mode'), scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", minw = 8.5, minh = 1.5, padding = 0.2}, nodes={ + UIBox_button({id = from_game_over and 'from_game_over' or nil, label = {localize('b_new_challenge')}, button = 'challenge_list', minw = 4, scale = 0.4, minh = 0.6}), + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.8, r = 0.1, minw = 4.5, colour = G.C.L_BLACK, emboss = 0.05, + progress_bar = { + max = _ch_tot, ref_table = _ch_tab, ref_value = 'unlocked', empty_col = G.C.L_BLACK, filled_col = G.C.FILTER + }}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, minw = 4.5}, nodes={ + {n=G.UIT.T, config={text = localize{type = 'variable', key = 'unlocked', vars = {_ch_tab.unlocked, _ch_tot}}, scale = 0.3, colour = G.C.WHITE, shadow =true}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.8, r = 0.1, minw = 4.5, colour = G.C.L_BLACK, emboss = 0.05, + progress_bar = { + max = _ch_tot, ref_table = _ch_tab, ref_value = 'comp', empty_col = G.C.L_BLACK, filled_col = adjust_alpha(G.C.GREEN, 0.5) + }}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, minw = 4.5}, nodes={ + {n=G.UIT.T, config={text = localize{type = 'variable', key = 'completed', vars = {_ch_comp, _ch_tot}}, scale = 0.3, colour = G.C.WHITE, shadow = true}}, + }}, + }}, + }}, + G.F_DAILIES and {n=G.UIT.R, config={align = "cm", padding = 0.1, r = 0.1 ,colour = G.C.BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.T, config={text = localize('k_daily_run'), scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cl", minw = 8.5, minh = 4}, nodes={ + G.UIDEF.daily_overview() + }} + }} or nil, + }} +end + +function G.UIDEF.daily_overview() + local hist_height, hist_width = 3, 3 + + local daily_results = { + score = { + me = {val = 20000, percentile = 75}, + hist = { + 0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.05,0.15,0.1,0.1,0.05,0.05,0.05,0.05,0.05 + } + } + } + local score_hist, max_score_hist = {}, 0 + for k, v in ipairs(daily_results.score.hist) do if max_score_hist < v then max_score_hist = v end end + for k, v in ipairs(daily_results.score.hist) do + score_hist[#score_hist+1] = {n=G.UIT.C, config={align = 'bm'}, nodes ={{n=G.UIT.C, config={colour = G.C.BLUE, minw = hist_width/#daily_results.score.hist, minh = (v + 0.05*math.random())/max_score_hist*hist_height}}}} + end + + return {n=G.UIT.R, config={align = "cm"}, nodes=score_hist} +end + +function G.UIDEF.run_setup(from_game_over) + G.run_setup_seed = nil + local _challenge_chosen = from_game_over == 'challenge_list' + from_game_over = from_game_over and not (from_game_over == 'challenge_list') + + local _can_continue = G.MAIN_MENU_UI and G.FUNCS.can_continue({config = {func = true}}) + G.FUNCS.false_ret = function() return false end + local t = create_UIBox_generic_options({no_back = from_game_over, no_esc = from_game_over, contents ={ + {n=G.UIT.R, config={align = "cm", padding = 0, draw_layer = 1}, nodes={ + create_tabs( + {tabs = { + { + label = localize('b_new_run'), + chosen = (not _challenge_chosen) and (not _can_continue), + tab_definition_function = G.UIDEF.run_setup_option, + tab_definition_function_args = 'New Run' + }, + G.STAGE == G.STAGES.MAIN_MENU and { + label = localize('b_continue'), + chosen = (not _challenge_chosen) and _can_continue, + tab_definition_function = G.UIDEF.run_setup_option, + tab_definition_function_args = 'Continue', + func = 'can_continue' + } or { + label = localize('b_challenges'), + tab_definition_function = G.UIDEF.challenges, + tab_definition_function_args = from_game_over, + chosen = _challenge_chosen + }, + G.STAGE == G.STAGES.MAIN_MENU and { + label = localize('b_challenges'), + tab_definition_function = G.UIDEF.challenges, + tab_definition_function_args = from_game_over, + chosen = _challenge_chosen + } or nil, + }, + snap_to_nav = true}), + }}, + }}) + return t +end + +function G.UIDEF.profile_select() + G.focused_profile = G.focused_profile or G.SETTINGS.profile or 1 + + local t = create_UIBox_generic_options({padding = 0,contents ={ + {n=G.UIT.R, config={align = "cm", padding = 0, draw_layer = 1, minw = 4}, nodes={ + create_tabs( + {tabs = { + { + label = 1, + chosen = G.focused_profile == 1, + tab_definition_function = G.UIDEF.profile_option, + tab_definition_function_args = 1 + }, + { + label = 2, + chosen = G.focused_profile == 2, + tab_definition_function = G.UIDEF.profile_option, + tab_definition_function_args = 2 + }, + { + label = 3, + chosen = G.focused_profile == 3, + tab_definition_function = G.UIDEF.profile_option, + tab_definition_function_args = 3 + } + }, + snap_to_nav = true}), + }}, + }}) + return t +end + +function G.UIDEF.profile_option(_profile) + set_discover_tallies() + G.focused_profile = _profile + local profile_data = get_compressed(G.focused_profile..'/'..'profile.jkr') + if profile_data ~= nil then + profile_data = STR_UNPACK(profile_data) + profile_data.name = profile_data.name or ("P".._profile) + end + G.PROFILES[_profile].name = profile_data and profile_data.name or '' + + local lwidth, rwidth, scale = 1, 1, 1 + G.CHECK_PROFILE_DATA = nil + local t = {n=G.UIT.ROOT, config={align = 'cm', colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = 'cm',padding = 0.1, minh = 0.8}, nodes={ + ((_profile == G.SETTINGS.profile) or not profile_data) and {n=G.UIT.R, config={align = "cm"}, nodes={ + create_text_input({ + w = 4, max_length = 16, prompt_text = localize('k_enter_name'), + ref_table = G.PROFILES[_profile], ref_value = 'name',extended_corpus = true, keyboard_offset = 1, + callback = function() + G:save_settings() + G.FILE_HANDLER.force = true + end + }), + }} or {n=G.UIT.R, config={align = 'cm',padding = 0.1, minw = 4, r = 0.1, colour = G.C.BLACK, minh = 0.6}, nodes={ + {n=G.UIT.T, config={text = G.PROFILES[_profile].name, scale = 0.45, colour = G.C.WHITE}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = 6}, nodes={ + (G.PROFILES[_profile].progress and G.PROFILES[_profile].progress.discovered) and create_progress_box(G.PROFILES[_profile].progress, 0.5) or + {n=G.UIT.C, config={align = "cm", minh = 4, minw = 5.2, colour = G.C.BLACK, r = 0.1}, nodes={ + {n=G.UIT.T, config={text = localize('k_empty_caps'), scale = 0.5, colour = G.C.UI.TRANSPARENT_LIGHT}} + }}, + }}, + {n=G.UIT.C, config={align = "cm", minh = 4}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 1}, nodes={ + profile_data and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = lwidth}, nodes={{n=G.UIT.T, config={text = localize('k_wins'),colour = G.C.UI.TEXT_LIGHT, scale = scale*0.7}}}}, + {n=G.UIT.C, config={align = "cm"}, nodes={{n=G.UIT.T, config={text = ': ',colour = G.C.UI.TEXT_LIGHT, scale = scale*0.7}}}}, + {n=G.UIT.C, config={align = "cl", minw = rwidth}, nodes={{n=G.UIT.T, config={text = tostring(profile_data.career_stats.c_wins),colour = G.C.RED, shadow = true, scale = 1*scale}}}} + }} or nil, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.2}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.R, config={align = "cm", minw = 4, maxw = 4, minh = 0.8, padding = 0.2, r = 0.1, hover = true, colour = G.C.BLUE,func = 'can_load_profile', button = "load_profile", shadow = true, focus_args = {nav = 'wide'}}, nodes={ + {n=G.UIT.T, config={text = _profile == G.SETTINGS.profile and localize('b_current_profile') or profile_data and localize('b_load_profile') or localize('b_create_profile'), ref_value = 'load_button_text', scale = 0.5, colour = G.C.UI.TEXT_LIGHT}} + }} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0, minh = 0.7}, nodes={ + {n=G.UIT.R, config={align = "cm", minw = 3, maxw = 4, minh = 0.6, padding = 0.2, r = 0.1, hover = true, colour = G.C.RED,func = 'can_delete_profile', button = "delete_profile", shadow = true, focus_args = {nav = 'wide'}}, nodes={ + {n=G.UIT.T, config={text = _profile == G.SETTINGS.profile and localize('b_reset_profile') or localize('b_delete_profile'), scale = 0.3, colour = G.C.UI.TEXT_LIGHT}} + }} + }}, + (_profile == G.SETTINGS.profile and not G.PROFILES[G.SETTINGS.profile].all_unlocked) and {n=G.UIT.R, config={align = "cm", padding = 0, minh = 0.7}, nodes={ + {n=G.UIT.R, config={align = "cm", minw = 3, maxw = 4, minh = 0.6, padding = 0.2, r = 0.1, hover = true, colour = G.C.ORANGE,func = 'can_unlock_all', button = "unlock_all", shadow = true, focus_args = {nav = 'wide'}}, nodes={ + {n=G.UIT.T, config={text = localize('b_unlock_all'), scale = 0.3, colour = G.C.UI.TEXT_LIGHT}} + }} + }} or {n=G.UIT.R, config={align = "cm", minw = 3, maxw = 4, minh = 0.7}, nodes={ + G.PROFILES[_profile].all_unlocked and ((not G.F_NO_ACHIEVEMENTS) and {n=G.UIT.T, config={text = localize(G.F_TROPHIES and 'k_trophies_disabled' or 'k_achievements_disabled'), scale = 0.3, colour = G.C.UI.TEXT_LIGHT}} or + nil) or nil + }}, + }}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={id = 'warning_text', text = localize('ph_click_confirm'), scale = 0.4, colour = G.C.CLEAR}} + }} + }} + return t +end + +function G.UIDEF.stake_description(_stake) + local _stake_center = G.P_CENTER_POOLS.Stake[_stake] + local ret_nodes = {} + if _stake_center then localize{type = 'descriptions', key = _stake_center.key, set = _stake_center.set, nodes = ret_nodes} end + + local desc_t = {} + for k, v in ipairs(ret_nodes) do + desc_t[#desc_t+1] = {n=G.UIT.R, config={align = "cm", maxw = 5.3}, nodes=v} + end + + return {n=G.UIT.C, config={align = "cm", padding = 0.05, r = 0.1, colour = G.C.L_BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize{type = 'name_text', key = _stake_center.key, set = _stake_center.set}, scale = 0.35, colour = G.C.WHITE}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.03, colour = G.C.WHITE, r = 0.1, minh = 1, minw = 5.5}, nodes=desc_t} + }} +end + +function G.UIDEF.stake_option(_type) + local middle = {n=G.UIT.R, config={align = "cm", minh = 1.7, minw = 7.3}, nodes={ + {n=G.UIT.O, config={id = nil, func = 'RUN_SETUP_check_stake2', object = Moveable()}}, + }} + + local max_stake = get_deck_win_stake(G.GAME.viewed_back.effect.center.key) + if G.PROFILES[G.SETTINGS.profile].all_unlocked then max_stake = #G.P_CENTER_POOLS['Stake'] end + local stake_options = {} + for i = 1, math.min(max_stake+1, #G.P_CENTER_POOLS['Stake']) do + stake_options[i] = i + end + + return {n=G.UIT.ROOT, config={align = "tm", colour = G.C.CLEAR, minh = 2.03, minw = 8.3}, nodes={_type == 'Continue' and middle or create_option_cycle({options = + stake_options, + opt_callback = 'change_stake', current_option = G.viewed_stake, colour = G.C.RED, w = 6, mid = middle + }) +}} +end + +function G.UIDEF.viewed_stake_option() + G.viewed_stake = G.viewed_stake or 1 + local max_stake = get_deck_win_stake(G.GAME.viewed_back.effect.center.key) + if G.PROFILES[G.SETTINGS.profile].all_unlocked then max_stake = #G.P_CENTER_POOLS['Stake'] end + + G.viewed_stake = math.min(max_stake+1, G.viewed_stake) + if G.viewed_stake > #G.P_CENTER_POOLS.Stake then G.viewed_stake = #G.P_CENTER_POOLS.Stake end + if _type ~= 'Continue' then G.PROFILES[G.SETTINGS.profile].MEMORY.stake = G.viewed_stake end + + local stake_sprite = get_stake_sprite(G.viewed_stake) + + return {n=G.UIT.ROOT, config={align = "cm", colour = G.C.BLACK, r = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize('k_stake'), scale = 0.4, colour = G.C.L_BLACK, vert = true}} + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={colour = G.C.BLUE, object = stake_sprite, hover = true, can_collide = false}}, + }}, + G.UIDEF.stake_description(G.viewed_stake) + }} + }} +end + +function G.UIDEF.challenge_list(from_game_over) + G.CHALLENGE_PAGE_SIZE = 10 + local challenge_pages = {} + for i = 1, math.ceil(#G.CHALLENGES/G.CHALLENGE_PAGE_SIZE) do + table.insert(challenge_pages, localize('k_page')..' '..tostring(i)..'/'..tostring(math.ceil(#G.CHALLENGES/G.CHALLENGE_PAGE_SIZE))) + end + G.E_MANAGER:add_event(Event({func = (function() + G.FUNCS.change_challenge_list_page{cycle_config = {current_option = 1}} + return true end)})) + + local _ch_comp, _ch_tot = 0,#G.CHALLENGES + for k, v in ipairs(G.CHALLENGES) do + if v.id and G.PROFILES[G.SETTINGS.profile].challenge_progress.completed[v.id or ''] then + _ch_comp = _ch_comp + 1 + end + end + + local t = create_UIBox_generic_options({ back_id = from_game_over and 'from_game_over' or nil, back_func = 'setup_run', back_id = 'challenge_list', contents = { + {n=G.UIT.C, config={align = "cm", padding = 0.0}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1, minh = 7, minw = 4.2}, nodes={ + {n=G.UIT.O, config={id = 'challenge_list', object = Moveable()}}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + create_option_cycle({id = 'challenge_page',scale = 0.9, h = 0.3, w = 3.5, options = challenge_pages, cycle_shoulders = true, opt_callback = 'change_challenge_list_page', current_option = 1, colour = G.C.RED, no_pips = true, focus_args = {snap_to = true}}) + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + {n=G.UIT.T, config={text = localize{type = 'variable', key = 'challenges_completed', vars = {_ch_comp, _ch_tot}}, scale = 0.4, colour = G.C.WHITE}}, + }}, + + }}, + {n=G.UIT.C, config={align = "cm", minh = 9, minw = 11.5}, nodes={ + {n=G.UIT.O, config={id = 'challenge_area', object = Moveable()}}, + }}, + }}) + return t +end + +function G.UIDEF.challenge_list_page(_page) + local snapped = false + local challenge_list = {} + for k, v in ipairs(G.CHALLENGES) do + if k > G.CHALLENGE_PAGE_SIZE*(_page or 0) and k <= G.CHALLENGE_PAGE_SIZE*((_page or 0) + 1) then + if G.CONTROLLER.focused.target and G.CONTROLLER.focused.target.config.id == 'challenge_page' then snapped = true end + local challenge_completed = G.PROFILES[G.SETTINGS.profile].challenge_progress.completed[v.id or ''] + local challenge_unlocked = G.PROFILES[G.SETTINGS.profile].challenges_unlocked and (G.PROFILES[G.SETTINGS.profile].challenges_unlocked >= k) + if v.unlocked and type(v.unlocked) == 'function' then + challenge_unlocked = v:unlocked() + elseif type(v.unlocked) == 'boolean' then + challenge_unlocked = v.unlocked + end + challenge_unlocked = challenge_unlocked or G.PROFILES[G.SETTINGS.profile].all_unlocked + + + challenge_list[#challenge_list+1] = + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = 'cl', minw = 0.8}, nodes = { + {n=G.UIT.T, config={text = k..'', scale = 0.4, colour = G.C.WHITE}}, + }}, + UIBox_button({id = k, col = true, label = {challenge_unlocked and localize(v.id, 'challenge_names') or localize('k_locked'),}, button = challenge_unlocked and 'change_challenge_description' or 'nil', colour = challenge_unlocked and G.C.RED or G.C.GREY, minw = 4, scale = 0.4, minh = 0.6, focus_args = {snap_to = not snapped}}), + {n=G.UIT.C, config={align = 'cm', padding = 0.05, minw = 0.6}, nodes = { + {n=G.UIT.C, config={minh = 0.4, minw = 0.4, emboss = 0.05, r = 0.1, colour = challenge_completed and G.C.GREEN or G.C.BLACK}, nodes = { + challenge_completed and {n=G.UIT.O, config={object = Sprite(0,0,0.4,0.4, G.ASSET_ATLAS["icons"], {x=1, y=0})}} or nil + }}, + }}, + }} + snapped = true + end + end + + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.1, colour = G.C.CLEAR}, nodes=challenge_list} +end + +function G.UIDEF.challenge_description(_id, daily, is_row) + local challenge = G.CHALLENGES[_id] + if not challenge then return {n=G.UIT.ROOT, config={align = "cm", colour = G.C.BLACK, minh = 8.82, minw = 11.5, r = 0.1}, nodes={{n=G.UIT.T, config={text = localize('ph_select_challenge'), scale = 0.3, colour = G.C.UI.TEXT_LIGHT}}}} end + + local joker_size = 0.6 + local jokers = CardArea(0,0, + 10*joker_size, + 0.6*G.CARD_H, + {card_limit = get_challenge_rule(challenge, 'modifiers', 'joker_limit') or 5, + card_w = joker_size*G.CARD_W, type = 'title_2', highlight_limit = 0}) + + if challenge.jokers then + for k, v in ipairs(challenge.jokers) do + local card = Card(0,0, G.CARD_W*joker_size, G.CARD_H*joker_size, nil, G.P_CENTERS[v.id], {bypass_discovery_center = true,bypass_discovery_ui = true, bypass_lock=true}) + if v.edition then card:set_edition({[v.edition] = true}, true, true) end + if v.eternal then card:set_eternal(true) end + if v.pinned then card.pinned = true end + jokers:emplace(card) + end + end + + local joker_col = {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.L_BLACK, r = 0.1, maxh = 1.8}, nodes={ + {n=G.UIT.T, config={text = localize('k_jokers_cap'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT, vert = true, shadow = true}}, + {n=G.UIT.C, config={align = "cm", minh = 0.6*G.CARD_H, minw = 5, r = 0.1, colour = G.C.UI.TRANSPARENT_DARK}, nodes={ + jokers and {n=G.UIT.O, config={object = jokers}} or {n=G.UIT.T, config={text = localize('k_none'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT}} + }} + }} + + local consumeables = CardArea(0,0, + 3*joker_size, + 0.6*G.CARD_H, + {card_limit = get_challenge_rule(challenge, 'modifiers', 'consumable_limit') or 2, + card_w = joker_size*G.CARD_W, type = 'title_2', spread = true, highlight_limit = 0}) + + if challenge.consumeables then + for k, v in ipairs(challenge.consumeables) do + local card = Card(0,0, G.CARD_W*joker_size, G.CARD_H*joker_size, nil, G.P_CENTERS[v.id], {bypass_discovery_center = true,bypass_discovery_ui = true, bypass_lock=true}) + if v.edition then card:set_edition({[v.edition] = true}, true, true) end + if v.eternal then card:set_eternal(true) end + consumeables:emplace(card) + end + end + + local consumable_col = {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.L_BLACK, r = 0.1, maxh = 1.8}, nodes={ + {n=G.UIT.T, config={text = localize('k_cap_consumables'), scale = 0.3, colour = G.C.UI.TEXT_LIGHT, vert = true, shadow = true}}, + {n=G.UIT.C, config={align = "cm", minh = 0.6*G.CARD_H, r = 0.1, colour = G.C.UI.TRANSPARENT_DARK}, nodes={ + consumeables and {n=G.UIT.O, config={object = consumeables}} or {n=G.UIT.T, config={text = localize('k_none'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT}}, + }} + }} + + local vouchers = CardArea(0,0, + 3*joker_size, + 0.6*G.CARD_H, + {card_limit = nil, + card_w = joker_size*G.CARD_W, type = 'title_2', spread = true, highlight_limit = 0}) + + if challenge.vouchers then + for k, v in ipairs(challenge.vouchers) do + local card = Card(0,0, G.CARD_W*joker_size, G.CARD_H*joker_size, nil, G.P_CENTERS[v.id], {bypass_discovery_center = true,bypass_discovery_ui = true, bypass_lock=true}) + if v.edition then card:set_edition({[v.edition] = true}, true, true) end + if v.eternal then card:set_eternal(true) end + vouchers:emplace(card) + end + end + + local voucher_col = {n=G.UIT.C, config={align = "cm", padding = 0.05, colour = G.C.L_BLACK, r = 0.1, maxh = 1.8}, nodes={ + {n=G.UIT.T, config={text = localize('k_vouchers_cap'), scale = 0.33, colour = G.C.UI.TEXT_LIGHT, vert = true, shadow = true}}, + {n=G.UIT.C, config={align = "cm", minh = 0.6*G.CARD_H, r = 0.1, colour = G.C.UI.TRANSPARENT_DARK}, nodes={ + vouchers and {n=G.UIT.O, config={object = vouchers}} or {n=G.UIT.T, config={text = localize('k_none'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT}}, + }} + }} + + + + return {n=is_row and G.UIT.R or G.UIT.ROOT, config={align = "cm", r = 0.1, colour = G.C.BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes={ + joker_col, consumable_col, voucher_col + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + create_tabs( + {tabs = { + { + label = localize('b_rules'), + chosen = true, + tab_definition_function = G.UIDEF.challenge_description_tab, + tab_definition_function_args = {_id = _id, _tab = 'Rules'} + }, + { + label = localize('b_restrictions'), + tab_definition_function = G.UIDEF.challenge_description_tab, + tab_definition_function_args = {_id = _id, _tab = 'Restrictions'} + }, + { + label = localize('b_deck'), + tab_definition_function = G.UIDEF.challenge_description_tab, + tab_definition_function_args = {_id = _id, _tab = 'Deck'} + } + }, + tab_h = 5, + padding = 0, + text_scale = 0.36, + scale = 0.85, + no_shoulders = true, + no_loop = true} + )}}, + not is_row and {n=G.UIT.R, config={align = "cm", minh = 0.9}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.1, minh = 0.7, minw = 9, r = 0.1, hover = true, colour = G.C.BLUE, button = "start_challenge_run", shadow = true, id = _id}, nodes={ + {n=G.UIT.T, config={text = localize('b_play_cap'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT,func = 'set_button_pip', focus_args = {button = 'x',set_button_pip = true}}} + }} + }} or nil, + }} +end + +function G.UIDEF.challenge_description_tab(args) + args = args or {} + if args._tab == 'Rules' then + local challenge = G.CHALLENGES[args._id] + local start_rules = {} + local modded_starts = nil + local game_rules = {} + local starting_params = get_starting_params() + local base_modifiers = { + dollars = {value = starting_params.dollars, order = 6}, + discards = {value = starting_params.discards, order = 2}, + hands = {value = starting_params.hands, order = 1}, + reroll_cost = {value = starting_params.reroll_cost, order = 7}, + joker_slots = {value = starting_params.joker_slots, order = 4}, + consumable_slots = {value = starting_params.consumable_slots, order = 5}, + hand_size = {value = starting_params.hand_size, order = 3}, + } + local bonus_mods = 100 + if challenge.rules then + if challenge.rules.modifiers then + for k, v in ipairs(challenge.rules.modifiers) do + base_modifiers[v.id] = {value = v.value, order = base_modifiers[v.id] and base_modifiers[v.id].order or bonus_mods, custom = true, old_val = base_modifiers[v.id].value} + bonus_mods = bonus_mods + 1 + end + end + end + local nu_base_modifiers = {} + for k, v in pairs(base_modifiers) do + v.key = k + nu_base_modifiers[#nu_base_modifiers+1] = v + end + table.sort(nu_base_modifiers, function(a,b) return a.order < b.order end) + for k, v in ipairs(nu_base_modifiers) do + if v.old_val then + modded_starts = modded_starts or {} + modded_starts[#modded_starts+1] = {n=G.UIT.R, config={align = "cl", maxw = 3.5}, nodes= localize{type = 'text', key = 'ch_m_'..v.key, vars = {v.value}, default_col = G.C.L_BLACK}} + + else + start_rules[#start_rules+1] = {n=G.UIT.R, config={align = "cl", maxw =3.5}, nodes= localize{type = 'text', key = 'ch_m_'..v.key, vars = {v.value}, default_col = not v.custom and G.C.UI.TEXT_INACTIVE or nil}} + end + end + + if modded_starts then + start_rules = { + modded_starts and {n=G.UIT.R, config={align = "cl", padding = 0.05}, nodes=modded_starts} or nil, + {n=G.UIT.R, config={align = "cl", padding = 0.05, colour = G.C.GREY}, nodes={}}, + {n=G.UIT.R, config={align = "cl", padding = 0.05}, nodes=start_rules}, + } + end + + if challenge.rules then + if challenge.rules.custom then + for k, v in ipairs(challenge.rules.custom) do + game_rules[#game_rules+1] = {n=G.UIT.R, config={align = "cl"}, nodes= localize{type = 'text', key = 'ch_c_'..v.id, vars = {v.value}}} + end + end + end + if (not start_rules[1]) and (not modded_starts) then start_rules[#start_rules+1] = {n=G.UIT.R, config={align = "cl"}, nodes= localize{type = 'text', key = 'ch_m_none', vars = {}}} end + if not game_rules[1] then game_rules[#game_rules+1] = {n=G.UIT.R, config={align = "cl"}, nodes= localize{type = 'text', key = 'ch_c_none', vars = {}}} end + + local starting_rule_list = {n=G.UIT.C, config={align = "cm", minw = 3, r = 0.1, colour = G.C.BLUE}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.08, minh = 0.6}, nodes={ + {n=G.UIT.T, config={text = localize('k_game_modifiers'), scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 4.1, minw = 4.2, padding = 0.05, r = 0.1, colour = G.C.WHITE}, nodes= start_rules} + }} + + local override_rule_list = {n=G.UIT.C, config={align = "cm", minw = 3, r = 0.1, colour = G.C.BLUE}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.08, minh = 0.6}, nodes={ + {n=G.UIT.T, config={text = localize('k_custom_rules'), scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 4.1, minw = 6.8, maxw = 6.7, padding = 0.05, r = 0.1, colour = G.C.WHITE}, nodes= game_rules} + }} + + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1, colour = G.C.L_BLACK, r = 0.1, minw = 3}, nodes={ + override_rule_list,starting_rule_list + }} + }} + elseif args._tab == 'Restrictions' then + local challenge = G.CHALLENGES[args._id] + + local banned_cards, banned_tags, banned_other = {}, {}, {} + + if challenge.restrictions then + if challenge.restrictions.banned_cards then + local row_cards = {} + local n_rows = math.max(1, math.floor(#challenge.restrictions.banned_cards/10) + 2 - math.floor(math.log(6, #challenge.restrictions.banned_cards))) + local max_width = 1 + for k, v in ipairs(challenge.restrictions.banned_cards) do + local _row = math.floor((k-1)*n_rows/(#challenge.restrictions.banned_cards)+1) + row_cards[_row] = row_cards[_row] or {} + row_cards[_row][#row_cards[_row]+1] = v + if #row_cards[_row] > max_width then max_width = #row_cards[_row] end + end + + local card_size = math.max(0.3, 0.75 - 0.01*(max_width*n_rows)) + + for _, row_card in ipairs(row_cards) do + local banned_card_area = CardArea( + 0,0, + 6.7, + 3.3/n_rows, + {card_limit = nil, type = 'title_2', view_deck = true, highlight_limit = 0, card_w = G.CARD_W*card_size}) + table.insert(banned_cards, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = banned_card_area}} + }} + ) + for k, v in ipairs(row_card) do + local card = Card(0,0, G.CARD_W*card_size, G.CARD_H*card_size, nil, G.P_CENTERS[v.id], {bypass_discovery_center = true,bypass_discovery_ui = true}) + banned_card_area:emplace(card) + end + end + end + if challenge.restrictions.banned_tags then + local tag_tab = {} + for k, v in pairs(challenge.restrictions.banned_tags) do + tag_tab[#tag_tab+1] = G.P_TAGS[v.id] + end + + table.sort(tag_tab, function (a, b) return a.order < b.order end) + + for k, v in ipairs(tag_tab) do + local temp_tag = Tag(v.key) + local temp_tag_ui = temp_tag:generate_UI(1.1 - 0.25*(math.sqrt(#challenge.restrictions.banned_tags))) + table.insert(banned_tags, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + temp_tag_ui + }} + ) + end + end + if challenge.restrictions.banned_other then + local other_tab = {} + for k, v in pairs(challenge.restrictions.banned_other) do + if v.type == 'blind' then + other_tab[#other_tab+1] = G.P_BLINDS[v.id] + end + end + + table.sort(other_tab, function (a, b) return a.order < b.order end) + + for k, v in ipairs(other_tab) do + local temp_blind = AnimatedSprite(0,0,1,1, G.ANIMATION_ATLAS[v.atlas or ''] or G.ANIMATION_ATLAS['blind_chips'], v.pos) + temp_blind:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'} + }) + temp_blind.float = true + temp_blind.states.hover.can = true + temp_blind.states.drag.can = false + temp_blind.states.collide.can = true + temp_blind.config = {blind = v, force_focus = true} + temp_blind.hover = function() + if not G.CONTROLLER.dragging.target or G.CONTROLLER.using_touch then + if not temp_blind.hovering and temp_blind.states.visible then + temp_blind.hovering = true + temp_blind.hover_tilt = 3 + temp_blind:juice_up(0.05, 0.02) + play_sound('chips1', math.random()*0.1 + 0.55, 0.12) + temp_blind.config.h_popup = create_UIBox_blind_popup(v, true) + temp_blind.config.h_popup_config ={align = 'cl', offset = {x=-0.1,y=0},parent = temp_blind} + Node.hover(temp_blind) + end + end + end + temp_blind.stop_hover = function() temp_blind.hovering = false; Node.stop_hover(temp_blind); temp_blind.hover_tilt = 0 end + + table.insert(banned_other, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = temp_blind}} + }} + ) + end + end + end + if not banned_cards[1] then banned_cards[#banned_cards+1] = {n=G.UIT.R, config={align = "cl"}, nodes= localize{type = 'text', key = 'ch_m_none', vars = {}}} end + if not banned_tags[1] then banned_tags[#banned_tags+1] = {n=G.UIT.R, config={align = "cl"}, nodes= localize{type = 'text', key = 'ch_c_none', vars = {}}} end + if not banned_other[1] then banned_other[#banned_other+1] = {n=G.UIT.R, config={align = "cl"}, nodes= localize{type = 'text', key = 'ch_c_none', vars = {}}} end + + local banned_cards = {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.RED}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.08, minh = 0.6}, nodes={ + {n=G.UIT.T, config={text = localize('k_banned_cards'), scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 4.1, minw =7.33, padding = 0.05, r = 0.1, colour = G.C.WHITE}, nodes= + banned_cards + } + }} + + local banned_tags = {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.RED}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.08, minh = 0.6, maxw = 1.48}, nodes={ + {n=G.UIT.T, config={text = localize('k_banned_tags'), scale = 0.3, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 4.1, minw = 1.48, padding = 0.05, r = 0.1, colour = G.C.WHITE}, nodes= + banned_tags} + }} + + local banned_other = {n=G.UIT.C, config={align = "cm", r = 0.1, colour = G.C.RED}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.08, minh = 0.6, maxw = 1.84}, nodes={ + {n=G.UIT.T, config={text = localize('k_other'), scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + }}, + {n=G.UIT.R, config={align = "cm", minh = 4.1, minw = 2, padding = 0.05, r = 0.1, colour = G.C.WHITE}, nodes= + banned_other} + }} + + return {n=G.UIT.ROOT, config={align = "cm", padding = 0.05, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1, colour = G.C.L_BLACK, r = 0.1}, nodes={ + banned_cards, banned_tags, banned_other + }} + }} + elseif args._tab == 'Deck' then + local challenge = G.CHALLENGES[args._id] + local deck_tables = {} + local SUITS = {} + local suit_map = {} + for i = #SMODS.Suit.obj_buffer, 1, -1 do + local suit = SMODS.Suits[SMODS.Suit.obj_buffer[i]] + SUITS[suit.card_key] = {} + suit_map[#suit_map+1] = suit.card_key + end + local card_protos = nil + local _de = nil + if challenge then + _de = challenge.deck + end + + if _de and _de.cards then + card_protos = _de.cards + end + + if not card_protos then + card_protos = {} + for k, v in pairs(G.P_CARDS) do + local _r, _s = SMODS.Ranks[v.value].card_key, SMODS.Suits[v.suit].card_key + local keep, _e, _d, _g = true, nil, nil, nil + if type(SMODS.Ranks[v.value].in_pool) == 'function' and not SMODS.Ranks[v.value]:in_pool({initial_deck = true}) then + keep = false + end + if type(SMODS.Suits[v.suit].in_pool) == 'function' and not SMODS.Suits[v.suit]:in_pool({initial_deck = true}) then + keep = false + end + if _de then + if _de.yes_ranks and not _de.yes_ranks[_r] then keep = false end + if _de.no_ranks and _de.no_ranks[_r] then keep = false end + if _de.yes_suits and not _de.yes_suits[_s] then keep = false end + if _de.no_suits and _de.no_suits[_s] then keep = false end + if _de.enhancement then _e = _de.enhancement end + if _de.edition then _d = _de.edition end + if _de.seal then _g = _de.seal end + end + + if keep then card_protos[#card_protos+1] = {s=_s,r=_r,e=_e,d=_d,g=_g} end + end + end + for k, v in ipairs(card_protos) do + local _card = Card(0,0, G.CARD_W*0.45, G.CARD_H*0.45, G.P_CARDS[v.s..'_'..v.r], G.P_CENTERS[v.e or 'c_base']) + if v.d then _card:set_edition({[v.d] = true}, true, true) end + if v.g then _card:set_seal(v.g, true, true) end + SUITS[v.s][#SUITS[v.s]+1] = _card + end + + local num_suits = 0 + for j = 1, #suit_map do + if SUITS[suit_map[j]][1] then num_suits = num_suits + 1 end + end + for j = 1, #suit_map do + if SUITS[suit_map[j]][1] then + table.sort(SUITS[suit_map[j]], function(a,b) return a:get_nominal() > b:get_nominal() end ) + local view_deck = CardArea( + 0,0, + 5.5*G.CARD_W, + (0.42 - (num_suits <= 4 and 0 or num_suits >= 8 and 0.28 or 0.07 * (num_suits - 4))) * G.CARD_H, + {card_limit = #SUITS[suit_map[j]], type = 'title_2', view_deck = true, highlight_limit = 0, card_w = G.CARD_W*0.5, draw_layers = {'card'}}) + table.insert(deck_tables, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.O, config={object = view_deck}} + }} + ) + + for i = 1, #SUITS[suit_map[j]] do + if SUITS[suit_map[j]][i] then + view_deck:emplace(SUITS[suit_map[j]][i]) + end + end + end + end + return {n=G.UIT.ROOT, config={align = "cm", padding = 0, colour = G.C.BLACK, r = 0.1, minw = 11.4, minh = 4.2}, nodes=deck_tables} + end +end + +function G.UIDEF.run_setup_option(type) + if not G.SAVED_GAME then + G.SAVED_GAME = get_compressed(G.SETTINGS.profile..'/'..'save.jkr') + if G.SAVED_GAME ~= nil then G.SAVED_GAME = STR_UNPACK(G.SAVED_GAME) end + end + + G.SETTINGS.current_setup = type + G.GAME.viewed_back = Back(get_deck_from_name(G.PROFILES[G.SETTINGS.profile].MEMORY.deck)) + + G.PROFILES[G.SETTINGS.profile].MEMORY.stake = G.PROFILES[G.SETTINGS.profile].MEMORY.stake or 1 + + if type == 'Continue' then + + G.viewed_stake = 1 + if G.SAVED_GAME ~= nil then + saved_game = G.SAVED_GAME + local viewed_deck = 'b_red' + for k, v in pairs(G.P_CENTERS) do + if v.name == saved_game.BACK.name then viewed_deck = k end + end + G.GAME.viewed_back:change_to(G.P_CENTERS[viewed_deck]) + G.viewed_stake = saved_game.GAME.stake or 1 + end + end + + if type == 'New Run' then + if G.OVERLAY_MENU then + local seed_toggle = G.OVERLAY_MENU:get_UIE_by_ID('run_setup_seed') + if seed_toggle then seed_toggle.states.visible = true end + end + G.viewed_stake = G.PROFILES[G.SETTINGS.profile].MEMORY.stake or 1 + G.FUNCS.change_stake({to_key = G.viewed_stake}) + else + G.run_setup_seed = nil + if G.OVERLAY_MENU then + local seed_toggle = G.OVERLAY_MENU:get_UIE_by_ID('run_setup_seed') + if seed_toggle then seed_toggle.states.visible = false end + end + end + + local area = CardArea( + G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, + G.CARD_W, + G.CARD_H, + {card_limit = 5, type = 'deck', highlight_limit = 0, deck_height = 0.75, thin_draw = 1}) + + for i = 1, 10 do + local card = Card(G.ROOM.T.x + 0.2*G.ROOM.T.w/2,G.ROOM.T.h, G.CARD_W, G.CARD_H, pseudorandom_element(G.P_CARDS), G.P_CENTERS.c_base, {playing_card = i, viewed_back = true}) + card.sprite_facing = 'back' + card.facing = 'back' + area:emplace(card) + if i == 10 then G.sticker_card = card; card.sticker = get_deck_win_sticker(G.GAME.viewed_back.effect.center) end + end + + local ordered_names, viewed_deck = {}, 1 + for k, v in ipairs(G.P_CENTER_POOLS.Back) do + ordered_names[#ordered_names+1] = v.name + if v.name == G.GAME.viewed_back.name then viewed_deck = k end + end + + local lwidth, rwidth = 1.4, 1.8 + + local type_colour = G.C.BLUE + + local scale = 0.39 + G.setup_seed = '' + + local t = {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR, minh = 6.6, minw = 6}, nodes={ + type == 'Continue' and {n=G.UIT.R, config={align = "tm", minh = 3.8, padding = 0.15}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 3.3, minw = 6.8}, nodes={ + {n=G.UIT.C, config={align = "cm", colour = G.C.BLACK, padding = 0.15, r = 0.1, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", shadow = false}, nodes={ + {n=G.UIT.O, config={object = area}} + }}, + }},{n=G.UIT.C, config={align = "cm", minw = 4, maxw = 4, minh = 1.7, r = 0.1, colour = G.C.L_BLACK, padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1, minw = 4, maxw = 4, minh = 0.6}, nodes={ + {n=G.UIT.O, config={id = nil, func = 'RUN_SETUP_check_back_name', object = Moveable()}}, + }}, + {n=G.UIT.R, config={align = "cm", colour = G.C.WHITE,padding = 0.03, minh = 1.75, r = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = lwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = localize('k_round'),colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cm"}, nodes={{n=G.UIT.T, config={text = ': ',colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cl", minw = rwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = tostring(saved_game.GAME.round),colour = G.C.RED, scale = 0.8*scale}}}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = lwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = localize('k_ante'),colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cm"}, nodes={{n=G.UIT.T, config={text = ': ',colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cl", minw = rwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = tostring(saved_game.GAME.round_resets.ante),colour = G.C.BLUE, scale = 0.8*scale}}}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = lwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = localize('k_money'),colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cm"}, nodes={{n=G.UIT.T, config={text = ': ',colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cl", minw = rwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = localize('$')..format_ui_value(saved_game.GAME.dollars),colour = G.C.ORANGE, scale = 0.8*scale}}}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = lwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = localize('k_best_hand'),colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cm"}, nodes={{n=G.UIT.T, config={text = ': ',colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cl", minw = rwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = number_format(saved_game.GAME.round_scores.hand.amt),colour = G.C.RED, scale = scale_number(saved_game.GAME.round_scores.hand.amt, 0.8*scale, 100000000000)}}}} + }}, + saved_game.GAME.seeded and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = lwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = localize('k_seed'),colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cm"}, nodes={{n=G.UIT.T, config={text = ': ',colour = G.C.UI.TEXT_DARK, scale = scale*0.8}}}}, + {n=G.UIT.C, config={align = "cl", minw = rwidth, maxw = lwidth}, nodes={{n=G.UIT.T, config={text = tostring(saved_game.GAME.pseudorandom.seed),colour = G.C.RED, scale = 0.8*scale}}}} + }} or nil, + }} + }}, + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={id = G.GAME.viewed_back.name, func = 'RUN_SETUP_check_back_stake_column', object = UIBox{definition = G.UIDEF.deck_stake_column(G.GAME.viewed_back.effect.center.key), config = {offset = {x=0,y=0}}}}} + }} + }} + }}}} or + {n=G.UIT.R, config={align = "cm", minh = 3.8}, nodes={ + create_option_cycle({options = ordered_names, opt_callback = 'change_viewed_back', current_option = viewed_deck, colour = G.C.RED, w = 3.5, mid = + {n=G.UIT.R, config={align = "cm", minh = 3.3, minw = 5}, nodes={ + {n=G.UIT.C, config={align = "cm", colour = G.C.BLACK, padding = 0.15, r = 0.1, emboss = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm", shadow = false}, nodes={ + {n=G.UIT.O, config={object = area}} + }}, + }},{n=G.UIT.C, config={align = "cm", minh = 1.7, r = 0.1, colour = G.C.L_BLACK, padding = 0.1}, nodes={ + {n=G.UIT.R, config={align = "cm", r = 0.1, minw = 4, maxw = 4, minh = 0.6}, nodes={ + {n=G.UIT.O, config={id = nil, func = 'RUN_SETUP_check_back_name', object = Moveable()}}, + }}, + {n=G.UIT.R, config={align = "cm", colour = G.C.WHITE, minh = 1.7, r = 0.1}, nodes={ + {n=G.UIT.O, config={id = G.GAME.viewed_back.name, func = 'RUN_SETUP_check_back', object = UIBox{definition = G.GAME.viewed_back:generate_UI(), config = {offset = {x=0,y=0}}}}} + }} + }}, + {n=G.UIT.C, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={id = G.GAME.viewed_back.name, func = 'RUN_SETUP_check_back_stake_column', object = UIBox{definition = G.UIDEF.deck_stake_column(G.GAME.viewed_back.effect.center.key), config = {offset = {x=0,y=0}}}}} + }} + }} + }} + }) + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + type == 'Continue' and {n=G.UIT.R, config={align = "cm", minh = 2.2, minw = 5}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 0.17}, nodes={}}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={id = nil, func = 'RUN_SETUP_check_stake', insta_func = true, object = Moveable()}}, + }} + }} + or {n=G.UIT.R, config={align = "cm", minh = 2.2, minw = 6.8}, nodes={ + {n=G.UIT.O, config={id = nil, func = 'RUN_SETUP_check_stake', insta_func = true, object = Moveable()}}, + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.05, minh = 0.9}, nodes={ + {n=G.UIT.O, config={align = "cm", func = 'toggle_seeded_run', object = Moveable()}, nodes={ + }}, + }}, + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = 2.4, id = 'run_setup_seed'}, nodes={ + type == 'New Run' and create_toggle{col = true, label = localize('k_seeded_run'), label_scale = 0.25, w = 0, scale = 0.7, ref_table = G, ref_value = 'run_setup_seed'} or nil + }}, + {n=G.UIT.C, config={align = "cm", minw = 5, minh = 0.8, padding = 0.2, r = 0.1, hover = true, colour = G.C.BLUE, button = "start_setup_run", shadow = true, func = 'can_start_run'}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0}, nodes={ + {n=G.UIT.T, config={text = localize('b_play_cap'), scale = 0.8, colour = G.C.UI.TEXT_LIGHT,func = 'set_button_pip', focus_args = {button = 'x',set_button_pip = true}}} + }} + }}, + {n=G.UIT.C, config={align = "cm", minw = 2.5}, nodes={}} + }} + }} + return t +end + +function create_button_binding_pip(args) + + local button_sprite_map = { + ['a'] = G.F_SWAP_AB_PIPS and 1 or 0, + ['b'] = G.F_SWAP_AB_PIPS and 0 or 1, + ['x'] = 2, + ['y'] = 3, + ['leftshoulder'] = 4, + ['rightshoulder'] = 5, + ['triggerleft'] = 6, + ['triggerright'] = 7, + ['start'] = 8, + ['back'] = 9, + ['dpadup'] = 10, + ['dpadright'] = 11, + ['dpaddown'] = 12, + ['dpadleft'] = 13, + ['left'] = 14, + ['right'] = 15, + ['leftstick'] = 16, + ['rightstick'] = 17, + ['guide'] = 19 + } + local BUTTON_SPRITE = Sprite(0,0,args.scale or 0.45,args.scale or 0.45,G.ASSET_ATLAS["gamepad_ui"], + {x=button_sprite_map[args.button], + y=G.CONTROLLER.GAMEPAD_CONSOLE == 'Nintendo' and 2 or G.CONTROLLER.GAMEPAD_CONSOLE == 'Playstation' and (G.F_PS4_PLAYSTATION_GLYPHS and 3 or 1) or 0}) + + return {n=G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR}, nodes={ + {n=G.UIT.O, config={object = BUTTON_SPRITE}}, + }} +end + +function create_UIBox_profile_button() + + local letters = {} + if G.F_DISP_USERNAME then + for _, c in utf8.chars(G.F_DISP_USERNAME) do + local _char = c + local leng = G.LANGUAGES['all1'].font.FONT:hasGlyphs(c) + letters[#letters+1] = {n=G.UIT.T, config={lang = G.LANGUAGES[leng and 'all1' or 'all2'],text = _char, scale = 0.3, colour = mix_colours(G.C.GREEN, G.C.WHITE, 0.7), shadow = true}} + end + end + + if not G.PROFILES[G.SETTINGS.profile].name then + G.PROFILES[G.SETTINGS.profile].name = "P"..G.SETTINGS.profile + end + + return {n=G.UIT.ROOT, config = {align = "cm", colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.2, r = 0.1, emboss = 0.1, colour = G.C.L_BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = localize('k_profile'), scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.15, minw = 2, minh = 0.8, maxw = 2, r = 0.1, hover = true, colour = mix_colours(G.C.WHITE, G.C.GREY, 0.2), button = 'profile_select', shadow = true}, nodes={ + {n=G.UIT.T, config={ref_table = G.PROFILES[G.SETTINGS.profile], ref_value = 'name', scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }}, + }} + }}, + G.F_DISP_USERNAME and {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = localize('k_playing_as'), scale = 0.3, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }}, + {n=G.UIT.R, config={align = "cm", minh = 0.12}, nodes={}}, + {n=G.UIT.R, config={align = "cm", maxw = 2}, nodes=letters} + }} or nil, + }} +end + +function create_UIBox_main_menu_buttons() + local text_scale = 0.45 + local language = nil + if not G.F_ENGLISH_ONLY then + language = Sprite(0,0,0.6,0.6,G.ASSET_ATLAS["icons"], {x=2, y=0}) + language.states.drag.can = false + end + local discord = nil + local twitter = nil + if G.F_DISCORD then + discord = Sprite(0,0,0.6,0.6,G.ASSET_ATLAS["icons"], {x=0, y=0}) + discord.states.drag.can = false + twitter = Sprite(0,0,0.6,0.6,G.ASSET_ATLAS["icons"], {x=0, y=1}) + twitter.states.drag.can = false + end + + local quit_func = 'quit' + + local t = { + n=G.UIT.ROOT, config = {align = "cm",colour = G.C.CLEAR}, nodes={ + {n=G.UIT.C, config={align = "bm"}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.2, r = 0.1, emboss = 0.1, colour = G.C.L_BLACK, mid = true}, nodes={ + UIBox_button{id = 'main_menu_play', button = not G.SETTINGS.tutorial_complete and "start_run" or "setup_run", colour = G.C.BLUE, minw = 3.65, minh = 1.55, label = {localize('b_play_cap')}, scale = text_scale*2, col = true}, + {n=G.UIT.C, config={align = "cm"}, nodes={ + UIBox_button{button = 'options', colour = G.C.ORANGE, minw = 2.65, minh = 1.35, label = {localize('b_options_cap')}, scale = text_scale * 1.2, col = true}, + G.F_QUIT_BUTTON and {n=G.UIT.C, config={align = "cm", minw = 0.2}, nodes={}} or nil, + G.F_QUIT_BUTTON and UIBox_button{button = quit_func, colour = G.C.RED, minw = 2.65, minh = 1.35, label = {localize('b_quit_cap')}, scale = text_scale * 1.2, col = true} or nil, + }}, + UIBox_button{id = 'collection_button', button = "your_collection", colour = G.C.PALE_GREEN, minw = 3.65, minh = 1.55, label = {localize('b_collection_cap')}, scale = text_scale*1.5, col = true}, + }}, + }}, + {n=G.UIT.C, config={align = "br", minw = 3.2, padding = 0.1}, nodes={ + G.F_DISCORD and {n=G.UIT.R, config = {align = "cm", padding = 0.2}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, hover = true, colour = mix_colours(G.C.BLUE, G.C.GREY, 0.4), button = 'go_to_discord', shadow = true}, nodes={ + {n=G.UIT.O, config={object = discord}}, + }}, + {n=G.UIT.C, config={align = "cm", padding = 0.1, r = 0.1, hover = true, colour = G.C.BLACK, button = 'go_to_twitter', shadow = true}, nodes={ + {n=G.UIT.O, config={object = twitter}}, + }} + }} or nil, + not G.F_ENGLISH_ONLY and {n=G.UIT.R, config = {align = "cm", padding = 0.2, r = 0.1, emboss = 0.1, colour = G.C.L_BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.15, minw = 1, r = 0.1, hover = true, colour = mix_colours(G.C.WHITE, G.C.GREY, 0.2), button = 'language_selection', shadow = true}, nodes={ + {n=G.UIT.O, config={object = language}}, + {n=G.UIT.T, config={text = G.LANG.label, scale = 0.4, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }} + }} or nil, + }}, + }} + return t +end + +function create_UIBox_main_menu_competittion_name() + G.SETTINGS.COMP.name = '' + local t = { + n=G.UIT.ROOT, config = {align = "cm",colour = G.C.CLEAR}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.2, r = 0.1, emboss = 0.1, colour = G.C.L_BLACK, mid = true}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + create_text_input({ + w = 4, max_length = 16,prompt_text = 'Enter Name', + ref_table = G.SETTINGS.COMP, ref_value = 'name' + }), + }}, + UIBox_button{button = "confirm_contest_name", colour = G.C.PALE_GREEN, minw = 2.65, minh = 1, label = {'Confirm'}, scale = 0.5}, + }}, + }} + return t +end + +function G.UIDEF.language_selector() + local rows = {} + local langs = {} + for k, v in pairs(G.LANGUAGES) do + if not v.omit then + langs[#langs+1] = v + end + end + table.sort(langs, (function(a, b) return a.label < b.label end)) + local _row = {} + for k, v in ipairs(langs) do + if not G.F_HIDE_BETA_LANGS or (not v.beta) then + _row[#_row+1] = {n=G.UIT.C, config={align = "cm", func = 'beta_lang_alert', padding = 0.05, r = 0.1, minh = 0.7, minw = 4.5, button = v.beta and 'warn_lang' or 'change_lang', ref_table = v, colour = v.beta and G.C.RED or G.C.BLUE, hover = true, shadow = true, focus_args = {snap_to = (k == 1)}}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.T, config={text = v.label, lang = v, scale = 0.45, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }} + }} + end + if _row[3] or (k == #langs) then + rows[#rows+1] = {n=G.UIT.R, config={align = "cm", padding = 0.1}, nodes=_row} + _row = {} + end + end + + local discord = nil + discord = Sprite(0,0,0.6,0.6,G.ASSET_ATLAS["icons"], {x=2, y=0}) + discord.states.drag.can = false + + local t = create_UIBox_generic_options({contents ={ + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes=rows}, + G.F_EXTERNAL_LINKS and {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes={ + {n=G.UIT.C, config={align = "cm", padding = 0.1, minw = 4, maxw = 4, r = 0.1, minh = 0.8, hover = true, colour = mix_colours(G.C.GREEN, G.C.GREY, 0.4), button = 'loc_survey', shadow = true}, nodes={ + {n=G.UIT.O, config={object = discord}}, + {n=G.UIT.T, config={text = G.LANG.button, scale = 0.45, colour = G.C.UI.TEXT_LIGHT, shadow = true}} + }}, + }} or nil + }}) + return t +end + +function create_UIBox_highlight(rect) + local t = {n=G.UIT.ROOT, config = {align = "cm", minh = rect.T.h+0.1, minw = rect.T.w+0.15, r = 0.15, colour = G.C.DARK_EDITION}, nodes={ + }} +return t +end + +function create_UIBox_generic_options(args) + args = args or {} + local back_func = args.back_func or "exit_overlay_menu" + local contents = args.contents or ({n=G.UIT.T, config={text = "EMPTY",colour = G.C.UI.RED, scale = 0.4}}) + if args.infotip then + G.E_MANAGER:add_event(Event({ + blocking = false, + blockable = false, + timer = 'REAL', + func = function() + if G.OVERLAY_MENU then + local _infotip_object = G.OVERLAY_MENU:get_UIE_by_ID('overlay_menu_infotip') + if _infotip_object then + _infotip_object.config.object:remove() + _infotip_object.config.object = UIBox{ + definition = overlay_infotip(args.infotip), + config = {offset = {x=0,y=0}, align = 'bm', parent = _infotip_object} + } + end + end + return true + end + })) + end + + return {n=G.UIT.ROOT, config = {align = "cm", minw = G.ROOM.T.w*5, minh = G.ROOM.T.h*5,padding = 0.1, r = 0.1, colour = args.bg_colour or {G.C.GREY[1], G.C.GREY[2], G.C.GREY[3],0.7}}, nodes={ + {n=G.UIT.R, config={align = "cm", minh = 1,r = 0.3, padding = 0.07, minw = 1, colour = args.outline_colour or G.C.JOKER_GREY, emboss = 0.1}, nodes={ + {n=G.UIT.C, config={align = "cm", minh = 1,r = 0.2, padding = 0.2, minw = 1, colour = args.colour or G.C.L_BLACK}, nodes={ + {n=G.UIT.R, config={align = "cm",padding = args.padding or 0.2, minw = args.minw or 7}, nodes= + contents + }, + not args.no_back and {n=G.UIT.R, config={id = args.back_id or 'overlay_menu_back_button', align = "cm", minw = 2.5, button_delay = args.back_delay, padding =0.1, r = 0.1, hover = true, colour = args.back_colour or G.C.ORANGE, button = back_func, shadow = true, focus_args = {nav = 'wide', button = 'b', snap_to = args.snap_back}}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0, no_fill = true}, nodes={ + {n=G.UIT.T, config={id = args.back_id or nil, text = args.back_label or localize('b_back'), scale = 0.5, colour = G.C.UI.TEXT_LIGHT, shadow = true, func = not args.no_pip and 'set_button_pip' or nil, focus_args = not args.no_pip and {button = args.back_button or 'b'} or nil}} + }} + }} or nil + }}, + }}, + {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={id = 'overlay_menu_infotip', object = Moveable()}}, + }}, + }} +end + +function UIBox_dyn_container(inner_table, horizontal, colour_override, background_override, flipped, padding) + return {n=G.UIT.R, config = {align = "cm", padding= 0.03, colour = G.C.UI.TRANSPARENT_DARK, r=0.1}, nodes={ + {n=G.UIT.R, config = {align = "cm", padding= 0.05, colour = colour_override or G.C.DYN_UI.MAIN, r=0.1}, nodes={ + {n=G.UIT.R, config={align = horizontal and "cl" or (flipped and 'bm' or "tm"), colour = background_override or G.C.DYN_UI.BOSS_DARK, minw = horizontal and 100 or 0, minh = horizontal and 0 or 30, r=0.1, padding = padding or 0.08}, nodes= + inner_table + }}}}} +end + +function simple_text_container(_loc, args) + if not _loc then return nil end + args = args or {} + local container = {} + local loc_result = localize(_loc) + if loc_result and type(loc_result) == 'table' then + for k, v in ipairs(loc_result) do + container[#container+1] = + {n=G.UIT.R, config = {align = "cm", padding= 0}, nodes={ + {n=G.UIT.T, config={text = v, scale = args.scale or 0.35, colour = args.colour or G.C.UI.TEXT_DARK, shadow = args.shadow}} + }} + end + return {n=args.col and G.UIT.C or G.UIT.R, config = {align = "cm", padding= args.padding or 0.03}, nodes=container} + end +end + +function UIBox_button(args) + args = args or {} + args.button = args.button or "exit_overlay_menu" + args.func = args.func or nil + args.colour = args.colour or G.C.RED + args.choice = args.choice or nil + args.chosen = args.chosen or nil + args.label = args.label or {'LABEL'} + args.minw = args.minw or 2.7 + args.maxw = args.maxw or (args.minw - 0.2) + if args.minw < args.maxw then args.maxw = args.minw - 0.2 end + args.minh = args.minh or 0.9 + args.scale = args.scale or 0.5 + args.focus_args = args.focus_args or nil + args.text_colour = args.text_colour or G.C.UI.TEXT_LIGHT + local but_UIT = args.col == true and G.UIT.C or G.UIT.R + + local but_UI_label = {} + + local button_pip = nil + if args.dynamic_label then + but_UI_label = {} + + table.insert(but_UI_label, {n=G.UIT.R, config={align = "cm", padding = 0, minw = args.minw, maxw = args.maxw}, nodes={ + {n=G.UIT.T, config={ref_table = args.dynamic_label, ref_value = 'text', scale = args.scale, colour = args.text_colour, shadow = args.shadow, focus_args = button_pip and args.focus_args or nil, func = button_pip,}} + }}) + end + for k, v in ipairs(args.label) do + if k == #args.label and args.focus_args and args.focus_args.set_button_pip then + button_pip ='set_button_pip' + end + table.insert(but_UI_label, {n=G.UIT.R, config={align = "cm", padding = 0, minw = args.minw, maxw = args.maxw}, nodes={ + {n=G.UIT.T, config={text = v, scale = args.scale, colour = args.text_colour, shadow = args.shadow, focus_args = button_pip and args.focus_args or nil, func = button_pip, ref_table = args.ref_table}} + }}) + end + + if args.count then + table.insert(but_UI_label, + {n=G.UIT.R, config={align = "cm", minh = 0.4}, nodes={ + {n=G.UIT.T, config={scale = 0.35,text = args.count.tally..' / '..args.count.of, colour = {1,1,1,0.9}}} + }} + ) + end + + return + {n= but_UIT, config = {align = 'cm'}, nodes={ + {n= G.UIT.C, config={ + align = "cm", + padding = args.padding or 0, + r = 0.1, + hover = true, + colour = args.ref_table and args.ref_table.colour or args.colour, -- Cartomancer + one_press = args.one_press, + button = (args.button ~= 'nil') and args.button or nil, + choice = args.choice, + chosen = args.chosen, + focus_args = args.focus_args, + minh = args.minh - 0.3*(args.count and 1 or 0), + shadow = true, + func = args.func, + id = args.id, + back_func = args.back_func, + ref_table = args.ref_table, + mid = args.mid + }, nodes= + args.ref_table and args.ref_table.custom_button or but_UI_label -- Cartomancer + }}} +end diff --git a/lovely/dump/functions/button_callbacks.lua b/lovely/dump/functions/button_callbacks.lua new file mode 100644 index 0000000..3351466 --- /dev/null +++ b/lovely/dump/functions/button_callbacks.lua @@ -0,0 +1,3237 @@ +LOVELY_INTEGRITY = '8857de192c196c2108ac03f2ec1ec6974c1d8c7ef54d7b5fbb92ce6db79734e0' + +--Moves the tutorial to the next step in queue +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.tut_next = function(e) + if G.OVERLAY_TUTORIAL then + G.OVERLAY_TUTORIAL.Jimbo:remove_button() + G.OVERLAY_TUTORIAL.Jimbo:remove_speech_bubble() + G.OVERLAY_TUTORIAL.step_complete = false + G.OVERLAY_TUTORIAL.step = G.OVERLAY_TUTORIAL.step+1 + end +end + +--Ensures the compatibility indicator for the Blueprint and Brainstorm Jokers +--matches with any new changes of compatibility determined by the Joker +-- +---@param e {} +--**e** Is the UIE that called this function\ +--**e.config.ref_table** points to the joker +G.FUNCS.blueprint_compat = function(e) + if e.config.ref_table.ability.blueprint_compat ~= e.config.ref_table.ability.blueprint_compat_check then + if e.config.ref_table.ability.blueprint_compat == 'compatible' then + e.config.colour = mix_colours(G.C.GREEN, G.C.JOKER_GREY, 0.8) + elseif e.config.ref_table.ability.blueprint_compat == 'incompatible' then + e.config.colour = mix_colours(G.C.RED, G.C.JOKER_GREY, 0.8) + end + e.config.ref_table.ability.blueprint_compat_ui = ' '..localize('k_'..e.config.ref_table.ability.blueprint_compat)..' ' + e.config.ref_table.ability.blueprint_compat_check = e.config.ref_table.ability.blueprint_compat + end +end + +--Sorts G.hand in descending order by suit (spades, hearts, clubs, diamonds, then rank) +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.sort_hand_suit = function(e) + G.hand:sort('suit desc') + play_sound('paper1') +end + +--Sorts G.hand in descending order by rank (rank, then spades, hearts, clubs, diamonds) +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.sort_hand_value = function(e) + G.hand:sort('desc') + play_sound('paper1') +end + +--Checks if the cost of a non voucher card is greater than what the player can afford and changes the +--buy button visuals accordingly +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.can_buy = function(e) + if (e.config.ref_table.cost > G.GAME.dollars - G.GAME.bankrupt_at) and (e.config.ref_table.cost > 0) then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.ORANGE + e.config.button = 'buy_from_shop' + end + if e.config.ref_parent and e.config.ref_parent.children.buy_and_use then + if e.config.ref_parent.children.buy_and_use.states.visible then + e.UIBox.alignment.offset.y = -0.6 + else + e.UIBox.alignment.offset.y = 0 + end + end +end + +--Checks if the cost of a non voucher card is greater than what the player can afford and changes the +--buy button visuals accordingly +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.can_buy_and_use = function(e) + if (((e.config.ref_table.cost > G.GAME.dollars - G.GAME.bankrupt_at) and (e.config.ref_table.cost > 0)) or (not e.config.ref_table:can_use_consumeable())) then + e.UIBox.states.visible = false + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + if e.config.ref_table.highlighted then + e.UIBox.states.visible = true + end + e.config.colour = G.C.SECONDARY_SET.Voucher + e.config.button = 'buy_from_shop' + end +end + +--Checks if the cost of a voucher card is greater than what the player can afford and changes the +--redeem button visuals accordingly +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.can_redeem = function(e) + if e.config.ref_table.cost > G.GAME.dollars - G.GAME.bankrupt_at then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.GREEN + e.config.button = 'use_card' + end +end + +--Checks if the cost of a booster pack is too much +--adjusts booster button visuals accordingly +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.can_open = function(e) + if (e.config.ref_table.cost) > 0 and (e.config.ref_table.cost > G.GAME.dollars - G.GAME.bankrupt_at) then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.GREEN + e.config.button = 'use_card' + end +end + +--ensures that the HUD blind section is only visible when there is an active blind +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.HUD_blind_visible = function(e) + if G.GAME.blind and (G.GAME.blind.name ~= '' and G.GAME.blind.blind_set) then + G.GAME.blind.states.visible = true + elseif G.GAME.blind then + G.GAME.blind.states.visible = false + end +end + +--Expands or contracts the 'debuff text' area of the blind HUD when it changes, +--either bigger with a new boss or smaller when it is disabled, or for a smaller blind +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.HUD_blind_debuff = function(e) + if G.GAME.blind and G.GAME.blind.loc_debuff_text and G.GAME.blind.loc_debuff_text ~= '' then + if e.parent.config.minh == 0 or e.config.prev_loc ~= G.GAME.blind.loc_debuff_text then + e.parent.config.minh = 0.35 + e.config.scale = 0.36 + if G.GAME.blind.loc_debuff_lines[e.config.ref_value] == '' then e.config.scale = 0.0; e.parent.config.minh = 0.001 end + e.config.prev_loc = G.GAME.blind.loc_debuff_text + e.UIBox:recalculate(true) + end + else + if e.parent.config.minh > 0 then + e.parent.config.minh = 0 + e.config.scale = 0 + e.UIBox:recalculate(true) + end + end +end + +--Adds the prefix for the debuff text for the wheel blind +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.HUD_blind_debuff_prefix = function(e) + if (G.GAME.blind and G.GAME.blind.name == 'The Wheel' and not G.GAME.blind.disabled) or + e.config.id == 'bl_wheel' then + e.config.ref_table.val = ''..G.GAME.probabilities.normal + e.config.scale = 0.32 + else + e.config.ref_table.val = '' + e.config.scale = 0 + end +end + +G.FUNCS.HUD_blind_reward = function(e) + if G.GAME.modifiers.no_blind_reward and (G.GAME.blind and G.GAME.modifiers.no_blind_reward[G.GAME.blind:get_type()]) then + if e.config.minh > 0.44 then + e.config.minh = 0.4 + e.children[1].config.text = localize('k_no_reward') + --e.children[2].states.visible = false + e.UIBox:recalculate(true) + end + else + if e.config.minh < 0.45 then + e.config.minh = 0.45 + e.children[1].config.text = localize('k_reward')..': ' + e.children[2].states.visible = true + e.UIBox:recalculate(true) + end + end +end + +--Determines if there is a valid save file to load and continue from main menu +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.can_continue = function(e) + if e.config.func then --refers to this function, or 'can_continue', so this doesn't run repeatedly + local _can_continue = nil + local savefile = love.filesystem.getInfo(G.SETTINGS.profile..'/'..'save.jkr') + if savefile == nil then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + if not G.SAVED_GAME then + G.SAVED_GAME = get_compressed(G.SETTINGS.profile..'/'..'save.jkr') + if G.SAVED_GAME ~= nil then G.SAVED_GAME = STR_UNPACK(G.SAVED_GAME) end + if G.SAVED_GAME == nil then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + return _can_continue + end + end + if not G.SAVED_GAME or not G.SAVED_GAME.VERSION or G.SAVED_GAME.VERSION < '0.9.2' then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + _can_continue = true + end + end + e.config.func = nil + return _can_continue + end +end + +G.FUNCS.can_load_profile = function(e) + if G.SETTINGS.profile == G.focused_profile then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.BLUE + e.config.button = 'load_profile' + end +end + +G.FUNCS.load_profile = function(delete_prof_data) + G.SAVED_GAME = nil + G.E_MANAGER:clear_queue() + G.FUNCS.wipe_on() + G.E_MANAGER:add_event(Event({ + no_delete = true, + func = function() + G:delete_run() + local _name = nil + if G.PROFILES[G.focused_profile].name and G.PROFILES[G.focused_profile].name ~= '' then + _name = G.PROFILES[G.focused_profile].name + end + if delete_prof_data then G.PROFILES[G.focused_profile] = {} end + G.DISCOVER_TALLIES = nil + G.PROGRESS = nil + G:load_profile(G.focused_profile) + G.PROFILES[G.focused_profile].name = _name + G:init_item_prototypes() + return true + end + })) + G.E_MANAGER:add_event(Event({ + no_delete = true, + blockable = true, + blocking = false, + func = function() + G:main_menu() + G.FILE_HANDLER.force = true + return true + end + })) + G.FUNCS.wipe_off() +end + +G.FUNCS.can_delete_profile = function(e) + G.CHECK_PROFILE_DATA = G.CHECK_PROFILE_DATA or love.filesystem.getInfo(G.focused_profile..'/'..'profile.jkr') + if (not G.CHECK_PROFILE_DATA) or e.config.disable_button then + G.CHECK_PROFILE_DATA = false + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.RED + e.config.button = 'delete_profile' + end +end + +G.FUNCS.delete_profile = function(e) + local warning_text = e.UIBox:get_UIE_by_ID('warning_text') + if warning_text.config.colour ~= G.C.WHITE then + warning_text:juice_up() + warning_text.config.colour = G.C.WHITE + warning_text.config.shadow = true + e.config.disable_button = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.35, blockable = false, blocking = false, func = function() + e.config.disable_button = nil;return true end})) + + play_sound('tarot2', 1, 0.4) + else + love.filesystem.remove(G.focused_profile..'/'..'profile.jkr') + love.filesystem.remove(G.focused_profile..'/'..'save.jkr') + love.filesystem.remove(G.focused_profile..'/'..'meta.jkr') + love.filesystem.remove(G.focused_profile..'/'..'unlock_notify.jkr') + love.filesystem.remove(G.focused_profile..'') + G.SAVED_GAME = nil + G.DISCOVER_TALLIES = nil + G.PROGRESS = nil + G.PROFILES[G.focused_profile] = {} + if G.focused_profile == G.SETTINGS.profile then + G.FUNCS.load_profile(true) + else + local tab_but = G.OVERLAY_MENU:get_UIE_by_ID('tab_but_'..G.focused_profile) + G.FUNCS.change_tab(tab_but) + end + end +end + +G.FUNCS.can_unlock_all = function(e) + if G.PROFILES[G.SETTINGS.profile].all_unlocked or e.config.disable_button then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.GREY + e.config.button = 'unlock_all' + end +end + +G.FUNCS.unlock_all = function(e) + local _infotip_object = G.OVERLAY_MENU:get_UIE_by_ID('overlay_menu_infotip') + + if (not _infotip_object.config.set) and (not G.F_NO_ACHIEVEMENTS) then + _infotip_object.config.object:remove() + _infotip_object.config.object = UIBox{ + definition = overlay_infotip(localize(G.F_TROPHIES and 'ml_unlock_all_trophies' or 'ml_unlock_all_explanation')), + config = {offset = {x=0,y=0}, align = 'bm', parent = _infotip_object} + } + _infotip_object.config.object.UIRoot:juice_up() + _infotip_object.config.set = true + e.config.disable_button = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.35, blockable = false, blocking = false, func = function() + e.config.disable_button = nil;return true end})) + + play_sound('tarot2', 1, 0.4) + else + G.PROFILES[G.SETTINGS.profile].all_unlocked = true + for k, v in pairs(G.P_CENTERS) do + if not v.demo and not v.wip then + v.alerted = true + v.discovered = true + v.unlocked = true + end + end + for k, v in pairs(G.P_BLINDS) do + if not v.demo and not v.wip then + v.alerted = true + v.discovered = true + v.unlocked = true + end + end + for k, v in pairs(G.P_TAGS) do + if not v.demo and not v.wip then + v.alerted = true + v.discovered = true + v.unlocked = true + end + end + set_profile_progress() + set_discover_tallies() + G:save_progress() + G.FILE_HANDLER.force = true + + local tab_but = G.OVERLAY_MENU:get_UIE_by_ID('tab_but_'..G.focused_profile) + G.FUNCS.change_tab(tab_but) + end +end + +--Creates an alert on this UIE if the round score for this id is a career high score +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.high_score_alert = function(e) + if e.config.id and not e.children.alert then + if G.GAME.round_scores[e.config.id] and G.GAME.round_scores[e.config.id].high_score then + e.children.alert = UIBox{ + definition = create_UIBox_card_alert({no_bg = true,text = localize('k_high_score_ex'), scale = 0.3}), + config = { + instance_type = 'ALERT', + align="tri", + offset = {x = 0.3, y = -0.18}, + major = e, parent = e} + } + e.children.alert.states.collide.can = false + end + end +end + +G.FUNCS.beta_lang_alert = function(e) + if not e.children.alert then + if e.config.ref_table and e.config.ref_table.beta then + e.children.alert = UIBox{ + definition = create_UIBox_card_alert({no_bg = true, text = 'BETA', scale = 0.35}), + config = { + instance_type = 'ALERT', + align="tri", + offset = {x = 0.07, y = -0.07}, + major = e, parent = e} + } + e.children.alert.states.collide.can = false + end + end +end + +--Creates a binding pip on this UIE if controller is being used +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.set_button_pip = function(e) + if G.CONTROLLER.HID.controller and e.config.focus_args and not e.children.button_pip then + e.children.button_pip = UIBox{ + definition = create_button_binding_pip{button = e.config.focus_args.button, scale = e.config.focus_args.scale}, + config = { + align= e.config.focus_args.orientation or 'cr', + offset = e.config.focus_args.offset or e.config.focus_args.orientation == 'bm' and {x = 0, y = 0.02} or {x = 0.1, y = 0.02}, + major = e, parent = e} + } + e.children.button_pip.states.collide.can = false + end + if not G.CONTROLLER.HID.controller and e.children.button_pip then + e.children.button_pip:remove() + e.children.button_pip = nil + end +end + +--Flashes text input cursor for the hooked text input, otherwise sets the width and alpha to 0 +-- +---@param e {} +--**e** Is the UIE cursor that called this function +G.FUNCS.flash = function(e) + if G.CONTROLLER.text_input_hook and G.CONTROLLER.text_input_id == e.config.id:sub(1,string.len(G.CONTROLLER.text_input_id)) then + if (math.floor(G.TIMERS.REAL*2))%2 == 1 then + e.config.colour[4] = 0 + else + e.config.colour[4] = 1 + end + if e.config.w ~= 0.1 then e.config.w = 0.1; e.UIBox:recalculate(true) end + else + e.config.colour[4] = 0 + if e.config.w ~= 0 then e.config.w = 0; e.UIBox:recalculate(true) end + end +end + +--highlights/lowlights the pips for any dynatext with multiple values based on which one is displaying +-- +---@param e {} +--**e** Is the dynatext that called this function +G.FUNCS.pip_dynatext = function(e) + if 'pip_'..tostring(e.config.ref_table.focused_string) == e.config.id then + if e.config.pip_state ~= 1 then + e.config.colour = e.config.pipcol1 + e.config.pip_state = 1 + end + elseif e.config.pip_state ~= 2 then + e.config.colour = e.config.pipcol2 + e.config.pip_state = 2 + end +end + +--for the toggle +-- +---@param e {} +--**e** Is the slider UIE that called this function +function G.FUNCS.toggle_button(e) + e.config.ref_table.ref_table[e.config.ref_table.ref_value] = not e.config.ref_table.ref_table[e.config.ref_table.ref_value] + if e.config.toggle_callback then + e.config.toggle_callback(e.config.ref_table.ref_table[e.config.ref_table.ref_value]) + end +end + +--for the toggle +-- +---@param e {} +--**e** Is the slider UIE that called this function +function G.FUNCS.toggle(e) + if not e.config.ref_table.ref_table[e.config.ref_table.ref_value] and e.config.toggle_active then + e.config.toggle_active = nil + e.config.colour = e.config.ref_table.inactive_colour + e.children[1].states.visible = false + e.children[1].config.object.states.visible = false + elseif e.config.ref_table.ref_table[e.config.ref_table.ref_value] and not e.config.toggle_active then + e.config.toggle_active = true + e.config.colour = e.config.ref_table.active_colour + e.children[1].states.visible = true + e.children[1].config.object.states.visible = true + end + +end + + +--Modifies the slider value if it is being dragged. e contains the 'container' for the bar and +--c contains the 'child' for the bar. either can be dragged. The value is lerped between the size +--of the child bar and the parent bar depending on any min/max values. Also changes the display text for the slider. +-- +---@param e {} +--**e** Is the slider UIE that called this function +function G.FUNCS.slider(e) + local c = e.children[1] + e.states.drag.can = true + c.states.drag.can = true + if G.CONTROLLER and G.CONTROLLER.dragging.target and + (G.CONTROLLER.dragging.target == e or + G.CONTROLLER.dragging.target == c) then + local rt = c.config.ref_table + rt.ref_table[rt.ref_value] = math.min(rt.max,math.max(rt.min, rt.min + (rt.max - rt.min)*(G.CURSOR.T.x - e.parent.T.x - G.ROOM.T.x)/e.T.w)) + rt.text = string.format("%."..tostring(rt.decimal_places).."f", rt.ref_table[rt.ref_value]) + c.T.w = (rt.ref_table[rt.ref_value] - rt.min)/(rt.max - rt.min)*rt.w + c.config.w = c.T.w + if rt.callback then G.FUNCS[rt.callback](rt) end + end +end + +--Modifies the slider value descreetly by percentage +--c contains the 'child' for the bar. either can be dragged. The value is lerped between the size +--of the child bar and the parent bar depending on any min/max values. Also changes the display text for the slider. +-- +---@param e {} +--**e** Is the slider UIE that called this function +function G.FUNCS.slider_descreet(e, per) + local c = e.children[1] + e.states.drag.can = true + c.states.drag.can = true + if per then + local rt = c.config.ref_table + rt.ref_table[rt.ref_value] = math.min(rt.max,math.max(rt.min, rt.ref_table[rt.ref_value] + per*(rt.max - rt.min))) + rt.text = string.format("%."..tostring(rt.decimal_places).."f", rt.ref_table[rt.ref_value]) + c.T.w = (rt.ref_table[rt.ref_value] - rt.min)/(rt.max - rt.min)*rt.w + c.config.w = c.T.w + end +end + +--When clicked, changes the selected option from an option cycle. Wraps around. +--Modifies any pips to show the currently selected option and resets last pip. +--Calls any functions from opt_callback defined in the option cycle when the value changes. +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.option_cycle = function(e) + local from_val = e.config.ref_table.options[e.config.ref_table.current_option] + local from_key = e.config.ref_table.current_option + local old_pip = e.UIBox:get_UIE_by_ID('pip_'..e.config.ref_table.current_option, e.parent.parent) + local cycle_main = e.UIBox:get_UIE_by_ID('cycle_main', e.parent.parent) + + if cycle_main and cycle_main.config.h_popup then + cycle_main:stop_hover() + G.E_MANAGER:add_event(Event({ + func = function() + cycle_main:hover() + return true + end + })) + end + + if e.config.ref_value == 'l' then + --cycle left + e.config.ref_table.current_option = e.config.ref_table.current_option - 1 + if e.config.ref_table.current_option <= 0 then e.config.ref_table.current_option = #e.config.ref_table.options end + else + --cycle right + e.config.ref_table.current_option = e.config.ref_table.current_option + 1 + if e.config.ref_table.current_option > #e.config.ref_table.options then e.config.ref_table.current_option = 1 end + end + local to_val = e.config.ref_table.options[e.config.ref_table.current_option] + local to_key = e.config.ref_table.current_option + e.config.ref_table.current_option_val = e.config.ref_table.options[e.config.ref_table.current_option] + + local new_pip = e.UIBox:get_UIE_by_ID('pip_'..e.config.ref_table.current_option, e.parent.parent) + + if old_pip then old_pip.config.colour = G.C.BLACK end + if new_pip then new_pip.config.colour = G.C.WHITE end + + if e.config.ref_table.opt_callback then + G.FUNCS[e.config.ref_table.opt_callback]{ + from_val = from_val, + to_val = to_val, + from_key = from_key, + to_key = to_key, + cycle_config = e.config.ref_table + } + end +end + +--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +-- CYCLE CALLBACKS +--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +--Generalized test framework callback for any testing option cycles +-- +---@param args {cycle_config: table, to_val: value} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_val** is the value the option is changing to +G.FUNCS.test_framework_cycle_callback = function(args) + args = args or {} + if args.cycle_config and args.cycle_config.ref_table and args.cycle_config.ref_value then + args.cycle_config.ref_table[args.cycle_config.ref_value] = args.to_val + end +end + +--Changing the current page being viewed for the Joker Collection +-- +---@param args {cycle_config: table} +--**cycle_config** Is the config table from the original option cycle UIE +G.FUNCS.your_collection_joker_page = function(args) + if not args or not args.cycle_config then return end + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards,1, -1 do + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + local joker_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Joker) + for i = 1, 5 do + for j = 1, #G.your_collection do + local center = joker_pool[i+(j-1)*5 + (5*#G.your_collection*(args.cycle_config.current_option - 1))] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + card.sticker = get_joker_win_sticker(center) + G.your_collection[j]:emplace(card) + end + end + INIT_COLLECTION_CARD_ALERTS() +end + +--Changing the current page being viewed for the tarot and planet card collection +-- +---@param args {cycle_config: table} +--**cycle_config** Is the config table from the original option cycle UIE +G.FUNCS.your_collection_tarot_page = function(args) + if not args or not args.cycle_config then return end + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards,1, -1 do + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + + for j = 1, #G.your_collection do + for i = 1, 4+j do + local center = G.P_CENTER_POOLS["Tarot"][i+(j-1)*(5) + (11*(args.cycle_config.current_option - 1))] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + INIT_COLLECTION_CARD_ALERTS() +end + +G.FUNCS.your_collection_spectral_page = function(args) + if not args or not args.cycle_config then return end + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards,1, -1 do + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + + for j = 1, #G.your_collection do + for i = 1, 3+j do + local center = G.P_CENTER_POOLS["Spectral"][i+(j-1)*(4) + (9*(args.cycle_config.current_option - 1))] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + INIT_COLLECTION_CARD_ALERTS() +end + +--Changing the current page being viewed for the booster pack card collection +-- +---@param args {cycle_config: table} +--**cycle_config** Is the config table from the original option cycle UIE +G.FUNCS.your_collection_booster_page = function(args) +local booster_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Booster) + if not args or not args.cycle_config then return end + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards,1, -1 do + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + + for j = 1, #G.your_collection do + for i = 1, 4 do + local center = booster_pool[i+(j-1)*4 + (8*(args.cycle_config.current_option - 1))] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W*1.27, G.CARD_H*1.27, nil, center) + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + INIT_COLLECTION_CARD_ALERTS() +end + +--Changing the current page being viewed for the voucher collection +-- +---@param args {cycle_config: table} +--**cycle_config** Is the config table from the original option cycle UIE +G.FUNCS.your_collection_voucher_page = function(args) +local voucher_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Voucher) + if not args or not args.cycle_config then return end + for j = 1, #G.your_collection do + for i = #G.your_collection[j].cards,1, -1 do + local c = G.your_collection[j]:remove_card(G.your_collection[j].cards[i]) + c:remove() + c = nil + end + end + for i = 1, 4 do + for j = 1, #G.your_collection do + local center = voucher_pool[i+(j-1)*4 + (8*(args.cycle_config.current_option - 1))] + if not center then break end + local card = Card(G.your_collection[j].T.x + G.your_collection[j].T.w/2, G.your_collection[j].T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, center) + card:start_materialize(nil, i>1 or j>1) + G.your_collection[j]:emplace(card) + end + end + INIT_COLLECTION_CARD_ALERTS() +end + +--Changing the selected card back +-- +---@param args {to_val: value} +--**to_val** Deck back name being changed to +G.FUNCS.change_selected_back = function(args) + G.GAME.selected_back:change_to(G.P_CENTER_POOLS.Back[args.to_key]) +end + +--Changing the collection viewed card back +-- +---@param args {to_val: value} +--**to_val** Deck back name being changed to +G.FUNCS.change_viewed_back = function(args) + G.viewed_stake = G.viewed_stake or 1 + local deck_pool = SMODS.collection_pool(G.P_CENTER_POOLS.Back) + G.GAME.viewed_back:change_to(deck_pool[args.to_key]) + if G.sticker_card then G.sticker_card.sticker = get_deck_win_sticker(G.GAME.viewed_back.effect.center) end + local max_stake = get_deck_win_stake(G.GAME.viewed_back.effect.center.key) or 0 + G.viewed_stake = math.min(G.viewed_stake, max_stake + 1) + G.PROFILES[G.SETTINGS.profile].MEMORY.deck = args.to_val + for key, val in pairs(G.sticker_card.area.cards) do + val.children.back = false + val:set_ability(val.config.center, true) + end +end + +G.FUNCS.change_stake = function(args) + G.viewed_stake = args.to_key + G.PROFILES[G.SETTINGS.profile].MEMORY.stake = args.to_key +end + +--Switch VSync on or off - add the change to the settings change queue +-- +---@param args {to_key: key} +--**to_key** new VSync key setting, 1 for On, 2 for Off +G.FUNCS.change_vsync = function(args) + G.SETTINGS.QUEUED_CHANGE.vsync = (G.SETTINGS.WINDOW.vsync == 0 and args.to_key == 1 and 1) or (G.SETTINGS.WINDOW.vsync == 1 and args.to_key == 2 and 0) or nil +end + +--Changes the screen resolution to the cycled resolution.\ +--Note - an issue with windows scaling above 100% means that these resolutions may not match the actual monitor resolution, +--they are more like render resolutions adjusted to fit the screen with scaling +-- +---@param args {cycle_config: table, to_key: key} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_key** The new resolution setting, refers to a resolution table generated with the option cycle +G.FUNCS.change_screen_resolution = function(args) + local curr_disp = G.SETTINGS.WINDOW.selected_display + local to_resolution = G.SETTINGS.WINDOW.DISPLAYS[curr_disp].screen_resolutions.values[args.to_key] + G.SETTINGS.QUEUED_CHANGE.screenres = {w = to_resolution.w, h = to_resolution.h} + +end + +--Changes the screen mode\ +--Options: Windowed, Fullscreen, Borderless +-- +---@param args {cycle_config: table, to_val: value} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_val** The new screenmode setting value +G.FUNCS.change_screenmode = function(args) + G.ARGS.screenmode_vals = G.ARGS.screenmode_vals or {"Windowed", "Fullscreen", "Borderless"} + G.SETTINGS.QUEUED_CHANGE.screenmode = G.ARGS.screenmode_vals[args.to_key] + G.FUNCS.change_window_cycle_UI() +end + +--Changes the displaying monitors +-- +---@param args {cycle_config: table, to_key: key} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_key** The new screenmode setting key +G.FUNCS.change_display = function(args) + G.SETTINGS.QUEUED_CHANGE.selected_display = args.to_key + G.FUNCS.change_window_cycle_UI() +end + +--Helper function to re-add the resolution cycle UIE with updated data +G.FUNCS.change_window_cycle_UI = function() + if G.OVERLAY_MENU then + local swap_node = G.OVERLAY_MENU:get_UIE_by_ID('resolution_cycle') + if swap_node then + local focused_display, focused_screenmode = G.SETTINGS.QUEUED_CHANGE.selected_display or G.SETTINGS.WINDOW.selected_display, G.SETTINGS.QUEUED_CHANGE.screenmode or G.SETTINGS.WINDOW.screenmode + + --Refresh the display information + local res_option = GET_DISPLAYINFO(focused_screenmode, focused_display) + + --Remove the old cycle, replace it with a new updated one reflecting any changes + swap_node.children[1]:remove() + swap_node.children[1] = nil + swap_node.UIBox:add_child( + create_option_cycle({w = 4,scale = 0.8, options = G.SETTINGS.WINDOW.DISPLAYS[focused_display].screen_resolutions.strings, opt_callback = 'change_screen_resolution',current_option = res_option or 1}), + swap_node) + end + end +end + +--Changes the speed that the game runs at, does not affect all timers, just G.TIMERS.TOTAL +-- +---@param args {cycle_config: table, to_val: value} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_val** The new screenmode setting key +G.FUNCS.change_gamespeed = function(args) + G.SETTINGS.GAMESPEED = args.to_val +end + +--Changes the relative position of play and discard buttons +-- +---@param args {cycle_config: table, to_key: value} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_val** The new screenmode setting key +G.FUNCS.change_play_discard_position = function(args) + G.SETTINGS.play_button_pos = args.to_key + if G.buttons then + G.buttons:remove() + G.buttons = UIBox{ + definition = create_UIBox_buttons(), + config = {align="bm", offset = {x=0,y=0.3},major = G.hand, bond = 'Weak'} + } + end +end + +--Changes the Shadow setting +-- +---@param args {cycle_config: table, to_val: value} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_val** The new value for shadows, 'On' or 'Off' +G.FUNCS.change_shadows = function(args) + G.SETTINGS.GRAPHICS.shadows = args.to_key == 1 and 'On' or 'Off' + G:save_settings() +end + +--Changes the Pixel smoothing, all sprites need to be realoaded when this changes\ +-- +---@param args {cycle_config: table, to_key: key} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_val** The new value for shadows, 'On' or 'Off' +G.FUNCS.change_pixel_smoothing = function(args) + G.SETTINGS.GRAPHICS.texture_scaling = args.to_key--^2 + SMODS.injectObjects(SMODS.Atlas) + G:save_settings() +end + +--Changes the Bloom amount for the CRT effect, number of samples to take for bloom\ +-- +---@param args {cycle_config: table, to_key: key} +--**cycle_config** Is the config table from the original option cycle UIE\ +--**to_val** The new value for shadows, 'On' or 'Off' +G.FUNCS.change_crt_bloom = function(args) + G.SETTINGS.GRAPHICS.bloom = args.to_key + G:save_settings() +end + +G.FUNCS.change_collab = function(args) + G.SETTINGS.CUSTOM_DECK.Collabs[args.cycle_config.curr_suit] = G.COLLABS.options[args.cycle_config.curr_suit][args.to_key] or 'default' + for k, v in pairs(G.I.CARD) do + if v.config and v.config.card and v.children.front and v.ability.effect ~= 'Stone Card' then + v:set_sprites(nil, v.config.card) + end + end + G:save_settings() +end + +--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +-- TEXT ENTRY +--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +--passes a keyboard input to the controller when a key UI button is pressed +-- +---@param e table +-- +--[e is the UI Element that calls this update function, contains ARGS in e.config.ref_table] +G.FUNCS.key_button = function(e) + local args = e.config.ref_table + if args.key then G.CONTROLLER:key_press_update(args.key) end +end + +--Modifies the text input to show the current text value being modified. Shows the prompt text if\ +--the text input area is not hooked. Also modifies the UIE colour to show the hooked/non hooked colour\ +--If using a keyboard, pops it up here or removes it if using KBM +-- +---@param e table +-- +--[e is the UI Element that calls this update function, contains ARGS in e.config.ref_table] +G.FUNCS.text_input = function(e) + local args =e.config.ref_table + if G.CONTROLLER.text_input_hook == e then + e.parent.parent.config.colour = args.hooked_colour + args.current_prompt_text = '' + args.current_position_text = args.position_text + else + e.parent.parent.config.colour = args.colour + args.current_prompt_text = (args.text.ref_table[args.text.ref_value] == '' and args.prompt_text or '') + args.current_position_text = '' + end + + local OSkeyboard_e = e.parent.parent.parent + if G.CONTROLLER.text_input_hook == e and G.CONTROLLER.HID.controller then + if not OSkeyboard_e.children.controller_keyboard then + OSkeyboard_e.children.controller_keyboard = UIBox{ + definition = create_keyboard_input{backspace_key = true, return_key = true, space_key = false}, + config = { + align= 'cm', + offset = {x = 0, y = G.CONTROLLER.text_input_hook.config.ref_table.keyboard_offset or -4}, + major = e.UIBox, parent = OSkeyboard_e} + } + G.CONTROLLER.screen_keyboard = OSkeyboard_e.children.controller_keyboard + G.CONTROLLER:mod_cursor_context_layer(1) + end + elseif OSkeyboard_e.children.controller_keyboard then + OSkeyboard_e.children.controller_keyboard:remove() + OSkeyboard_e.children.controller_keyboard = nil + G.CONTROLLER.screen_keyboard = nil + G.CONTROLLER:mod_cursor_context_layer(-1) + end +end + +G.FUNCS.paste_seed = function(e) + G.CONTROLLER.text_input_hook = e.UIBox:get_UIE_by_ID('text_input').children[1].children[1] + G.CONTROLLER.text_input_id = 'text_input' + for i = 1, 8 do + G.FUNCS.text_input_key({key = 'right'}) + end + for i = 1, 8 do + G.FUNCS.text_input_key({key = 'backspace'}) + end + local clipboard = (G.F_LOCAL_CLIPBOARD and G.CLIPBOARD or love.system.getClipboardText()) or '' + for i = 1, #clipboard do + local c = clipboard:sub(i,i) + G.FUNCS.text_input_key({key = c}) + end + G.FUNCS.text_input_key({key = 'return'}) +end + +--When clicked, hooks the text input defined by e->1->1, which should be the text input UIE +-- +---@param e table +-- +--[e is the UI Element that calls this click function] +G.FUNCS.select_text_input = function(e) + G.CONTROLLER.text_input_hook = e.children[1].children[1] + G.CONTROLLER.text_input_id = e.config.id + + --Start by setting the cursor position to the correct location + TRANSPOSE_TEXT_INPUT(0) + e.UIBox:recalculate(true) +end + +--Handles all key inputs for the hooked text input. +-- +---@param args {key: string, caps: boolean} +--**key** the key being pressed\ +--**caps** if the key should be capitalized +G.FUNCS.text_input_key = function(args) + args = args or {} + + if args.key == '[' or args.key == ']' then return end + if args.key == '0' then args.key = 'o' end + + --shortcut to hook config + local hook_config = G.CONTROLLER.text_input_hook.config.ref_table + hook_config.orig_colour = hook_config.orig_colour or copy_table(hook_config.colour) + + args.key = args.key or '%' + args.caps = args.caps or G.CONTROLLER.capslock or hook_config.all_caps --capitalize if caps lock or hook requires + + --Some special keys need to be mapped accordingly before passing through the corpus + local keymap = { + space = ' ', + backspace = 'BACKSPACE', + delete = 'DELETE', + ['return'] = 'RETURN', + right = 'RIGHT', + left = 'LEFT' + } + local hook = G.CONTROLLER.text_input_hook + local corpus = '123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'..(hook.config.ref_table.extended_corpus and " 0!$&()<>?:{}+-=,.[]_" or '') + + if hook.config.ref_table.extended_corpus then + local lower_ext = '1234567890-=;\',./' + local upper_ext = '!@#$%^&*()_+:"<>?' + if string.find(lower_ext, args.key) and args.caps then + args.key = string.sub(string.sub(upper_ext,string.find(lower_ext, args.key)), 0, 1) + end + end + local text = hook_config.text + + --set key to mapped key or upper if caps is true + args.key = keymap[args.key] or (args.caps and string.upper(args.key) or args.key) + + --Start by setting the cursor position to the correct location + TRANSPOSE_TEXT_INPUT(0) + + if string.len(text.ref_table[text.ref_value]) > 0 and args.key == 'BACKSPACE' then --If not at start, remove preceding letter + MODIFY_TEXT_INPUT{ + letter = '', + text_table = text, + pos = text.current_position, + delete = true + } + TRANSPOSE_TEXT_INPUT(-1) + elseif string.len(text.ref_table[text.ref_value]) > 0 and args.key == 'DELETE' then --if not at end, remove following letter + MODIFY_TEXT_INPUT{ + letter = '', + text_table = text, + pos = text.current_position+1, + delete = true + } + TRANSPOSE_TEXT_INPUT(0) + elseif args.key == 'RETURN' then --Release the hook + if hook.config.ref_table.callback then hook.config.ref_table.callback() end + hook.parent.parent.config.colour = hook_config.colour + local temp_colour = copy_table(hook_config.orig_colour) + hook_config.colour[1] = G.C.WHITE[1] + hook_config.colour[2] = G.C.WHITE[2] + hook_config.colour[3] = G.C.WHITE[3] + ease_colour(hook_config.colour, temp_colour) + G.CONTROLLER.text_input_hook = nil + elseif args.key == 'LEFT' then --Move cursor position to the left + TRANSPOSE_TEXT_INPUT(-1) + elseif args.key == 'RIGHT' then --Move cursor position to the right + TRANSPOSE_TEXT_INPUT(1) + elseif hook_config.max_length > string.len(text.ref_table[text.ref_value]) and + (string.len(args.key) == 1) and + string.find( corpus, args.key , 1, true) then --check to make sure the key is in the valid corpus, add it to the string + MODIFY_TEXT_INPUT{ + letter = args.key, + text_table = text, + pos = text.current_position+1 + } + TRANSPOSE_TEXT_INPUT(1) + end +end + +--Helper function for G.FUNCS.text_input_key +function GET_TEXT_FROM_INPUT() + local new_text = '' + local hook = G.CONTROLLER.text_input_hook + for i = 1, #hook.children do + if hook.children[i].config and hook.children[i].config.id:sub(1, 8+string.len(G.CONTROLLER.text_input_id)) == G.CONTROLLER.text_input_id..'_letter_' and hook.children[i].config.text ~= '' then + new_text = new_text..hook.children[i].config.text + end + end + return new_text +end + +--Helper function for G.FUNCS.text_input_key +-- +---@param args {letter: string, text_table: table, pos: number, delete: boolean} +--**letter** the letter being pressed\ +--**text_table** the table full of letters from hook\ +--**pos** the current position of the iterator\ +--**delete** if the action is a deletion action +function MODIFY_TEXT_INPUT(args) + args = args or {} + + if args.delete and args.pos > 0 then + if args.pos >= #args.text_table.letters then + args.text_table.letters[args.pos] = '' + else + args.text_table.letters[args.pos] = args.text_table.letters[args.pos+1] + MODIFY_TEXT_INPUT{ + letter = args.letter, + text_table = args.text_table, + pos = args.pos+1, + delete = args.delete + } + end + return + end + local swapped_letter = args.text_table.letters[args.pos] + args.text_table.letters[args.pos] = args.letter + if swapped_letter and swapped_letter ~= '' then + MODIFY_TEXT_INPUT{ + letter = swapped_letter, + text_table = args.text_table, + pos = args.pos+1 + } + end +end + +--Helper function for G.FUNCS.text_input_key\ +--Moves the cursor left or right. Typing a key, deleting or backspacing also counts\ +--as a cursor move, since empty strings are used to fill the hook +-- +---@param amount number +function TRANSPOSE_TEXT_INPUT(amount) + local position_child = nil + local hook = G.CONTROLLER.text_input_hook + local text = G.CONTROLLER.text_input_hook.config.ref_table.text + for i = 1, #hook.children do + if hook.children[i].config then + if hook.children[i].config.id == G.CONTROLLER.text_input_id..'_position' then + position_child = i; break + end + end + end + + local dir = (amount/math.abs(amount)) or 0 + + while amount ~= 0 do + if position_child + dir < 1 or position_child + dir >= #hook.children then break end + local real_letter = hook.children[position_child+dir].config.id:sub(1, 8+string.len(G.CONTROLLER.text_input_id)) == G.CONTROLLER.text_input_id..'_letter_' and hook.children[position_child+dir].config.text ~= '' + SWAP(hook.children, position_child, position_child + dir) + if real_letter then amount = amount - dir end + position_child = position_child + dir + end + + text.current_position = math.min(position_child-1, string.len(text.ref_table[text.ref_value])) + hook.UIBox:recalculate(true) + text.ref_table[text.ref_value] = GET_TEXT_FROM_INPUT() +end + +--Determines if there are any graphical changes in the queue that require window re-initialization, +--changes the button accordingly +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.can_apply_window_changes = function(e) + local can_apply = false + if G.SETTINGS.QUEUED_CHANGE then + if G.SETTINGS.QUEUED_CHANGE.screenmode and + G.SETTINGS.QUEUED_CHANGE.screenmode ~= G.SETTINGS.WINDOW.screenmode then + can_apply = true + elseif G.SETTINGS.QUEUED_CHANGE.screenres then + can_apply = true + elseif G.SETTINGS.QUEUED_CHANGE.vsync then + can_apply = true + elseif G.SETTINGS.QUEUED_CHANGE.selected_display and + G.SETTINGS.QUEUED_CHANGE.selected_display ~= G.SETTINGS.WINDOW.selected_display then + can_apply = true + end + end + + if can_apply then + e.config.button = 'apply_window_changes' + e.config.colour = G.C.RED + else + e.config.button = nil + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + end +end + +--Applies all window changes, including updates to the screenmode, selected display, resolution and vsync.\ +--These changes are all defined in the G.SETTINGS.QUEUED_CHANGE table. Any unchanged settings use the previous value +G.FUNCS.apply_window_changes = function(_initial) + --Set the screenmode setting from Windowed, Fullscreen or Borderless + G.SETTINGS.WINDOW.screenmode = (G.SETTINGS.QUEUED_CHANGE and G.SETTINGS.QUEUED_CHANGE.screenmode) or G.SETTINGS.WINDOW.screenmode or 'Windowed' + + --Set the monitor the window should be rendered to + G.SETTINGS.WINDOW.selected_display = (G.SETTINGS.QUEUED_CHANGE and G.SETTINGS.QUEUED_CHANGE.selected_display) or G.SETTINGS.WINDOW.selected_display or 1 + + --Set the screen resolution + G.SETTINGS.WINDOW.DISPLAYS[G.SETTINGS.WINDOW.selected_display].screen_res = { + w = (G.SETTINGS.QUEUED_CHANGE and G.SETTINGS.QUEUED_CHANGE.screenres and G.SETTINGS.QUEUED_CHANGE.screenres.w) or (G.SETTINGS.screen_res and G.SETTINGS.screen_res.w) or love.graphics.getWidth( ), + h = (G.SETTINGS.QUEUED_CHANGE and G.SETTINGS.QUEUED_CHANGE.screenres and G.SETTINGS.QUEUED_CHANGE.screenres.h) or (G.SETTINGS.screen_res and G.SETTINGS.screen_res.h) or love.graphics.getHeight( ) + } + + --Set the vsync value, 0 is off 1 is on + G.SETTINGS.WINDOW.vsync = (G.SETTINGS.QUEUED_CHANGE and G.SETTINGS.QUEUED_CHANGE.vsync) or G.SETTINGS.WINDOW.vsync or 1 + + love.window.updateMode( + (G.SETTINGS.QUEUED_CHANGE and G.SETTINGS.QUEUED_CHANGE.screenmode == 'Windowed') and love.graphics.getWidth()*0.8 or G.SETTINGS.WINDOW.DISPLAYS[G.SETTINGS.WINDOW.selected_display].screen_res.w, + (G.SETTINGS.QUEUED_CHANGE and G.SETTINGS.QUEUED_CHANGE.screenmode == 'Windowed') and love.graphics.getHeight()*0.8 or G.SETTINGS.WINDOW.DISPLAYS[G.SETTINGS.WINDOW.selected_display].screen_res.h, + {fullscreen = G.SETTINGS.WINDOW.screenmode ~= 'Windowed', + fullscreentype = (G.SETTINGS.WINDOW.screenmode == 'Borderless' and 'desktop') or (G.SETTINGS.WINDOW.screenmode == 'Fullscreen' and 'exclusive') or nil, + vsync = G.SETTINGS.WINDOW.vsync, + resizable = true, + display = G.SETTINGS.WINDOW.selected_display, + highdpi = (love.system.getOS() == 'OS X') + }) + G.SETTINGS.QUEUED_CHANGE = {} + if _initial ~= true then + love.resize(love.graphics.getWidth(),love.graphics.getHeight()) + G:save_settings() + end + if G.OVERLAY_MENU then + local tab_but = G.OVERLAY_MENU:get_UIE_by_ID('tab_but_Video') + G.FUNCS.change_tab(tab_but) + end +end + +--Iterates through any collection cardareas, applies the initial collection alert update.\ +--This update_alert function for each card is also called by the card in its own update function +function INIT_COLLECTION_CARD_ALERTS() + for j = 1, #G.your_collection do + for _, v in ipairs(G.your_collection[j].cards) do + v:update_alert() + end + end +end + +--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +-- RUN SETUP +--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +--Monitors the run option deck to determine if a new 'back' has been chosen from the option cycle\ +--if so, this function removes the old UI describing the ability of the back and replaces it with the new ability UI +-- +---@param e {} +--**e** Is the UIE that called this function +G.FUNCS.RUN_SETUP_check_back = function(e) + if G.GAME.viewed_back.name ~= e.config.id then + --removes the UI from the previously selected back and adds the new one + + e.config.object:remove() + e.config.object = UIBox{ + definition = G.GAME.viewed_back:generate_UI(), + config = {offset = {x=0,y=0}, align = 'cm', parent = e} + } + e.config.id = G.GAME.viewed_back.name + end +end + +G.FUNCS.RUN_SETUP_check_back_name = function(e) + if e.config.object and G.GAME.viewed_back.name ~= e.config.id then + --removes the UI from the previously selected back and adds the new one + + e.config.object:remove() + e.config.object = UIBox{ + definition = {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={ + {n=G.UIT.O, config={id = G.GAME.viewed_back.name, func = 'RUN_SETUP_check_back_name', object = DynaText({string = G.GAME.viewed_back:get_name(),maxw = 4, colours = {G.C.WHITE}, shadow = true, bump = true, scale = 0.5, pop_in = 0, silent = true})}}, + }}, + config = {offset = {x=0,y=0}, align = 'cm', parent = e} + } + e.config.id = G.GAME.viewed_back.name + end +end + +G.FUNCS.RUN_SETUP_check_stake = function(e) + if (G.GAME.viewed_back.name ~= e.config.id) then + e.config.object:remove() + e.config.object = UIBox{ + definition = G.UIDEF.stake_option(G.SETTINGS.current_setup), + config = {offset = {x=0,y=0}, align = 'tmi', parent = e} + } + e.config.id = G.GAME.viewed_back.name + end +end + +G.FUNCS.RUN_SETUP_check_stake2 = function(e) + if (G.viewed_stake ~= e.config.id) then + e.config.object:remove() + e.config.object = UIBox{ + definition = G.UIDEF.viewed_stake_option(), + config = {offset = {x=0,y=0}, align = 'cm', parent = e} + } + e.config.id = G.viewed_stake + end +end + +G.FUNCS.RUN_SETUP_check_back_stake_column= function(e) + if G.GAME.viewed_back.name ~= e.config.id then + --removes the UI from the previously selected back and adds the new one + e.config.object:remove() + e.config.object = UIBox{ + definition = G.UIDEF.deck_stake_column(G.GAME.viewed_back.effect.center.key), + config = {offset = {x=0,y=0}, align = 'cm', parent = e} + } + e.config.id = G.GAME.viewed_back.name + end +end + +G.FUNCS.RUN_SETUP_check_back_stake_highlight= function(e) + if G.viewed_stake == e.config.id and e.config.outline < 0.1 then + e.config.outline = 0.8 + elseif G.viewed_stake ~= e.config.id and e.config.outline > 0.1 then + e.config.outline = 0 + end +end + +G.FUNCS.change_tab = function(e) + if not e then return end + local _infotip_object = G.OVERLAY_MENU:get_UIE_by_ID('overlay_menu_infotip') + if _infotip_object and _infotip_object.config.object then + _infotip_object.config.object:remove() + _infotip_object.config.object = Moveable() + end + + local tab_contents = e.UIBox:get_UIE_by_ID('tab_contents') + tab_contents.config.object:remove() + tab_contents.config.object = UIBox{ + definition = e.config.ref_table.tab_definition_function(e.config.ref_table.tab_definition_function_args), + config = {offset = {x=0,y=0}, parent = tab_contents, type = 'cm'} + } + tab_contents.UIBox:recalculate() +end + +--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| +-- OVERLAY MENUS +--|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||| + +--The overlay menu is a full screen menu interface, and is usually called from button presses. The base Overlay menu function here\ +--creates a global G.OVERLAY_MENU that represents this full screen UIBox. The game may be paused at this time as well\ +--The generated UIBox is created below the visible screen area and eased up to the center of the screen +-- +---@param args {definition: table, config: table, pause: boolean} +--**definition** The definition table for the UIBox\ +--**config** A configuration table for the UIBox\ +--**pause** Whether the game should be paused on creation of the UIBox +G.FUNCS.overlay_menu = function(args) + if not args then return end + --Remove any existing overlays if there is one + if G.OVERLAY_MENU then G.OVERLAY_MENU:remove() end + G.CONTROLLER.locks.frame_set = true + G.CONTROLLER.locks.frame = true + G.CONTROLLER.cursor_down.target = nil + G.CONTROLLER:mod_cursor_context_layer(G.NO_MOD_CURSOR_STACK and 0 or 1) + + args.config = args.config or {} + args.config = { + align = args.config.align or "cm", + offset = args.config.offset or {x=0,y=10}, + major = args.config.major or G.ROOM_ATTACH, + bond = 'Weak', + no_esc = args.config.no_esc + } + G.OVERLAY_MENU = true + --Generate the UIBox + G.OVERLAY_MENU = UIBox{ + definition = args.definition, + config = args.config + } + + --Set the offset and align. The menu overlay can be initially offset in the y direction and this will ensure it slides to middle + G.OVERLAY_MENU.alignment.offset.y = 0 + G.ROOM.jiggle = G.ROOM.jiggle + 1 + G.OVERLAY_MENU:align_to_major() +end + +--Removes the overlay menu if one exists, unpauses the game, and saves the settings to file +G.FUNCS.exit_overlay_menu = function() + if not G.OVERLAY_MENU then return end + G.CONTROLLER.locks.frame_set = true + G.CONTROLLER.locks.frame = true + G.CONTROLLER:mod_cursor_context_layer(-1000) + G.OVERLAY_MENU:remove() + G.OVERLAY_MENU = nil + G.VIEWING_DECK = nil + G.SETTINGS.paused = false + + --Save settings to file + G:save_settings() +end + +--Removes overlay menu and immediately generates the next unlock menu if there is one +G.FUNCS.continue_unlock = function() + G.FUNCS.exit_overlay_menu() + G.CONTROLLER:mod_cursor_context_layer(-2000) + G.E_MANAGER:update(0, true) +end + +G.FUNCS.test_framework = function(options) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_test_framework(options), + } +end + +G.FUNCS.options = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_options(), + } +end + +G.FUNCS.current_hands = function(e, simple) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_current_hands(simple), + } +end + +G.FUNCS.run_info = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = G.UIDEF.run_info(), + } +end + +G.FUNCS.deck_info = function(e) + G.SETTINGS.paused = true + if G.deck_preview then + G.deck_preview:remove() + G.deck_preview = nil + end + G.FUNCS.overlay_menu{ + definition = G.UIDEF.deck_info( + G.STATE == G.STATES.SELECTING_HAND or G.STATE == G.STATES.HAND_PLAYED or G.STATE == G.STATES.DRAW_TO_HAND + ), + } +end + +G.FUNCS.settings = function(e, instant) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_settings(), + config = {offset = {x=0,y=instant and 0 or 10}} +} +end + +G.FUNCS.show_credits = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = G.UIDEF.credits(), + } +end + +G.FUNCS.language_selection = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = G.UIDEF.language_selector(), + } +end + +G.FUNCS.your_collection = function(e) +if update_cry_member_count then update_cry_member_count() end + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection(), + } +end + +G.FUNCS.your_collection_blinds = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_blinds(), + } +end + +G.FUNCS.your_collection_jokers = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_jokers(), + } +end + +G.FUNCS.your_collection_tarots = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_tarots(), + } +end + +G.FUNCS.your_collection_planets = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_planets(), + } +end + +G.FUNCS.your_collection_spectrals = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_spectrals(), + } +end + +G.FUNCS.your_collection_vouchers = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_vouchers(), + } +end + +G.FUNCS.your_collection_enhancements_exit_overlay_menu = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_enhancements('exit_overlay_menu'), + } +end + +G.FUNCS.your_collection_enhancements = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_enhancements(), + } +end + +G.FUNCS.your_collection_decks = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_decks(), + } +end + +G.FUNCS.your_collection_editions = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_editions(), + } +end + +G.FUNCS.your_collection_tags = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_tags(), + } +end + +G.FUNCS.your_collection_seals = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_seals(), + } +end + +G.FUNCS.your_collection_boosters = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_your_collection_boosters(), + } +end + +G.FUNCS.challenge_list = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = G.UIDEF.challenge_list((e.config.id == 'from_game_over')), + } + if (e.config.id == 'from_game_over') then G.OVERLAY_MENU.config.no_esc =true end +end + +G.FUNCS.high_scores = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_high_scores(), + } +end + +G.FUNCS.customize_deck = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_customize_deck(), + } +end + +G.FUNCS.usage = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = G.UIDEF.usage_tabs() + } +end + +G.FUNCS.setup_run = function(e) + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = G.UIDEF.run_setup((e.config.id == 'from_game_over' or e.config.id == 'from_game_won' or e.config.id == 'challenge_list') and e.config.id), + } + if (e.config.id == 'from_game_over' or e.config.id == 'from_game_won') then G.OVERLAY_MENU.config.no_esc =true end +end + +G.FUNCS.wait_for_high_scores = function(e) + if G.ARGS.HIGH_SCORE_RESPONSE then + e.config.object:remove() + e.config.object = UIBox{ + definition = create_UIBox_high_scores_filling(G.ARGS.HIGH_SCORE_RESPONSE), + config = {offset = {x=0,y=0}, align = 'cm', parent = e} + } + G.ARGS.HIGH_SCORE_RESPONSE = nil + end +end + +G.FUNCS.notify_then_setup_run = function(e) + G.OVERLAY_MENU:remove() + G.OVERLAY_MENU = nil + + G.E_MANAGER:add_event(Event({ + blockable = false, + func = (function() + unlock_notify() + return true + end) + })) + + G.E_MANAGER:add_event(Event({ + blockable = false, + func = (function() + if #G.E_MANAGER.queues.unlock <= 0 and not G.OVERLAY_MENU then + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = G.UIDEF.run_setup((e.config.id == 'from_game_over' or e.config.id == 'from_game_won') and e.config.id), + } + if (e.config.id == 'from_game_over' or e.config.id == 'from_game_won') then G.OVERLAY_MENU.config.no_esc =true end + return true + end + end) + })) +end + +G.FUNCS.change_challenge_description = function(e) + if G.OVERLAY_MENU then + local desc_area = G.OVERLAY_MENU:get_UIE_by_ID('challenge_area') + if desc_area and desc_area.config.oid ~= e.config.id then + if desc_area.config.old_chosen then desc_area.config.old_chosen.config.chosen = nil end + e.config.chosen = 'vert' + if desc_area.config.object then + desc_area.config.object:remove() + end + desc_area.config.object = UIBox{ + definition = G.UIDEF.challenge_description(e.config.id), + config = {offset = {x=0,y=0}, align = 'cm', parent = desc_area} + } + desc_area.config.oid = e.config.id + desc_area.config.old_chosen = e + end + end +end + +G.FUNCS.change_challenge_list_page = function(args) + if not args or not args.cycle_config then return end + if G.OVERLAY_MENU then + local ch_list = G.OVERLAY_MENU:get_UIE_by_ID('challenge_list') + if ch_list then + if ch_list.config.object then + ch_list.config.object:remove() + end + ch_list.config.object = UIBox{ + definition = G.UIDEF.challenge_list_page(args.cycle_config.current_option-1), + config = {offset = {x=0,y=0}, align = 'cm', parent = ch_list} + } + G.FUNCS.change_challenge_description{config = {id = 'nil'}} + end + end +end + +G.FUNCS.deck_view_challenge = function(e) + G.FUNCS.overlay_menu{ + definition = create_UIBox_generic_options({back_func = 'deck_info', contents ={ + G.UIDEF.challenge_description(get_challenge_int_from_id(e.config.id.id or ''), nil, true) + } + }) + } +end + +G.FUNCS.profile_select = function(e) + G.SETTINGS.paused = true + G.focused_profile = G.SETTINGS.profile + + for i = 1, 3 do + if i ~= G.focused_profile and love.filesystem.getInfo(i..'/'..'profile.jkr') then G:load_profile(i) end + end + G:load_profile(G.focused_profile) + + G.FUNCS.overlay_menu{ + definition = G.UIDEF.profile_select(), + } +end + +G.FUNCS.quit = function(e) + love.event.quit() +end + +G.FUNCS.quit_cta = function(e) + G.SETTINGS.paused = true + G.SETTINGS.DEMO.quit_CTA_shown = true + + G:save_progress() + + G.FUNCS.overlay_menu{ + definition = create_UIBox_exit_CTA(), + config = {no_esc = true} + } + local Jimbo = nil + + if not G.jimboed then + G.jimboed = true + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = 2.5, + func = (function() + if G.OVERLAY_MENU and G.OVERLAY_MENU:get_UIE_by_ID('jimbo_spot') then + Jimbo = Card_Character({x = 0, y = 5}) + local spot = G.OVERLAY_MENU:get_UIE_by_ID('jimbo_spot') + spot.config.object:remove() + spot.config.object = Jimbo + Jimbo.ui_object_updated = true + Jimbo:add_speech_bubble( {"Having fun?", {{text = "Wishlist Balatro!", type = 'GREEN'}}}) + Jimbo:say_stuff(5) + end + return true + end) + })) + end +end + +G.FUNCS.demo_survey = function(e) + love.system.openURL( "https://forms.gle/WX26BHq1AwwV5xyH9")--"https://forms.gle/P8F4WzdCsccrm15T6" ) +end + +G.FUNCS.louisf_insta = function(e) + love.system.openURL( "https://www.instagram.com/louisfsoundtracks/" ) +end + +G.FUNCS.wishlist_steam = function(e) + love.system.openURL( "https://store.steampowered.com/app/2379780/Balatro/#game_area_purchase" ) +end + +G.FUNCS.go_to_playbalatro = function(e) + love.system.openURL( "https://www.playbalatro.com" ) +end + +G.FUNCS.go_to_discord = function(e) + love.system.openURL( "https://discord.gg/balatro" ) +end + +G.FUNCS.go_to_discord_loc = function(e) + love.system.openURL( "https://discord.com/channels/1116389027176787968/1207803392978853898" ) +end + +G.FUNCS.loc_survey = function(e) + love.system.openURL( "https://forms.gle/pL5tMh1oXLmv8czz9" ) +end + +G.FUNCS.go_to_twitter = function(e) + love.system.openURL( "https://twitter.com/LocalThunk" ) +end + +G.FUNCS.unlock_this = function(e) + unlock_achievement(e.config.id) +end + +G.FUNCS.reset_achievements = function(e) + G.ACHIEVEMENTS = nil + G.SETTINGS.ACHIEVEMENTS_EARNED = {} + G:save_progress() + G.FUNCS.exit_overlay_menu() +end + +G.FUNCS.refresh_contrast_mode = function() + local new_colour_proto = G.C["SO_"..(G.SETTINGS.colourblind_option and 2 or 1)] + G.C.SUITS.Hearts = new_colour_proto.Hearts + G.C.SUITS.Diamonds = new_colour_proto.Diamonds + G.C.SUITS.Spades = new_colour_proto.Spades + G.C.SUITS.Clubs = new_colour_proto.Clubs + for k, v in pairs(G.I.CARD) do + if v.config and v.config.card and v.children.front and v.ability.effect ~= 'Stone Card' then + v:set_sprites(nil, v.config.card) + end + end +end + +G.FUNCS.warn_lang = function(e) + local _infotip_object = G.OVERLAY_MENU:get_UIE_by_ID('overlay_menu_infotip') + if (_infotip_object.config.set ~= e.config.ref_table.label) then + _infotip_object.config.object:remove() + _infotip_object.config.object = UIBox{ + definition = overlay_infotip({e.config.ref_table.warning[1],e.config.ref_table.warning[2],e.config.ref_table.warning[3], lang = e.config.ref_table}), + config = {offset = {x=0,y=0}, align = 'bm', parent = _infotip_object} + } + _infotip_object.config.object.UIRoot:juice_up() + _infotip_object.config.set = e.config.ref_table.label + e.config.disable_button = true + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.35, blockable = false, blocking = false, func = function() + e.config.disable_button = nil;return true end})) + e.config.button = 'change_lang' + play_sound('tarot2', 1, 0.4) + end +end + + + +G.FUNCS.change_lang = function(e) + local lang = e.config.ref_table + if not lang or lang == G.LANG then + G.FUNCS.exit_overlay_menu() + else + G.SETTINGS.language = lang.key + G:set_language() + G.E_MANAGER:clear_queue() + G.FUNCS.wipe_on() + G.E_MANAGER:add_event(Event({ + no_delete = true, + blockable = true, + blocking = false, + func = function() + G:delete_run() + G:init_item_prototypes() + G:main_menu() + return true + end + })) + G.FUNCS.wipe_off() + end +end + +G.FUNCS.copy_seed = function(e) + if G.F_LOCAL_CLIPBOARD then + G.CLIPBOARD = G.GAME.pseudorandom.seed + else + love.system.setClipboardText(G.GAME.pseudorandom.seed) + end +end + +G.FUNCS.start_setup_run = function(e) + if G.OVERLAY_MENU then G.FUNCS.exit_overlay_menu() end + if G.SETTINGS.current_setup == 'New Run' then + if not G.GAME or (not G.GAME.won and not G.GAME.seeded) then + if G.SAVED_GAME ~= nil then + if not G.SAVED_GAME.GAME.won then + G.PROFILES[G.SETTINGS.profile].high_scores.current_streak.amt = 0 + end + G:save_settings() + end + end + local _seed = G.run_setup_seed and G.setup_seed or G.forced_seed or nil + local _challenge = G.challenge_tab or nil + local _stake = G.forced_stake or G.PROFILES[G.SETTINGS.profile].MEMORY.stake or 1 + G.FUNCS.start_run(e, {stake = _stake, seed = _seed, challenge = _challenge}) + + elseif G.SETTINGS.current_setup == 'Continue' then + if G.SAVED_GAME ~= nil then + G.FUNCS.start_run(nil, {savetext = G.SAVED_GAME}) + end + end +end + +G.FUNCS.start_challenge_run = function(e) + if G.OVERLAY_MENU then G.FUNCS.exit_overlay_menu() end + G.FUNCS.start_run(e, {stake = 1, challenge = G.CHALLENGES[e.config.id]}) +end + +function G.FUNCS.toggle_seeded_run(e) + if e.config.object and not G.run_setup_seed then + e.config.object:remove() + e.config.object = nil + elseif not e.config.object and G.run_setup_seed then + e.config.object = UIBox{ + definition = {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={ + {n=G.UIT.C, config={align = "cm", minw = 2.5, padding = 0.05}, nodes={ + simple_text_container('ml_disabled_seed',{colour = G.C.UI.TEXT_LIGHT, scale = 0.26, shadow = true}), + }}, + {n=G.UIT.C, config={align = "cm", minw = 0.1}, nodes={ + create_text_input({max_length = 8, all_caps = true, ref_table = G, ref_value = 'setup_seed', prompt_text = localize('k_enter_seed')}), + {n=G.UIT.C, config={align = "cm", minw = 0.1}, nodes={}}, + UIBox_button({label = localize('ml_paste_seed'),minw = 1, minh = 0.6, button = 'paste_seed', colour = G.C.BLUE, scale = 0.3, col = true}) + }}, + + {n=G.UIT.C, config={align = "cm", minw = 2.5}, nodes={ + }}, + }}, + config = {offset = {x=0,y=0}, parent = e, type = 'cm'} + } + e.config.object:recalculate() + end +end + +G.FUNCS.start_tutorial = function(e) + if G.OVERLAY_MENU then G.FUNCS.exit_overlay_menu() end + G.SETTINGS.tutorial_progress = + { + forced_shop = {'j_joker', 'c_empress'}, + forced_voucher = 'v_grabber', + forced_tags = {'tag_handy', 'tag_garbage'}, + hold_parts = {}, + completed_parts = {}, + } + G.SETTINGS.tutorial_complete = false + G.FUNCS.start_run(e) +end + +G.FUNCS.chip_UI_set = function(e) + local new_chips_text = number_format(G.GAME.chips) + if G.GAME.chips_text ~= new_chips_text then + e.config.scale = math.min(0.8, scale_number(G.GAME.chips, 1.1)) + G.GAME.chips_text = new_chips_text + end +end + +G.FUNCS.blind_chip_UI_scale = function(e) + if G.GAME.blind and G.GAME.blind.chips then + e.config.scale = scale_number(G.GAME.blind.chips, 0.7, 100000) + end +end + +function scale_number(number, scale, max, e_switch_point) + G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 + if not number or type(number) ~= 'number' then return scale end + if not max then max = 10000 end + if math.abs(number) >= (e_switch_point or G.E_SWITCH_POINT) then + scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.log(1000000*10, 10)) + elseif number >= max then + scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.log(number*10, 10)) + end + return scale +end + +G.FUNCS.hand_mult_UI_set = function(e) + local new_mult_text = number_format(G.GAME.current_round.current_hand.mult) + if new_mult_text ~= G.GAME.current_round.current_hand.mult_text then + G.GAME.current_round.current_hand.mult_text = new_mult_text + e.config.object.scale = scale_number(G.GAME.current_round.current_hand.mult, 0.9, 1000) + e.config.object:update_text() + if not G.TAROT_INTERRUPT_PULSE then G.FUNCS.text_super_juice(e, math.min(2,math.max(0,math.floor(math.log10(is_number(G.GAME.current_round.current_hand.mult) and G.GAME.current_round.current_hand.mult or 1))))) end + end +end + +G.FUNCS.hand_chip_UI_set = function(e) + local new_chip_text = number_format(G.GAME.current_round.current_hand.chips) + if new_chip_text ~= G.GAME.current_round.current_hand.chip_text then + G.GAME.current_round.current_hand.chip_text = new_chip_text + e.config.object.scale = scale_number(G.GAME.current_round.current_hand.chips, 0.9, 1000) + e.config.object:update_text() + if not G.TAROT_INTERRUPT_PULSE then G.FUNCS.text_super_juice(e, math.min(2,math.max(0,math.floor(math.log10(is_number(G.GAME.current_round.current_hand.chips) and G.GAME.current_round.current_hand.chips or 1))))) end + end +end + +G.FUNCS.hand_chip_total_UI_set = function(e) + if to_big(G.GAME.current_round.current_hand.chip_total) < to_big(1) then + G.GAME.current_round.current_hand.chip_total_text = '' + else + local new_chip_total_text = number_format(G.GAME.current_round.current_hand.chip_total) + if new_chip_total_text ~= G.GAME.current_round.current_hand.chip_total_text then + e.config.object.scale = scale_number(G.GAME.current_round.current_hand.chip_total, 0.95, 100000000) + + G.GAME.current_round.current_hand.chip_total_text = new_chip_total_text + if not G.ARGS.hand_chip_total_UI_set or to_big(G.ARGS.hand_chip_total_UI_set) < to_big(G.GAME.current_round.current_hand.chip_total) then + G.FUNCS.text_super_juice(e, math.floor(math.log10(G.GAME.current_round.current_hand.chip_total))) + end + G.ARGS.hand_chip_total_UI_set = G.GAME.current_round.current_hand.chip_total + --e.UIBox:recalculate() + end + end +end + +function G.FUNCS.text_super_juice(e, _amount) + e.config.object:set_quiver(0.03*_amount) + e.config.object:pulse(0.3 + 0.08*_amount) + e.config.object:update_text() + e.config.object:align_letters() + e:update_object() +end + +G.FUNCS.flame_handler = function(e) + G.C.UI_CHIPLICK = G.C.UI_CHIPLICK or {1, 1, 1, 1} + G.C.UI_MULTLICK = G.C.UI_MULTLICK or {1, 1, 1, 1} + for i=1, 3 do + G.C.UI_CHIPLICK[i] = math.min(math.max(((G.C.UI_CHIPS[i]*0.5+G.C.YELLOW[i]*0.5) + 0.1)^2, 0.1), 1) + G.C.UI_MULTLICK[i] = math.min(math.max(((G.C.UI_MULT[i]*0.5+G.C.YELLOW[i]*0.5) + 0.1)^2, 0.1), 1) + end + + G.ARGS.flame_handler = G.ARGS.flame_handler or { + chips = { + id = 'flame_chips', + arg_tab = 'chip_flames', + colour = G.C.UI_CHIPS, + accent = G.C.UI_CHIPLICK + }, + mult = { + id = 'flame_mult', + arg_tab = 'mult_flames', + colour = G.C.UI_MULT, + accent = G.C.UI_MULTLICK + } + } + for k, v in pairs(G.ARGS.flame_handler) do + if e.config.id == v.id then + if not e.config.object:is(Sprite) or e.config.object.ID ~= v.ID then + e.config.object:remove() + e.config.object = Sprite(0, 0, 2.5, 2.5, G.ASSET_ATLAS["ui_1"], {x = 2, y = 0}) + v.ID = e.config.object.ID + G.ARGS[v.arg_tab] = { + intensity = 0, + real_intensity = 0, + intensity_vel = 0, + colour_1 = v.colour, + colour_2 = v.accent, + timer = G.TIMERS.REAL + } + e.config.object:set_alignment({ + major = e.parent, + type = 'bmi', + offset = {x=0,y=0}, + xy_bond = 'Weak' + }) + e.config.object:define_draw_steps({{ + shader = 'flame', + send = { + {name = 'time', ref_table = G.ARGS[v.arg_tab], ref_value = 'timer'}, + {name = 'amount', ref_table = G.ARGS[v.arg_tab], ref_value = 'real_intensity'}, + {name = 'image_details', ref_table = e.config.object, ref_value = 'image_dims'}, + {name = 'texture_details', ref_table = e.config.object.RETS, ref_value = 'get_pos_pixel'}, + {name = 'colour_1', ref_table = G.ARGS[v.arg_tab], ref_value = 'colour_1'}, + {name = 'colour_2', ref_table = G.ARGS[v.arg_tab], ref_value = 'colour_2'}, + {name = 'id', val = e.config.object.ID}, + }}}) + e.config.object:get_pos_pixel() + end + local _F = G.ARGS[v.arg_tab] + local exptime = math.exp(-0.4*G.real_dt) + + if to_big(G.ARGS.score_intensity.earned_score) >= to_big(G.ARGS.score_intensity.required_score) and to_big(G.ARGS.score_intensity.required_score) > to_big(0) then + _F.intensity = ((G.pack_cards and not G.pack_cards.REMOVED) or (G.TAROT_INTERRUPT)) and 0 or Cartomancer.get_flames_intensity() + else + _F.intensity = 0 + end + + _F.timer = Cartomancer.handle_flames_timer(_F.timer, _F.intensity) + if _F.intensity_vel < 0 then _F.intensity_vel = _F.intensity_vel*(1 - 10*G.real_dt) end + _F.intensity_vel = (1-exptime)*(_F.intensity - _F.real_intensity)*G.real_dt*25 + exptime*_F.intensity_vel + _F.real_intensity = math.max(0, _F.real_intensity + _F.intensity_vel) + + _F.real_intensity = (G.cry_flame_override and G.cry_flame_override['duration'] > 0) and ((_F.real_intensity + G.cry_flame_override['intensity'])/2) or _F.real_intensity + _F.change = (_F.change or 0)*(1 - 4.*G.real_dt) + ( 4.*G.real_dt)*(_F.real_intensity < _F.intensity - 0.0 and 1 or 0)*_F.real_intensity + _F.change = (G.cry_flame_override and G.cry_flame_override['duration'] > 0) and ((_F.change + G.cry_flame_override['intensity'])/2) or _F.change + end + end +end + +G.FUNCS.hand_text_UI_set = function(e) + if G.GAME.current_round.current_hand.handname ~= G.GAME.current_round.current_hand.handname_text then + G.GAME.current_round.current_hand.handname_text = G.GAME.current_round.current_hand.handname + if G.GAME.current_round.current_hand.handname:len() >= 13 then + e.config.object.scale = 12*0.56/G.GAME.current_round.current_hand.handname:len() + else + e.config.object.scale = 2.4/math.sqrt(G.GAME.current_round.current_hand.handname:len()+5) + end + e.config.object:update_text() + end +end + + G.FUNCS.can_play = function(e) + if #G.hand.highlighted <= (G.GAME.blind and G.GAME.blind.name == 'cry-Sapphire Stamp' and not G.GAME.blind.disabled and 1 or 0) or G.GAME.blind.block_play then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.BLUE + e.config.button = 'play_cards_from_highlighted' + end + end + + G.FUNCS.can_start_run = function(e) + if not G.GAME.viewed_back.effect.center.unlocked then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.BLUE + e.config.button = 'start_setup_run' + end + end + + G.FUNCS.visible_blind = function(e) + if next(G.GAME.blind.config.blind) then + e.states.visible = true + else + e.states.visible = false + end + end + + G.FUNCS.can_reroll = function(e) + if ((G.GAME.dollars-G.GAME.bankrupt_at) - G.GAME.current_round.reroll_cost < 0) and G.GAME.current_round.reroll_cost ~= 0 then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + --e.children[1].children[1].config.shadow = false + --e.children[2].children[1].config.shadow = false + --e.children[2].children[2].config.shadow = false + else + e.config.colour = G.C.GREEN + e.config.button = 'reroll_shop' + --e.children[1].children[1].config.shadow = true + --e.children[2].children[1].config.shadow = true + --e.children[2].children[2].config.shadow = true + end + end + + G.FUNCS.can_discard = function(e) + if G.GAME.current_round.discards_left <= 0 or #G.hand.highlighted <= 0 then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.RED + e.config.button = 'discard_cards_from_highlighted' + end + end + + G.FUNCS.can_use_consumeable = function(e) + if e.config.ref_table:can_use_consumeable() then + e.config.colour = G.C.RED + e.config.button = 'use_card' + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + end + end + + G.FUNCS.can_select_card = function(e) + if e.config.ref_table.ability.set ~= 'Joker' or (e.config.ref_table.edition and e.config.ref_table.edition.negative) or #G.jokers.cards < G.jokers.config.card_limit then + e.config.colour = G.C.GREEN + e.config.button = 'use_card' + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + end + end + + G.FUNCS.can_sell_card = function(e) + if e.config.ref_table:can_sell_card() then + e.config.colour = G.C.GREEN + e.config.button = 'sell_card' + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + end + end + + G.FUNCS.can_skip_booster = function(e) + if G.pack_cards and (G.pack_cards.cards[1]) and +(G.STATE == G.STATES.SMODS_BOOSTER_OPENED or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.STANDARD_PACK or G.STATE == G.STATES.BUFFOON_PACK or (G.hand and (G.hand.cards[1] or (G.hand.config.card_limit <= 0)))) then + e.config.colour = G.C.GREY + e.config.button = 'skip_booster' + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + end + end + + G.FUNCS.show_infotip = function(e) + if e.config.ref_table then + e.children.info = UIBox{ + definition = {n=G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR, padding = 0.02}, nodes=e.config.ref_table}, + config = (not e.config.ref_table or not e.config.ref_table[1].config.card_pos or e.config.ref_table[1].config.card_pos > G.ROOM.T.w*0.4) and + {offset = {x=-0.03,y=0}, align = 'cl', parent = e} or + {offset = {x=0.03,y=0}, align = 'cr', parent = e} + } + e.children.info:align_to_major() + e.config.ref_table = nil + end + end + + + G.FUNCS.use_card = function(e, mute, nosave) + e.config.button = nil + local card = e.config.ref_table + local area = card.area + local prev_state = G.STATE + local dont_dissolve = nil + local delay_fac = 1 + + if card:check_use() then + G.E_MANAGER:add_event(Event({func = function() + e.disable_button = nil + e.config.button = 'use_card' + return true end })) + return + end + + if card.ability.set == 'Booster' and not nosave and G.STATE == G.STATES.SHOP then + save_with_action({ + type = 'use_card', + card = card.sort_id, + }) + end + + G.TAROT_INTERRUPT = G.STATE + if card.ability.set == 'Booster' then G.GAME.PACK_INTERRUPT = G.STATE end + G.STATE = (G.STATE == G.STATES.TAROT_PACK and G.STATES.TAROT_PACK) or + (G.STATE == G.STATES.PLANET_PACK and G.STATES.PLANET_PACK) or + (G.STATE == G.STATES.SPECTRAL_PACK and G.STATES.SPECTRAL_PACK) or + (G.STATE == G.STATES.STANDARD_PACK and G.STATES.STANDARD_PACK) or + (G.STATE == G.STATES.SMODS_BOOSTER_OPENED and G.STATES.SMODS_BOOSTER_OPENED) or + (G.STATE == G.STATES.BUFFOON_PACK and G.STATES.BUFFOON_PACK) or + G.STATES.PLAY_TAROT + + G.CONTROLLER.locks.use = true + if G.booster_pack and not G.booster_pack.alignment.offset.py and (card.ability.consumeable or not (G.GAME.pack_choices and G.GAME.pack_choices > 1)) then + G.booster_pack.alignment.offset.py = G.booster_pack.alignment.offset.y + G.booster_pack.alignment.offset.y = G.ROOM.T.y + 29 + end + if G.shop and not G.shop.alignment.offset.py then + G.shop.alignment.offset.py = G.shop.alignment.offset.y + G.shop.alignment.offset.y = G.ROOM.T.y + 29 + end + if G.blind_select and not G.blind_select.alignment.offset.py then + G.blind_select.alignment.offset.py = G.blind_select.alignment.offset.y + G.blind_select.alignment.offset.y = G.ROOM.T.y + 39 + end + if G.round_eval and not G.round_eval.alignment.offset.py then + G.round_eval.alignment.offset.py = G.round_eval.alignment.offset.y + G.round_eval.alignment.offset.y = G.ROOM.T.y + 29 + end + + if card.children.use_button then card.children.use_button:remove(); card.children.use_button = nil end + if card.children.sell_button then card.children.sell_button:remove(); card.children.sell_button = nil end + if card.children.price then card.children.price:remove(); card.children.price = nil end + + local nc + if card.ability.consumeable then + local obj = card.config.center + if obj.keep_on_use and type(obj.keep_on_use) == 'function' then + nc = obj:keep_on_use(card) + end + end + if not nc and card.area then card.area:remove_card(card) end + + if card.ability.consumeable then + if G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.PLANET_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED then + card.T.x = G.hand.T.x + G.hand.T.w/2 - card.T.w/2 + card.T.y = G.hand.T.y + G.hand.T.h/2 - card.T.h/2 - 0.5 + discover_card(card.config.center) + elseif nc then + area:remove_from_highlighted(card) + play_sound('cardSlide2', nil, 0.3) + dont_dissolve = true + else draw_card(G.hand, G.play, 1, 'up', true, card, nil, mute) end + delay(0.2) + e.config.ref_table:use_consumeable(area) + if card.edition and card.edition.key then + local ed = SMODS.Centers[card.edition.key] + if ed.calculate and type(ed.calculate) == 'function' then + ed:calculate(card, {from_consumable = true}) + end + end + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({using_consumeable = true, consumeable = card}) + end + elseif card.ability.set == 'Enhanced' or card.ability.set == 'Default' then + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + G.deck:emplace(card) + play_sound('card1', 0.8, 0.6) + play_sound('generic1') + card.playing_card = G.playing_card + playing_card_joker_effects({card}) + card:add_to_deck() + table.insert(G.playing_cards, card) + dont_dissolve = true + delay_fac = 0.2 + elseif card.ability.set == 'Joker' then + card:add_to_deck() + G.jokers:emplace(card) + play_sound('card1', 0.8, 0.6) + play_sound('generic1') + dont_dissolve = true + delay_fac = 0.2 + elseif card.ability.set == 'Booster' then + delay(0.1) + if card.ability.booster_pos then G.GAME.current_round.used_packs[card.ability.booster_pos] = 'USED' end + draw_card(G.hand, G.play, 1, 'up', true, card, nil, true) + if not card.from_tag then + G.GAME.round_scores.cards_purchased.amt = G.GAME.round_scores.cards_purchased.amt + 1 + end + e.config.ref_table:open() + elseif card.ability.set == 'Voucher' then + delay(0.1) + draw_card(G.hand, G.play, 1, 'up', true, card, nil, true) + G.GAME.round_scores.cards_purchased.amt = G.GAME.round_scores.cards_purchased.amt + 1 + if area == G.pack_cards then e.config.ref_table.cost = 0 end + e.config.ref_table:redeem() + end + if card.ability.set == 'Booster' then + G.CONTROLLER.locks.use = false + G.TAROT_INTERRUPT = nil + else + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.2, + func = function() + if not dont_dissolve then card:start_dissolve() end + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.1, + func = function() + G.STATE = prev_state + G.TAROT_INTERRUPT=nil + G.CONTROLLER.locks.use = false + + if (prev_state == G.STATES.TAROT_PACK or prev_state == G.STATES.PLANET_PACK or + prev_state == G.STATES.SPECTRAL_PACK or prev_state == G.STATES.STANDARD_PACK or + prev_state == G.STATES.SMODS_BOOSTER_OPENED or + prev_state == G.STATES.BUFFOON_PACK) and G.booster_pack then + if area == G.consumeables or area == G.hand then + G.booster_pack.alignment.offset.y = G.booster_pack.alignment.offset.py + G.booster_pack.alignment.offset.py = nil + elseif G.GAME.pack_choices and G.GAME.pack_choices > 1 then + if G.booster_pack.alignment.offset.py then + G.booster_pack.alignment.offset.y = G.booster_pack.alignment.offset.py + G.booster_pack.alignment.offset.py = nil + end + G.GAME.pack_choices = G.GAME.pack_choices - 1 + else + G.CONTROLLER.interrupt.focus = true + if prev_state == G.STATES.TAROT_PACK then inc_career_stat('c_tarot_reading_used', 1) end + if prev_state == G.STATES.PLANET_PACK then inc_career_stat('c_planetarium_used', 1) end + G.FUNCS.end_consumeable(nil, delay_fac) + end + else + if G.shop then + G.shop.alignment.offset.y = G.shop.alignment.offset.py + G.shop.alignment.offset.py = nil + end + if G.blind_select then + G.blind_select.alignment.offset.y = G.blind_select.alignment.offset.py + G.blind_select.alignment.offset.py = nil + end + if G.round_eval then + G.round_eval.alignment.offset.y = G.round_eval.alignment.offset.py + G.round_eval.alignment.offset.py = nil + end + if area and area.cards and area.cards[1] then + G.E_MANAGER:add_event(Event({func = function() + G.E_MANAGER:add_event(Event({func = function() + G.CONTROLLER.interrupt.focus = nil + if card.ability.set == 'Voucher' then + G.CONTROLLER:snap_to({node = G.shop:get_UIE_by_ID('next_round_button')}) + elseif area then + G.CONTROLLER:recall_cardarea_focus(area) + end + return true end })) + return true end })) + end + end + return true + end})) + return true + end})) + end + end + + G.FUNCS.sell_card = function(e) + local card = e.config.ref_table + card:sell_card() + for i = 1, #G.jokers.cards do + if G.jokers.cards[i] ~= card then + G.jokers.cards[i]:calculate_joker({selling_card = true, card = card}) + end + end + end + + G.FUNCS.can_confirm_contest_name = function(e) + if G.SETTINGS.COMP and G.SETTINGS.COMP.name ~= '' then + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + else + e.config.colour = G.C.PALE_GREEN + e.config.button = 'confirm_contest_name' + end + end + + G.FUNCS.confirm_contest_name = function(e) + G.SETTINGS.COMP.submission_name = true + if G.MAIN_MENU_UI then G.MAIN_MENU_UI:remove() end + if G.PROFILE_BUTTON then G.PROFILE_BUTTON:remove() end + set_main_menu_UI() + G:save_progress() + G.FILE_HANDLER.force = true + end + + G.FUNCS.change_contest_name = function(e) + G.SETTINGS.COMP.name = '' + if G.MAIN_MENU_UI then G.MAIN_MENU_UI:remove() end + if G.PROFILE_BUTTON then G.PROFILE_BUTTON:remove() end + set_main_menu_UI() + end + + G.FUNCS.skip_tutorial_section = function(e) + G.OVERLAY_TUTORIAL.skip_steps = true + + if G.OVERLAY_TUTORIAL.Jimbo then G.OVERLAY_TUTORIAL.Jimbo:remove() end + if G.OVERLAY_TUTORIAL.content then G.OVERLAY_TUTORIAL.content:remove() end + G.OVERLAY_TUTORIAL:remove() + G.OVERLAY_TUTORIAL = nil + G.E_MANAGER:clear_queue('tutorial') + if G.SETTINGS.tutorial_progress.section == 'small_blind' then + G.SETTINGS.tutorial_progress.completed_parts['small_blind'] = true + elseif G.SETTINGS.tutorial_progress.section == 'big_blind' then + G.SETTINGS.tutorial_progress.completed_parts['big_blind'] = true + G.SETTINGS.tutorial_progress.forced_tags = nil + elseif G.SETTINGS.tutorial_progress.section == 'second_hand' then + G.SETTINGS.tutorial_progress.completed_parts['second_hand'] = true + G.SETTINGS.tutorial_progress.hold_parts['second_hand'] = true + elseif G.SETTINGS.tutorial_progress.section == 'first_hand' then + G.SETTINGS.tutorial_progress.completed_parts['first_hand'] = true + G.SETTINGS.tutorial_progress.completed_parts['first_hand_2'] = true + G.SETTINGS.tutorial_progress.completed_parts['first_hand_3'] = true + G.SETTINGS.tutorial_progress.completed_parts['first_hand_4'] = true + G.SETTINGS.tutorial_progress.completed_parts['first_hand_section'] = true + elseif G.SETTINGS.tutorial_progress.section == 'shop' then + G.SETTINGS.tutorial_progress.completed_parts['shop_1'] = true + G.SETTINGS.tutorial_progress.forced_voucher = nil + end + end + +G.FUNCS.shop_voucher_empty = function(e) + if (G.shop_vouchers and G.shop_vouchers.cards and (G.shop_vouchers.cards[1] or G.GAME.current_round.voucher)) then + e.states.visible = false + elseif G.SETTINGS.language ~= 'en-us' then + e.states.visible = false + else + e.states.visible = true + end +end + +G.FUNCS.check_for_buy_space = function(card) + if card.ability.set ~= 'Voucher' and + card.ability.set ~= 'Enhanced' and + card.ability.set ~= 'Default' and + not (card.ability.set == 'Joker' and #G.jokers.cards < G.jokers.config.card_limit + ((card.edition and card.edition.negative) and 1 or 0)) and + not (card.ability.consumeable and #G.consumeables.cards < G.consumeables.config.card_limit + ((card.edition and card.edition.negative) and 1 or 0)) then + alert_no_space(card, card.ability.consumeable and G.consumeables or G.jokers) + return false + end + return true +end + +G.FUNCS.buy_from_shop = function(e) + local c1 = e.config.ref_table + if c1 and c1:is(Card) then + if e.config.id ~= 'buy_and_use' then + if not G.FUNCS.check_for_buy_space(c1) then + e.disable_button = nil + return false + end + end + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + c1.area:remove_card(c1) + c1:add_to_deck() + if c1.children.price then c1.children.price:remove() end + c1.children.price = nil + if c1.children.buy_button then c1.children.buy_button:remove() end + c1.children.buy_button = nil + remove_nils(c1.children) + if c1.ability.set == 'Default' or c1.ability.set == 'Enhanced' then + inc_career_stat('c_playing_cards_bought', 1) + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + G.deck:emplace(c1) + c1.playing_card = G.playing_card + playing_card_joker_effects({c1}) + table.insert(G.playing_cards, c1) + elseif e.config.id ~= 'buy_and_use' then + if c1.ability.consumeable then + G.consumeables:emplace(c1) + else + G.jokers:emplace(c1) + end + G.E_MANAGER:add_event(Event({func = function() c1:calculate_joker({buying_card = true, card = c1}) return true end})) + end + --Tallies for unlocks + G.GAME.round_scores.cards_purchased.amt = G.GAME.round_scores.cards_purchased.amt + 1 + if c1.ability.consumeable then + if c1.config.center.set == 'Planet' then + inc_career_stat('c_planets_bought', 1) + elseif c1.config.center.set == 'Tarot' then + inc_career_stat('c_tarots_bought', 1) + end + elseif c1.ability.set == 'Joker' then + G.GAME.current_round.jokers_purchased = G.GAME.current_round.jokers_purchased + 1 + end + + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({buying_card = true, card = c1}) + end + + if G.GAME.modifiers.inflation then + G.GAME.inflation = G.GAME.inflation + 1 + G.E_MANAGER:add_event(Event({func = function() + for k, v in pairs(G.I.CARD) do + if v.set_cost then v:set_cost() end + end + return true end })) + end + + play_sound('card1') + inc_career_stat('c_shop_dollars_spent', c1.cost) + if c1.cost ~= 0 then + ease_dollars(-c1.cost) + end + G.CONTROLLER:save_cardarea_focus('jokers') + G.CONTROLLER:recall_cardarea_focus('jokers') + + if e.config.id == 'buy_and_use' then + G.FUNCS.use_card(e, true) + end + return true + end + })) + end +end + + G.FUNCS.toggle_shop = function(e) + stop_use() + G.CONTROLLER.locks.toggle_shop = true + if G.shop then + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({ending_shop = true}) + end + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.shop.alignment.offset.y = G.ROOM.T.y + 29 + G.SHOP_SIGN.alignment.offset.y = -15 + if G.GAME.oldbpfactor then G.GAME.oldbpfactor = math.max(G.GAME.oldbpfactor - G.GAME.oldbpfactor/8, 1) end + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.5, + func = function() + G.shop:remove() + G.shop = nil + G.SHOP_SIGN:remove() + G.SHOP_SIGN = nil + G.STATE_COMPLETE = false + G.STATE = G.STATES.BLIND_SELECT + G.CONTROLLER.locks.toggle_shop = nil + return true + end + })) + end + end + + G.FUNCS.select_blind = function(e) + stop_use() + if G.blind_select then + G.GAME.facing_blind = true + G.blind_prompt_box:get_UIE_by_ID('prompt_dynatext1').config.object.pop_delay = 0 + G.blind_prompt_box:get_UIE_by_ID('prompt_dynatext1').config.object:pop_out(5) + G.blind_prompt_box:get_UIE_by_ID('prompt_dynatext2').config.object.pop_delay = 0 + G.blind_prompt_box:get_UIE_by_ID('prompt_dynatext2').config.object:pop_out(5) + + G.E_MANAGER:add_event(Event({ + trigger = 'before', delay = 0.2, + func = function() + G.blind_prompt_box.alignment.offset.y = -10 + G.blind_select.alignment.offset.y = 40 + G.blind_select.alignment.offset.x = 0 + return true + end})) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + ease_round(1) + inc_career_stat('c_rounds', 1) + if _DEMO then + G.SETTINGS.DEMO_ROUNDS = (G.SETTINGS.DEMO_ROUNDS or 0) + 1 + inc_steam_stat('demo_rounds') + G:save_settings() + end + G.GAME.round_resets.blind = e.config.ref_table + G.GAME.round_resets.blind_states[G.GAME.blind_on_deck] = 'Current' + G.blind_select:remove() + G.blind_prompt_box:remove() + G.blind_select = nil + delay(0.2) + return true + end})) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + new_round() + return true + end + })) + end + end + + G.FUNCS.skip_booster = function(e) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({skipping_booster = true}) + end + G.FUNCS.end_consumeable(e) + end + + G.FUNCS.end_consumeable = function(e, delayfac) + delayfac = delayfac or 1 + stop_use() + if G.booster_pack then + if G.booster_pack_sparkles then G.booster_pack_sparkles:fade(1*delayfac) end + if G.booster_pack_stars then G.booster_pack_stars:fade(1*delayfac) end + if G.booster_pack_meteors then G.booster_pack_meteors:fade(1*delayfac) end + G.booster_pack.alignment.offset.y = G.ROOM.T.y + 9 + + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.2*delayfac,blocking = false, blockable = false, + func = function() + G.booster_pack:remove() + G.booster_pack = nil + return true + end})) + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 1*delayfac,blocking = false, blockable = false, + func = function() + if G.booster_pack_sparkles then G.booster_pack_sparkles:remove(); G.booster_pack_sparkles = nil end + if G.booster_pack_stars then G.booster_pack_stars:remove(); G.booster_pack_stars = nil end + if G.booster_pack_meteors then G.booster_pack_meteors:remove(); G.booster_pack_meteors = nil end + return true + end})) + end + + delay(0.2*delayfac) + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.2*delayfac, + func = function() + G.FUNCS.draw_from_hand_to_deck() + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.2*delayfac, + func = function() + if G.shop and G.shop.alignment.offset.py then + G.shop.alignment.offset.y = G.shop.alignment.offset.py + G.shop.alignment.offset.py = nil + end + if G.blind_select and G.blind_select.alignment.offset.py then + G.blind_select.alignment.offset.y = G.blind_select.alignment.offset.py + G.blind_select.alignment.offset.py = nil + end + if G.round_eval and G.round_eval.alignment.offset.py then + G.round_eval.alignment.offset.y = G.round_eval.alignment.offset.py + G.round_eval.alignment.offset.py = nil + end + G.CONTROLLER.interrupt.focus = true + + G.E_MANAGER:add_event(Event({func = function() + if G.shop then G.CONTROLLER:snap_to({node = G.shop:get_UIE_by_ID('next_round_button')}) end + return true end })) + G.STATE = G.GAME.PACK_INTERRUPT + ease_background_colour_blind(G.GAME.PACK_INTERRUPT) + G.GAME.PACK_INTERRUPT = nil + Handy.insta_booster_skip.is_skipped = false + return true + end})) + for i = 1, #G.GAME.tags do + if G.GAME.tags[i]:apply_to_run({type = 'new_blind_choice'}) then break end + end + + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.2*delayfac, + func = function() + save_run() + return true + end})) + + return true + end})) + end + + G.FUNCS.blind_choice_handler = function(e) + if not e.config.ref_table.run_info and G.blind_select and G.blind_select.VT.y < 10 and e.config.id and G.blind_select_opts[string.lower(e.config.id)] then + if e.UIBox.role.xy_bond ~= 'Weak' then e.UIBox:set_role({xy_bond = 'Weak'}) end + if (e.config.ref_table.deck ~= 'on' and e.config.id == G.GAME.blind_on_deck) or + (e.config.ref_table.deck ~= 'off' and e.config.id ~= G.GAME.blind_on_deck) then + + local _blind_choice = G.blind_select_opts[string.lower(e.config.id)] + local _top_button = e.UIBox:get_UIE_by_ID('select_blind_button') + local _border = e.UIBox.UIRoot.children[1].children[1] + local _tag = e.UIBox:get_UIE_by_ID('tag_'..e.config.id) + local _tag_container = e.UIBox:get_UIE_by_ID('tag_container') + if _tag_container and not G.SETTINGS.tutorial_complete and not G.SETTINGS.tutorial_progress.completed_parts['shop_1'] then _tag_container.states.visible = false + elseif _tag_container then _tag_container.states.visible = true end + if e.config.id == G.GAME.blind_on_deck then + e.config.ref_table.deck = 'on' + e.config.draw_after = false + e.config.colour = G.C.CLEAR + _border.parent.config.outline = 2 + _border.parent.config.outline_colour = G.C.UI.TRANSPARENT_DARK + _border.config.outline_colour = _border.config.outline and _border.config.outline_colour or get_blind_main_colour(e.config.id) + _border.config.outline = 1.5 + _blind_choice.alignment.offset.y = -0.9 + if _tag and _tag_container then + _tag_container.children[2].config.draw_after = false + _tag_container.children[2].config.colour = G.C.BLACK + _tag.children[2].config.button = 'skip_blind' + _tag.config.outline_colour = adjust_alpha(G.C.BLUE, 0.5) + _tag.children[2].config.hover = true + _tag.children[2].config.colour = G.C.RED + _tag.children[2].children[1].config.colour = G.C.UI.TEXT_LIGHT + local _sprite = _tag.config.ref_table + _sprite.config.force_focus = nil + end + if _top_button then + G.E_MANAGER:add_event(Event({func = function() + G.CONTROLLER:snap_to({node = _top_button}) + return true end })) + _top_button.config.button = 'select_blind' + _top_button.config.colour = G.C.FILTER + _top_button.config.hover = true + _top_button.children[1].config.colour = G.C.WHITE + end + elseif e.config.id ~= G.GAME.blind_on_deck then + e.config.ref_table.deck = 'off' + e.config.draw_after = true + e.config.colour = adjust_alpha(G.GAME.round_resets.blind_states[e.config.id] == 'Skipped' and mix_colours(G.C.BLUE, G.C.L_BLACK, 0.1) or G.C.L_BLACK, 0.5) + _border.parent.config.outline = nil + _border.parent.config.outline_colour = nil + _border.config.outline_colour = nil + _border.config.outline = nil + _blind_choice.alignment.offset.y = -0.2 + if _tag and _tag_container then + if G.GAME.round_resets.blind_states[e.config.id] == 'Skipped' or + G.GAME.round_resets.blind_states[e.config.id] == 'Defeated' then + _tag_container.children[2]:set_role({xy_bond = 'Weak'}) + _tag_container.children[2]:align(0, 10) + _tag_container.children[1]:set_role({xy_bond = 'Weak'}) + _tag_container.children[1]:align(0, 10) + end + if G.GAME.round_resets.blind_states[e.config.id] == 'Skipped' then + _blind_choice.children.alert = UIBox{ + definition = create_UIBox_card_alert({text_rot = -0.35, no_bg = true,text = localize('k_skipped_cap'), bump_amount = 1, scale = 0.9, maxw = 3.4}), + config = { + align="tmi", + offset = {x = 0, y = 2.2}, + major = _blind_choice, parent = _blind_choice} + } + end + _tag.children[2].config.button = nil + _tag.config.outline_colour = G.C.UI.BACKGROUND_INACTIVE + _tag.children[2].config.hover = false + _tag.children[2].config.colour = G.C.UI.BACKGROUND_INACTIVE + _tag.children[2].children[1].config.colour = G.C.UI.TEXT_INACTIVE + local _sprite = _tag.config.ref_table + _sprite.config.force_focus = true + end + if _top_button then + _top_button.config.colour = G.C.UI.BACKGROUND_INACTIVE + _top_button.config.button = nil + _top_button.config.hover = false + _top_button.children[1].config.colour = G.C.UI.TEXT_INACTIVE + end + end + end + end + end + + G.FUNCS.hover_tag_proxy = function(e) + if not e.parent or not e.parent.states then return end + if (e.states.hover.is or e.parent.states.hover.is) and (e.created_on_pause == G.SETTINGS.paused) and + not e.parent.children.alert then + local _sprite = e.config.ref_table:get_uibox_table() + e.parent.children.alert = UIBox{ + definition = G.UIDEF.card_h_popup(_sprite), + config = {align="tm", offset = {x = 0, y = -0.1}, + major = e.parent, + instance_type = 'POPUP'}, + } + _sprite:juice_up(0.05, 0.02) + play_sound('paper1', math.random()*0.1 + 0.55, 0.42) + play_sound('tarot2', math.random()*0.1 + 0.55, 0.09) + e.parent.children.alert.states.collide.can = false + elseif e.parent.children.alert and + ((not e.states.collide.is and not e.parent.states.collide.is) or (e.created_on_pause ~= G.SETTINGS.paused)) then + e.parent.children.alert:remove() + e.parent.children.alert = nil + end + end + + G.FUNCS.skip_blind = function(e) + stop_use() + G.CONTROLLER.locks.skip_blind = true + G.E_MANAGER:add_event(Event({ + no_delete = true, + trigger = 'after', + blocking = false,blockable = false, + delay = 2.5, + timer = 'TOTAL', + func = function() + G.CONTROLLER.locks.skip_blind = nil + return true + end + })) + local _tag = e.UIBox:get_UIE_by_ID('tag_container') + G.GAME.skips = (G.GAME.skips or 0) + 1 + if _tag and not G.GAME.events.ev_cry_choco2 then + add_tag(_tag.config.ref_table, true) + local skipped, skip_to = G.GAME.blind_on_deck or 'Small', + G.GAME.blind_on_deck == 'Small' and 'Big' or G.GAME.blind_on_deck == 'Big' and 'Boss' or 'Boss' + G.GAME.round_resets.blind_states[skipped] = 'Skipped' + G.GAME.round_resets.blind_states[skip_to] = 'Select' + G.GAME.blind_on_deck = skip_to + play_sound('generic1') + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + delay(0.3) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({skip_blind = true}) + end + save_run() + for i = 1, #G.GAME.tags do + G.GAME.tags[i]:apply_to_run({type = 'immediate'}) + end + for i = 1, #G.GAME.tags do + if G.GAME.tags[i]:apply_to_run({type = 'new_blind_choice'}) then break end + end + return true + end + })) + end + end + + G.FUNCS.reroll_boss_button = function(e) + if ((G.GAME.dollars-G.GAME.bankrupt_at) - cry_cheapest_boss_reroll() >= 0) and + (G.GAME.used_vouchers["v_retcon"] or + (G.GAME.used_vouchers["v_directors_cut"] and not G.GAME.round_resets.boss_rerolled)) then + e.config.colour = G.C.RED + e.config.button = 'reroll_boss' + e.children[1].children[1].config.shadow = true + if e.children[2] then e.children[2].children[1].config.shadow = true end + else + e.config.colour = G.C.UI.BACKGROUND_INACTIVE + e.config.button = nil + e.children[1].children[1].config.shadow = false + if e.children[2] then e.children[2].children[1].config.shadow = false end + end + end + + G.FUNCS.reroll_boss = function(e) + stop_use() + G.GAME.round_resets.boss_rerolled = true + if not G.from_boss_tag then ease_dollars(-cry_cheapest_boss_reroll()) end + G.from_boss_tag = nil + G.CONTROLLER.locks.boss_reroll = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + play_sound('other1') + G.blind_select_opts.boss:set_role({xy_bond = 'Weak'}) + G.blind_select_opts.boss.alignment.offset.y = 20 + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.3, + func = (function() + local par = G.blind_select_opts.boss.parent + G.GAME.round_resets.blind_choices.Boss = get_new_boss() + + G.blind_select_opts.boss:remove() + G.blind_select_opts.boss = UIBox{ + T = {par.T.x, 0, 0, 0, }, + definition = + {n=G.UIT.ROOT, config={align = "cm", colour = G.C.CLEAR}, nodes={ + UIBox_dyn_container({create_UIBox_blind_choice('Boss')},false,get_blind_main_colour('Boss'), mix_colours(G.C.BLACK, get_blind_main_colour('Boss'), 0.8)) + }}, + config = {align="bmi", + offset = {x=0,y=G.ROOM.T.y + 9}, + major = par, + xy_bond = 'Weak' + } + } + par.config.object = G.blind_select_opts.boss + par.config.object:recalculate() + G.blind_select_opts.boss.parent = par + G.blind_select_opts.boss.alignment.offset.y = 0 + + G.E_MANAGER:add_event(Event({blocking = false, trigger = 'after', delay = 0.5,func = function() + G.CONTROLLER.locks.boss_reroll = nil + return true + end + })) + + save_run() + for i = 1, #G.GAME.tags do + if G.GAME.tags[i]:apply_to_run({type = 'new_blind_choice'}) then break end + end + return true + end) + })) + end + + G.FUNCS.reroll_shop = function(e) + stop_use() + G.CONTROLLER.locks.shop_reroll = true + if G.CONTROLLER:save_cardarea_focus('shop_jokers') then G.CONTROLLER.interrupt.focus = true end + if G.GAME.current_round.reroll_cost > 0 then + inc_career_stat('c_shop_dollars_spent', G.GAME.current_round.reroll_cost) + inc_career_stat('c_shop_rerolls', 1) + ease_dollars(-G.GAME.current_round.reroll_cost) + end + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + local final_free = G.GAME.current_round.free_rerolls > 0 + G.GAME.current_round.free_rerolls = math.max(G.GAME.current_round.free_rerolls - 1, 0) + G.GAME.round_scores.times_rerolled.amt = G.GAME.round_scores.times_rerolled.amt + 1 + + calculate_reroll_cost(final_free) + for i = #G.shop_jokers.cards,1, -1 do + local c = G.shop_jokers:remove_card(G.shop_jokers.cards[i]) + c:remove() + c = nil + end + + --save_run() + + play_sound('coin2') + play_sound('other1') + + for i = 1, G.GAME.shop.joker_max - #G.shop_jokers.cards do + local new_shop_card = create_card_for_shop(G.shop_jokers) + G.shop_jokers:emplace(new_shop_card) + new_shop_card:juice_up() + end + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.3, + func = function() + G.E_MANAGER:add_event(Event({ + func = function() + G.CONTROLLER.interrupt.focus = false + G.CONTROLLER.locks.shop_reroll = false + G.CONTROLLER:recall_cardarea_focus('shop_jokers') + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({reroll_shop = true}) + end + return true + end + })) + return true + end + })) + G.E_MANAGER:add_event(Event({ func = function() save_run(); return true end})) + end + +G.FUNCS.cash_out = function(e) +if Handy.insta_cash_out.is_skipped and e.config.button then return end + stop_use() + if G.round_eval then + e.config.button = nil + G.round_eval.alignment.offset.y = G.ROOM.T.y + 15 + G.round_eval.alignment.offset.x = 0 + Handy.insta_cash_out.is_button_created = false + G.deck:shuffle('cashout'..G.GAME.round_resets.ante) + G.deck:hard_set_T() + delay(0.3) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + if G.round_eval then + G.round_eval:remove() + G.round_eval = nil + end + G.GAME.current_round.jokers_purchased = 0 + G.GAME.current_round.discards_left = math.max(0, G.GAME.round_resets.discards + G.GAME.round_bonus.discards) + G.GAME.current_round.hands_left = (math.max(1, G.GAME.round_resets.hands + G.GAME.round_bonus.next_hands)) + G.STATE = G.STATES.SHOP + Handy.insta_cash_out.is_skipped = false + G.GAME.shop_free = nil + G.GAME.shop_d6ed = nil + G.STATE_COMPLETE = false + return true + end + })) + ease_dollars(G.GAME.current_round.dollars) + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.previous_round.dollars = G.GAME.dollars + return true + end + })) + play_sound("coin7") + G.VIBRATION = G.VIBRATION + 1 + end + ease_chips(to_big(0)) + if G.GAME.round_resets.blind_states.Boss == 'Defeated' then + G.GAME.round_resets.blind_ante = G.GAME.round_resets.ante + G.GAME.round_resets.blind_tags.Small = get_next_tag_key() + G.GAME.round_resets.blind_tags.Big = get_next_tag_key() + end + reset_blinds() + delay(0.6) +end + +G.FUNCS.start_run = function(e, args) +args = args or {} + G.SETTINGS.paused = true + if e and e.config.id == 'restart_button' then G.GAME.viewed_back = nil end + G.E_MANAGER:clear_queue() + G.FUNCS.wipe_on() + G.E_MANAGER:add_event(Event({ + no_delete = true, + func = function() + G:delete_run() + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + no_delete = true, + func = function() + G:start_run(args) + return true + end + })) + G.FUNCS.wipe_off() +end + +G.FUNCS.go_to_menu = function(e) + G.SETTINGS.paused = true + G.E_MANAGER:clear_queue() + G.FUNCS.wipe_on() + G.E_MANAGER:add_event(Event({ + no_delete = true, + func = function() + G:delete_run() + return true + end + })) + G.E_MANAGER:add_event(Event({ + no_delete = true, + blockable = true, + blocking = false, + func = function() + G:main_menu('game') + return true + end + })) + G.FUNCS.wipe_off() +end + +G.FUNCS.go_to_demo_cta = function(e) + G.SETTINGS.paused = true + G.E_MANAGER:clear_queue(nil, G.exception_queue) + play_sound('explosion_buildup1', nil, 0.3) + play_sound('whoosh1', 0.7, 0.8) + play_sound('introPad1', 0.704, 0.8) + G.video_organ = 0.6 + G.FUNCS.wipe_on(nil, true, nil, G.C.WHITE) + G.E_MANAGER:add_event(Event({ + no_delete = true, + func = function() + G:delete_run() + return true + end + })) + G.E_MANAGER:add_event(Event({ + no_delete = true, + blockable = true, + blocking = false, + func = function() + G:demo_cta() + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.3, + no_delete = true, + blockable = false, + blocking = false, + func = function() + G.video_organ = nil + G.normal_music_speed = nil + return true + end })) + return true + end + })) + G.FUNCS.wipe_off() +end + +G.FUNCS.show_main_cta = function(e) + if e then + if e.config.id == 'lose_cta' and not G.SETTINGS.DEMO.lose_CTA_shown then + G.SETTINGS.DEMO.lose_CTA_shown = true + end + if e.config.id == 'win_cta' and not G.SETTINGS.DEMO.win_CTA_shown then + G.SETTINGS.DEMO.win_CTA_shown = true + end + end + + G:save_progress() + + G.SETTINGS.paused = true + G.normal_music_speed = true + + G.FUNCS.overlay_menu{ + definition = create_UIBox_demo_video_CTA(), + config = {no_esc = true} + } +end + +G.FUNCS.wipe_on = function(message, no_card, timefac, alt_colour) + timefac = timefac or 1 + if G.screenwipe then return end + G.CONTROLLER.locks.wipe = true + G.STAGE_OBJECT_INTERRUPT = true + local colours = { + black = HEX("4f6367FF"), + white = {1, 1, 1, 1} + } + if not no_card then + G.screenwipecard = Card(1, 1, G.CARD_W, G.CARD_H, pseudorandom_element(G.P_CARDS), G.P_CENTERS.c_base) + G.screenwipecard.sprite_facing = 'back' + G.screenwipecard.facing = 'back' + G.screenwipecard.states.hover.can = false + G.screenwipecard:juice_up(0.5, 1) + end + local message_t = nil + if message then + message_t = {} + for k, v in ipairs(message) do + table.insert(message_t, {n=G.UIT.R, config={align = "cm"}, nodes={{n=G.UIT.O, config={object = DynaText({string = v or '', colours = {math.min(G.C.BACKGROUND.C[1], G.C.BACKGROUND.C[2]) > 0.5 and G.C.BLACK or G.C.WHITE},shadow = true, silent = k ~= 1, float = true, scale = 1.3, pop_in = 0, pop_in_rate = 2, rotate = 1})}}}}) + end + end + + G.screenwipe = UIBox{ + definition = + {n=G.UIT.ROOT, config = {align = "cm", minw =0, minh =0 ,padding = 0.15, r = 0.1, colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm"}, nodes={ + message and {n=G.UIT.R, config={id = 'text', align = "cm", padding = 0.7}, nodes=message_t} or nil, + not no_card and {n=G.UIT.O, config={object = G.screenwipecard, role = {role_type = 'Major'}}} or nil + }}, + }}, + config = {align="cm", offset = {x=0,y=0}, major = G.ROOM_ATTACH} + } + G.screenwipe.colours = colours + G.screenwipe.children.particles = Particles(0, 0, 0,0, { + timer = 0, + max = 1, + scale = 40, + speed = 0, + lifespan = 1.7*timefac, + attach = G.screenwipe, + colours = {alt_colour or G.C.BACKGROUND.C} + }) + G.STAGE_OBJECT_INTERRUPT = nil + G.screenwipe.alignment.offset.y = 0 + if message then + for k, v in ipairs(G.screenwipe:get_UIE_by_ID('text').children) do + v.children[1].config.object:pulse() + end + end + + + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.7, + no_delete = true, + blockable = false, + func = function() + if not no_card then + G.screenwipecard:flip() + play_sound('cardFan2') + end + return true + end + })) +end + +G.FUNCS.wipe_off = function() + G.E_MANAGER:add_event(Event({ + no_delete = true, + func = function() + delay(0.3) + G.screenwipe.children.particles.max = 0 + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + no_delete = true, + blockable = false, + blocking = false, + timer = 'REAL', + ref_table = G.screenwipe.colours.black, + ref_value = 4, + ease_to = 0, + delay = 0.3, + func = (function(t) return t end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + no_delete = true, + blockable = false, + blocking = false, + timer = 'REAL', + ref_table = G.screenwipe.colours.white, + ref_value = 4, + ease_to = 0, + delay = 0.3, + func = (function(t) return t end) + })) + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.55, + no_delete = true, + blocking = false, + timer = 'REAL', + func = function() + if G.screenwipecard then G.screenwipecard:start_dissolve({G.C.BLACK, G.C.ORANGE,G.C.GOLD, G.C.RED}) end + if G.screenwipe:get_UIE_by_ID('text') then + for k, v in ipairs(G.screenwipe:get_UIE_by_ID('text').children) do + v.children[1].config.object:pop_out(4) + end + end + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 1.1, + no_delete = true, + blocking = false, + timer = 'REAL', + func = function() + G.screenwipe.children.particles:remove() + G.screenwipe:remove() + G.screenwipe.children.particles = nil + G.screenwipe = nil + G.screenwipecard = nil + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 1.2, + no_delete = true, + blocking = true, + timer = 'REAL', + func = function() + return true + end + })) +end diff --git a/lovely/dump/functions/common_events.lua b/lovely/dump/functions/common_events.lua new file mode 100644 index 0000000..00353ff --- /dev/null +++ b/lovely/dump/functions/common_events.lua @@ -0,0 +1,3162 @@ +LOVELY_INTEGRITY = '438a59ab63623d723f338cbbdf5d809c59e9a912c1f610047af5dade5f53ac5f' + +function set_screen_positions() + if G.STAGE == G.STAGES.RUN then + G.hand.T.x = G.TILE_W - G.hand.T.w - 2.85 + G.hand.T.y = G.TILE_H - G.hand.T.h + + G.play.T.x = G.hand.T.x + (G.hand.T.w - G.play.T.w)/2 + G.play.T.y = G.hand.T.y - 3.6 + + G.jokers.T.x = G.hand.T.x - 0.1 + G.jokers.T.y = 0 + + G.consumeables.T.x = G.jokers.T.x + G.jokers.T.w + 0.2 + G.consumeables.T.y = 0 + G.jokers.T.x = G.hand.T.x - 0.1 + G.jokers.T.y = 0 + + G.deck.T.x = G.TILE_W - G.deck.T.w - 0.5 + G.deck.T.y = G.TILE_H - G.deck.T.h + + G.discard.T.x = G.jokers.T.x + G.jokers.T.w/2 + 0.3 + 15 + G.discard.T.y = 4.2 + + G.hand:hard_set_VT() + G.play:hard_set_VT() + G.jokers:hard_set_VT() + G.consumeables:hard_set_VT() + G.deck:hard_set_VT() + G.discard:hard_set_VT() + end + if G.STAGE == G.STAGES.MAIN_MENU then + if G.STATE == G.STATES.DEMO_CTA then + G.title_top.T.x = G.TILE_W/2 - G.title_top.T.w/2 + G.title_top.T.y = G.TILE_H/2 - G.title_top.T.h/2 - 2 + else + G.title_top.T.x = G.TILE_W/2 - G.title_top.T.w/2 + G.title_top.T.y = G.TILE_H/2 - G.title_top.T.h/2 -(G.debug_splash_size_toggle and 2 or 1.2)--||||||||||||||||| + end + + G.title_top:hard_set_VT() + end +end + +function ease_chips(mod) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + local chip_UI = G.HUD:get_UIE_by_ID('chip_UI_count') + + mod = mod or 0 + + --Ease from current chips to the new number of chips + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = false, + ref_table = G.GAME, + ref_value = 'chips', + ease_to = mod, + delay = 0.3, + func = (function(t) return math.floor(t) end) + })) + --Popup text next to the chips in UI showing number of chips gained/lost + chip_UI:juice_up() + --Play a chip sound + play_sound('chips2') + return true + end + })) +end + +function ease_dollars(mod, instant) + local function _mod(mod) + local dollar_UI = G.HUD:get_UIE_by_ID('dollar_text_UI') + mod = mod or 0 + local text = '+'..localize('$') + local col = G.C.MONEY + if mod < 0 then + text = '-'..localize('$') + col = G.C.RED + else + inc_career_stat('c_dollars_earned', mod) + end + --Ease from current chips to the new number of chips + G.GAME.dollars = G.GAME.dollars + mod + check_and_set_high_score('most_money', G.GAME.dollars) + check_for_unlock({type = 'money'}) + dollar_UI.config.object:update() + G.HUD:recalculate() + --Popup text next to the chips in UI showing number of chips gained/lost + attention_text({ + text = text..tostring(math.abs(mod)), + scale = 0.8, + hold = 0.7, + cover = dollar_UI.parent, + cover_colour = col, + align = 'cm', + }) + --Play a chip sound + play_sound('coin1') + end + if instant then + _mod(mod) + else + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + _mod(mod) + return true + end + })) + end +end + +function ease_discard(mod, instant, silent) + local _mod = function(mod) + if math.abs(math.max(G.GAME.current_round.discards_left, mod)) == 0 then return end + local discard_UI = G.HUD:get_UIE_by_ID('discard_UI_count') + mod = mod or 0 + mod = math.max(-G.GAME.current_round.discards_left, mod) + local text = '+' + local col = G.C.GREEN + if mod < 0 then + text = '' + col = G.C.RED + end + --Ease from current chips to the new number of chips + G.GAME.current_round.discards_left = G.GAME.current_round.discards_left + mod + --Popup text next to the chips in UI showing number of chips gained/lost + discard_UI.config.object:update() + G.HUD:recalculate() + attention_text({ + text = text..mod, + scale = 0.8, + hold = 0.7, + cover = discard_UI.parent, + cover_colour = col, + align = 'cm', + }) + --Play a chip sound + if not silent then play_sound('chips2') end + end + if instant then + _mod(mod) + else + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + _mod(mod) + return true + end + })) + end +end + +function ease_hands_played(mod, instant) + local _mod = function(mod) + local hand_UI = G.HUD:get_UIE_by_ID('hand_UI_count') + mod = mod or 0 + local text = '+' + local col = G.C.GREEN + if mod < 0 then + text = '' + col = G.C.RED + end + --Ease from current chips to the new number of chips + G.GAME.current_round.hands_left = G.GAME.current_round.hands_left + mod + hand_UI.config.object:update() + G.HUD:recalculate() + --Popup text next to the chips in UI showing number of chips gained/lost + attention_text({ + text = text..mod, + scale = 0.8, + hold = 0.7, + cover = hand_UI.parent, + cover_colour = col, + align = 'cm', + }) + --Play a chip sound + play_sound('chips2') + end + if instant then + _mod(mod) + else + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + _mod(mod) + return true + end + })) + end +end + +function ease_ante(mod) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + local ante_UI = G.hand_text_area.ante + mod = mod or 0 + local text = '+' + local col = G.C.IMPORTANT + if mod < 0 then + text = '-' + col = G.C.RED + end + G.GAME.round_resets.ante = G.GAME.round_resets.ante + mod + G.GAME.round_resets.ante_disp = number_format(G.GAME.round_resets.ante) + G.GAME.round_resets.ante = Big and (to_number(math.floor(to_big(G.GAME.round_resets.ante)))) or math.floor(G.GAME.round_resets.ante) + check_and_set_high_score('furthest_ante', G.GAME.round_resets.ante) + ante_UI.config.object:update() + G.HUD:recalculate() + --Popup text next to the chips in UI showing number of chips gained/lost + attention_text({ + text = text..tostring(math.abs(mod)), + scale = 1, + hold = 0.7, + cover = ante_UI.parent, + cover_colour = col, + align = 'cm', + }) + --Play a chip sound + play_sound('highlight2', 0.685, 0.2) + play_sound('generic1') + return true + end + })) +end + +function ease_round(mod) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + local round_UI = G.hand_text_area.round + mod = mod or 0 + local text = '+' + local col = G.C.IMPORTANT + if mod < 0 then + text = '' + col = G.C.RED + end + G.GAME.round = G.GAME.round + mod + check_and_set_high_score('furthest_round', G.GAME.round) + check_and_set_high_score('furthest_ante', G.GAME.round_resets.ante) + round_UI.config.object:update() + G.HUD:recalculate() + --Popup text next to the chips in UI showing number of chips gained/lost + attention_text({ + text = text..tostring(math.abs(mod)), + scale = 1, + hold = 0.7, + cover = round_UI.parent, + cover_colour = col, + align = 'cm', + }) + --Play a chip sound + play_sound('timpani', 0.8) + play_sound('generic1') + return true + end + })) +end + +function ease_value(ref_table, ref_value, mod, floored, timer_type, not_blockable, delay, ease_type) + mod = mod or 0 + + --Ease from current chips to the new number of chips + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blockable = (not_blockable == false), + blocking = false, + ref_table = ref_table, + ref_value = ref_value, + ease_to = ref_table[ref_value] + mod, + timer = timer_type, + delay = delay or 0.3, + type = ease_type or nil, + func = (function(t) if floored then return math.floor(t) else return t end end) + })) +end + +function ease_background_colour(args) + for k, v in pairs(G.C.BACKGROUND) do + if args.new_colour and (k == 'C' or k == 'L' or k == 'D') then + if args.special_colour and args.tertiary_colour then + local col_key = k == 'L' and 'new_colour' or k == 'C' and 'special_colour' or k == 'D' and 'tertiary_colour' + ease_value(v, 1, args[col_key][1] - v[1], false, nil, true, 0.6) + ease_value(v, 2, args[col_key][2] - v[2], false, nil, true, 0.6) + ease_value(v, 3, args[col_key][3] - v[3], false, nil, true, 0.6) + else + local brightness = k == 'L' and 1.3 or k == 'D' and (args.special_colour and 0.4 or 0.7) or 0.9 + if k == 'C' and args.special_colour then + ease_value(v, 1, args.special_colour[1] - v[1], false, nil, true, 0.6) + ease_value(v, 2, args.special_colour[2] - v[2], false, nil, true, 0.6) + ease_value(v, 3, args.special_colour[3] - v[3], false, nil, true, 0.6) + else + ease_value(v, 1, args.new_colour[1]*brightness - v[1], false, nil, true, 0.6) + ease_value(v, 2, args.new_colour[2]*brightness - v[2], false, nil, true, 0.6) + ease_value(v, 3, args.new_colour[3]*brightness - v[3], false, nil, true, 0.6) + end + end + end + end + if args.contrast then + ease_value(G.C.BACKGROUND, 'contrast', args.contrast - G.C.BACKGROUND.contrast, false, nil, true, 0.6) + end +end + +function ease_colour(old_colour, new_colour, delay) + ease_value(old_colour, 1, new_colour[1] - old_colour[1], false, 'REAL', nil, delay) + ease_value(old_colour, 2, new_colour[2] - old_colour[2], false, 'REAL', nil, delay) + ease_value(old_colour, 3, new_colour[3] - old_colour[3], false, 'REAL', nil, delay) + ease_value(old_colour, 4, new_colour[4] - old_colour[4], false, 'REAL', nil, delay) +end + + +function ease_background_colour_blind(state, blind_override) + local blindname = ((blind_override or (G.GAME.blind and G.GAME.blind.name ~= '' and G.GAME.blind.name)) or 'Small Blind') + local blindname = (blindname == '' and 'Small Blind' or blindname) + + --For the blind related colours + if state == G.STATES.SHOP then + ease_colour(G.C.DYN_UI.MAIN, mix_colours(G.C.RED, G.C.BLACK, 0.9)) + elseif state == G.STATES.TAROT_PACK then + ease_colour(G.C.DYN_UI.MAIN, mix_colours(G.C.WHITE, G.C.BLACK, 0.9)) + elseif state == G.STATES.SPECTRAL_PACK then + ease_colour(G.C.DYN_UI.MAIN, mix_colours(G.C.SECONDARY_SET.Spectral, G.C.BLACK, 0.9)) + elseif state == G.STATES.STANDARD_PACK then + ease_colour(G.C.DYN_UI.MAIN, G.C.RED) + elseif state == G.STATES.BUFFOON_PACK then + ease_colour(G.C.DYN_UI.MAIN, G.C.FILTER) + elseif state == G.STATES.PLANET_PACK then + ease_colour(G.C.DYN_UI.MAIN, mix_colours(G.C.SECONDARY_SET.Planet, G.C.BLACK, 0.9)) + elseif G.GAME.blind then + G.GAME.blind:change_colour() + end + --For the actual background colour + if state == G.STATES.TAROT_PACK then + ease_background_colour{new_colour = G.C.PURPLE, special_colour = darken(G.C.BLACK, 0.2), contrast = 1.5} + elseif state == G.STATES.SPECTRAL_PACK then + ease_background_colour{new_colour = G.C.SECONDARY_SET.Spectral, special_colour = darken(G.C.BLACK, 0.2), contrast = 2} + elseif state == G.STATES.STANDARD_PACK then + ease_background_colour{new_colour = darken(G.C.BLACK, 0.2), special_colour = G.C.RED, contrast = 3} + elseif state == G.STATES.BUFFOON_PACK then + ease_background_colour{new_colour = G.C.FILTER, special_colour = G.C.BLACK, contrast = 2} + elseif state == G.STATES.PLANET_PACK then + ease_background_colour{new_colour = G.C.BLACK, contrast = 3} + elseif G.GAME.won then + ease_background_colour{new_colour = G.C.BLIND.won, contrast = 1} + elseif blindname == 'Small Blind' or blindname == 'Big Blind' or blindname == '' then + ease_background_colour{new_colour = G.C.BLIND['Small'], contrast = 1} + else + + local boss_col = G.C.BLACK + for k, v in pairs(G.P_BLINDS) do + if v.name == blindname then + if v.boss.showdown then + ease_background_colour{new_colour = G.C.BLUE, special_colour = G.C.RED, tertiary_colour = darken(G.C.BLACK, 0.4), contrast = 3} + return + end + boss_col = v.boss_colour or G.C.BLACK + end + end + ease_background_colour{new_colour = lighten(mix_colours(boss_col, G.C.BLACK, 0.3), 0.1), special_colour = boss_col, contrast = 2} + end +end + +function delay(time, queue) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = time or 1, + func = function() + return true + end + }), queue) +end + +function add_joker(joker, edition, silent, eternal) + local _area = G.P_CENTERS[joker].consumeable and G.consumeables or G.jokers + local _T = _area and _area.T or {x = G.ROOM.T.w/2 - G.CARD_W/2, y = G.ROOM.T.h/2 - G.CARD_H/2} + local card = Card(_T.x, _T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[joker],{discover = true, bypass_discovery_center = true, bypass_discovery_ui = true, bypass_back = G.GAME.selected_back.pos }) + card:start_materialize(nil, silent) + if _area then card:add_to_deck() end + if edition then card:set_edition{[edition] = true} end + if eternal then card:set_eternal(true) end + if _area and card.ability.set == 'Joker' then _area:emplace(card) + elseif G.consumeables then G.consumeables:emplace(card) end + card.created_on_pause = nil + return card +end + +function draw_card(from, to, percent, dir, sort, card, delay, mute, stay_flipped, vol, discarded_only) + percent = percent or 50 + delay = delay or 0.1 + if dir == 'down' then + percent = 1-percent + end + sort = sort or false + local drawn = nil + + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = delay, + func = function() + if card then + if from then card = from:remove_card(card) end + if card then drawn = true end + if card and to == G.hand and not card.states.visible then + card.states.visible = true + end + local stay_flipped = G.GAME and G.GAME.blind and G.GAME.blind:stay_flipped(to, card) + if G.GAME.modifiers.flipped_cards and to == G.hand then + if pseudorandom(pseudoseed('flipped_card')) < 1/G.GAME.modifiers.flipped_cards then + stay_flipped = true + end + end + to:emplace(card, nil, stay_flipped) + else + if to:draw_card_from(from, stay_flipped, discarded_only) then drawn = true end + end + if not mute and drawn then + if from == G.deck or from == G.hand or from == G.play or from == G.jokers or from == G.consumeables or from == G.discard then + G.VIBRATION = G.VIBRATION + 0.6 + end + play_sound('card1', 0.85 + percent*0.2/100, 0.6*(vol or 1)) + end + if sort then + to:sort() + end + return true + end + })) +end + +function highlight_card(card, percent, dir) + percent = percent or 0.5 + local highlight = true + if dir == 'down' then + percent = 1-percent + highlight = false + end + + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 0.1, + func = function() + card:highlight(highlight) + play_sound('cardSlide1', 0.85 + percent*0.2) + return true + end + })) +end + +function play_area_status_text(text, silent, delay) + local delay = delay or 0.6 + G.E_MANAGER:add_event(Event({ + trigger = (delay==0 and 'immediate' or 'before'), + delay = delay, + func = function() + attention_text({ + scale = 0.9, text = text, hold = 0.9, align = 'tm', + major = G.play, offset = {x = 0, y = -1} + }) + if not silent then + G.ROOM.jiggle = G.ROOM.jiggle + 2 + play_sound('cardFan2') + end + return true + end + })) +end + +function level_up_hand(card, hand, instant, amount) + amount = amount or 1 + if not next(find_joker('cry-Universum')) then + G.GAME.hands[hand].level = math.max(0, G.GAME.hands[hand].level + amount) + G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].mult + G.GAME.hands[hand].l_mult*amount, 1) + G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].chips + G.GAME.hands[hand].l_chips*amount, 1) + else + universum_mod = 1 + for i = 1, #G.jokers.cards do + local effects = G.jokers.cards[i]:calculate_joker({cry_universum = true, callback = function(card, effects) + universum_mod = universum_mod * (effects and effects.mod or 1) + end}) + end + G.GAME.hands[hand].level = math.max(0, G.GAME.hands[hand].level + amount) + G.GAME.hands[hand].mult = math.max(G.GAME.hands[hand].mult * (universum_mod)^amount, 1) + G.GAME.hands[hand].chips = math.max(G.GAME.hands[hand].chips * (universum_mod)^amount, 1) + end + if not instant then + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.2, func = function() + play_sound('tarot1') + if card and card.juice_up then card:juice_up(0.8, 0.5) end + G.TAROT_INTERRUPT_PULSE = true + return true end })) + update_hand_text({delay = 0}, {mult = cry_ascend(G.GAME.hands[hand].mult), StatusText = true}) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function() + play_sound('tarot1') + if card and card.juice_up then card:juice_up(0.8, 0.5) end + return true end })) + update_hand_text({delay = 0}, {chips = cry_ascend(G.GAME.hands[hand].chips), StatusText = true}) + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.9, func = function() + play_sound('tarot1') + if card and card.juice_up then card:juice_up(0.8, 0.5) end + G.TAROT_INTERRUPT_PULSE = nil + return true end })) + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.9, delay = 0}, {level=G.GAME.hands[hand].level}) + delay(1.3) + end + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() check_for_unlock{type = 'upgrade_hand', hand = hand, level = G.GAME.hands[hand].level} return true end) + })) +end + +function update_hand_text(config, vals) + G.E_MANAGER:add_event(Event({--This is the Hand name text for the poker hand + trigger = 'before', + blockable = not config.immediate, + delay = config.delay or 0.8, + func = function() + local col = G.C.GREEN + if vals.chips and G.GAME.current_round.current_hand.chips ~= vals.chips then + local delta = (is_number(vals.chips) and is_number(G.GAME.current_round.current_hand.chips)) and (vals.chips - G.GAME.current_round.current_hand.chips) or 0 + if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED + elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta) + else delta = number_format(delta) + end + if type(vals.chips) == 'string' then delta = vals.chips end + G.GAME.current_round.current_hand.chips = vals.chips + G.hand_text_area.chips:update(0) + if vals.StatusText then + attention_text({ + text =delta, + scale = 0.8, + hold = 1, + cover = G.hand_text_area.chips.parent, + cover_colour = mix_colours(G.C.CHIPS, col, 0.1), + emboss = 0.05, + align = 'cm', + cover_align = 'cr' + }) + end + end + if vals.mult and G.GAME.current_round.current_hand.mult ~= vals.mult then + local delta = (is_number(vals.mult) and is_number(G.GAME.current_round.current_hand.mult))and (vals.mult - G.GAME.current_round.current_hand.mult) or 0 + if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED + elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta) + else delta = number_format(delta) + end + if type(vals.mult) == 'string' then delta = vals.mult end + G.GAME.current_round.current_hand.mult = vals.mult + G.hand_text_area.mult:update(0) + if vals.StatusText then + attention_text({ + text =delta, + scale = 0.8, + hold = 1, + cover = G.hand_text_area.mult.parent, + cover_colour = mix_colours(G.C.MULT, col, 0.1), + emboss = 0.05, + align = 'cm', + cover_align = 'cl' + }) + end + if not G.TAROT_INTERRUPT then G.hand_text_area.mult:juice_up() end + end + if vals.handname and G.GAME.current_round.current_hand.handname ~= vals.handname then + G.GAME.current_round.current_hand.handname = vals.handname + if not config.nopulse then + G.hand_text_area.handname.config.object:pulse(0.2) + end + end + if vals.chip_total then G.GAME.current_round.current_hand.chip_total = vals.chip_total;G.hand_text_area.chip_total.config.object:pulse(0.5) end + if vals.level and G.GAME.current_round.current_hand.hand_level ~= ' '..localize('k_lvl')..tostring(vals.level) then + if vals.level == '' then + G.GAME.current_round.current_hand.hand_level = vals.level + else + G.GAME.current_round.current_hand.hand_level = ' '..localize('k_lvl')..tostring(vals.level) + if type(vals.level) == 'number' then + G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[math.min(vals.level, 7)] + else + G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[1] + end + G.hand_text_area.hand_level:juice_up() + end + end + if config.sound and not config.modded then play_sound(config.sound, config.pitch or 1, config.volume or 1) end + if config.modded then + SMODS.juice_up_blind() + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + play_sound('tarot2', 1, 0.4) + end + return true + end})) +end + +function eval_card(card, context) + local enhancement_calculated = false + local center = card.config.center + context = context or {} + local ret = {} + + if context.repetition_only then + if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then + center:calculate(card, context, ret) + enhancement_calculated = true + end + local seals = card:calculate_seal(context) + if seals then + ret.seals = seals + end + return ret + end + + if context.cardarea == G.play then + local chips = card:get_chip_bonus() + if chips > 0 then + ret.chips = chips + end + + local mult = card:get_chip_mult() + if mult > 0 then + ret.mult = mult + end + + local x_mult = card:get_chip_x_mult(context) + if x_mult > 0 then + ret.x_mult = x_mult + end + + local x_chips = card:get_chip_x_bonus() + if x_chips > 0 then + ret.x_chips = x_chips + end + + local e_chips = card:get_chip_e_bonus() + if e_chips > 0 then + ret.e_chips = e_chips + end + + local ee_chips = card:get_chip_ee_bonus() + if ee_chips > 0 then + ret.ee_chips = ee_chips + end + + local eee_chips = card:get_chip_eee_bonus() + if eee_chips > 0 then + ret.eee_chips = eee_chips + end + + local hyper_chips = card:get_chip_hyper_bonus() + if type(hyper_chips) == 'table' and hyper_chips[1] > 0 and hyper_chips[2] > 0 then + ret.hyper_chips = hyper_chips + end + + local e_mult = card:get_chip_e_mult() + if e_mult > 0 then + ret.e_mult = e_mult + end + + local ee_mult = card:get_chip_ee_mult() + if ee_mult > 0 then + ret.ee_mult = ee_mult + end + + local eee_mult = card:get_chip_eee_mult() + if eee_mult > 0 then + ret.eee_mult = eee_mult + end + + local hyper_mult = card:get_chip_hyper_mult() + if type(hyper_mult) == 'table' and hyper_mult[1] > 0 and hyper_mult[2] > 0 then + ret.hyper_mult = hyper_mult + end + local p_dollars = card:get_p_dollars() + if p_dollars > 0 then + ret.p_dollars = p_dollars + end + + if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then + center:calculate(card, context, ret) + enhancement_calculated = true + end + local jokers = card:calculate_joker(context) + if jokers then + ret.jokers = jokers + end + + local edition = card:get_edition(context) + if edition then + ret.edition = edition + end + end + + if context.cardarea == G.hand then + local h_mult = card:get_chip_h_mult() + if h_mult > 0 then + ret.h_mult = h_mult + end + + local h_x_mult = card:get_chip_h_x_mult() + if h_x_mult > 0 then + ret.x_mult = h_x_mult + end + + if card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then + center:calculate(card, context, ret) + enhancement_calculated = true + end + local jokers = card:calculate_joker(context) + if jokers then + ret.jokers = jokers + end + end + + if card.edition and card.edition.key then + local ed = SMODS.Centers[card.edition.key] + if ed.calculate and type(ed.calculate) == 'function' then + context.from_playing_card = true + ed:calculate(card, context) + context.from_playing_card = nil + end + end + if not enhancement_calculated and card.ability.set == 'Enhanced' and center.calculate and type(center.calculate) == 'function' then + center:calculate(card, context, ret) + enhancement_calculated = true + end + local seals = card:calculate_seal(context) + if seals then + ret.seals = seals + end + if context.cardarea == G.jokers or context.card == G.consumeables then + local jokers = nil + if context.edition then + jokers = card:get_edition(context) + elseif context.other_joker then + jokers = context.other_joker:calculate_joker(context) + else + jokers = card:calculate_joker(context) + end + if jokers then + ret.jokers = jokers + end + end + + return ret +end + +function set_alerts() + if G.REFRESH_ALERTS then + G.REFRESH_ALERTS = nil + local alert_joker, alert_voucher, alert_tarot, alert_planet, alert_spectral, alert_blind, alert_edition, alert_tag, alert_seal, alert_booster = false,false,false,false,false,false,false,false,false,false + for k, v in pairs(G.P_CENTERS) do + if v.discovered and not v.alerted then + if v.set == 'Voucher' then alert_voucher = true end + if v.set == 'Tarot' then alert_tarot = true end + if v.set == 'Planet' then alert_planet = true end + if v.set == 'Spectral' then alert_spectral = true end + if v.set == 'Joker' then alert_joker = true end + if v.set == 'Edition' then alert_edition = true end + if v.set == 'Booster' then alert_booster = true end + end + end + for k, v in pairs(G.P_BLINDS) do + if v.discovered and not v.alerted then + alert_blind = true + end + end + for k, v in pairs(G.P_TAGS) do + if v.discovered and not v.alerted then + alert_tag = true + end + end + for k, v in pairs(G.P_SEALS) do + if v.discovered and not v.alerted then + alert_seal = true + end + end + + local alert_any = alert_voucher or alert_joker or alert_tarot or alert_planet or alert_spectral or alert_blind or alert_edition or alert_seal or alert_tag + + G.ARGS.set_alerts_alertables = G.ARGS.set_alerts_alertables or { + {id = 'your_collection', alert_uibox_name = 'your_collection_alert'}, + {id = 'your_collection_jokers', alert_uibox_name = 'your_collection_jokers_alert'}, + {id = 'your_collection_tarots', alert_uibox_name = 'your_collection_tarots_alert'}, + {id = 'your_collection_planets', alert_uibox_name = 'your_collection_planets_alert'}, + {id = 'your_collection_spectrals', alert_uibox_name = 'your_collection_spectrals_alert'}, + {id = 'your_collection_vouchers', alert_uibox_name = 'your_collection_vouchers_alert'}, + {id = 'your_collection_editions', alert_uibox_name = 'your_collection_editions_alert'}, + {id = 'your_collection_blinds', alert_uibox_name = 'your_collection_blinds_alert'}, + {id = 'your_collection_tags', alert_uibox_name = 'your_collection_tags_alert'}, + {id = 'your_collection_seals', alert_uibox_name = 'your_collection_seals_alert'}, + {id = 'your_collection_boosters', alert_uibox_name = 'your_collection_boosters_alert'}, + } + G.ARGS.set_alerts_alertables[1].should_alert = alert_any + G.ARGS.set_alerts_alertables[2].should_alert = alert_joker + G.ARGS.set_alerts_alertables[3].should_alert = alert_tarot + G.ARGS.set_alerts_alertables[4].should_alert = alert_planet + G.ARGS.set_alerts_alertables[5].should_alert = alert_spectral + G.ARGS.set_alerts_alertables[6].should_alert = alert_voucher + G.ARGS.set_alerts_alertables[7].should_alert = alert_edition + G.ARGS.set_alerts_alertables[8].should_alert = alert_blind + G.ARGS.set_alerts_alertables[9].should_alert = alert_tag + G.ARGS.set_alerts_alertables[10].should_alert = alert_seal + G.ARGS.set_alerts_alertables[11].should_alert = alert_booster + table.insert(G.ARGS.set_alerts_alertables, {id = 'mods_button', alert_uibox_name = 'mods_button_alert', should_alert = SMODS.mod_button_alert}) + + for k, v in ipairs(G.ARGS.set_alerts_alertables) do + if G.OVERLAY_MENU and G.OVERLAY_MENU:get_UIE_by_ID(v.id) then + if v.should_alert then + if not G[v.alert_uibox_name] then + G[v.alert_uibox_name] = UIBox{ + definition = create_UIBox_card_alert({red_bad = true}), + config = {align="tri", offset = {x = 0.05, y = -0.05}, major = G.OVERLAY_MENU:get_UIE_by_ID(v.id), instance_type = 'ALERT'} + } + G[v.alert_uibox_name].states.collide.can = false + end + elseif G[v.alert_uibox_name] then + G[v.alert_uibox_name]:remove() + G[v.alert_uibox_name] = nil + end + elseif G[v.alert_uibox_name] then + G[v.alert_uibox_name]:remove() + G[v.alert_uibox_name] = nil + end + end + + if G.MAIN_MENU_UI then + if alert_any then + if not G.collection_alert then + G.collection_alert = UIBox{definition = create_UIBox_card_alert(), config = {align="tri", offset = {x = 0.05, y = -0.05}, major = G.MAIN_MENU_UI:get_UIE_by_ID('collection_button')}} + G.collection_alert.states.collide.can = false + end + elseif G.collection_alert then + G.collection_alert:remove() + G.collection_alert = nil + end + elseif G.collection_alert then + G.collection_alert:remove() + G.collection_alert = nil + end + end +end + +function set_main_menu_UI() + G.MAIN_MENU_UI = UIBox{ + definition = create_UIBox_main_menu_buttons(), + config = {align="bmi", offset = {x=0,y=10}, major = G.ROOM_ATTACH, bond = 'Weak'} + } + G.MAIN_MENU_UI.alignment.offset.y = 0 + G.MAIN_MENU_UI:align_to_major() + G.E_MANAGER:add_event(Event({ + blockable = false, + blocking = false, + func = (function() + if (not G.F_DISP_USERNAME) or (type(G.F_DISP_USERNAME) == 'string') then + G.PROFILE_BUTTON = UIBox{ + definition = create_UIBox_profile_button(), + config = {align="bli", offset = {x=-10,y=0}, major = G.ROOM_ATTACH, bond = 'Weak'}} + G.PROFILE_BUTTON.alignment.offset.x = 0 + G.PROFILE_BUTTON:align_to_major() + return true + end + end) + })) + + + G.CONTROLLER:snap_to{node = G.MAIN_MENU_UI:get_UIE_by_ID('main_menu_play')} +end + +function card_eval_status_text(card, eval_type, amt, percent, dir, extra) + percent = percent or (0.9 + 0.2*math.random()) + if dir == 'down' then + percent = 1-percent + end + + if extra and extra.focus then card = extra.focus end + + local text = '' + local sound = nil + local volume = 1 + local card_aligned = 'bm' + local y_off = 0.15*G.CARD_H + if card.area == G.jokers or card.area == G.consumeables then + y_off = 0.05*card.T.h + elseif card.area == G.hand then + y_off = -0.05*G.CARD_H + card_aligned = 'tm' + elseif card.area == G.play then + y_off = -0.05*G.CARD_H + card_aligned = 'tm' + elseif card.jimbo then + y_off = -0.05*G.CARD_H + card_aligned = 'tm' + end + local config = {} + local delay = 0.65 + local colour = config.colour or (extra and extra.colour) or ( G.C.FILTER ) + local extrafunc = nil + + if eval_type == 'debuff' then + sound = 'cancel' + amt = 1 + colour = G.C.RED + config.scale = 0.6 + text = localize('k_debuffed') + elseif eval_type == 'chips' then + sound = 'chips1' + amt = amt + colour = G.C.CHIPS + text = localize{type='variable',key='a_chips',vars={amt}} + delay = 0.6 + elseif eval_type == 'mult' then + sound = 'multhit1'--'other1' + amt = amt + text = localize{type='variable',key='a_mult',vars={amt}} + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 + elseif (eval_type == 'x_mult') or (eval_type == 'h_x_mult') then + sound = 'multhit2' + volume = 0.7 + amt = amt + text = localize{type='variable',key='a_xmult',vars={amt}} + colour = G.C.XMULT + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'h_mult' then + sound = 'multhit1' + amt = amt + text = localize{type='variable',key='a_mult',vars={amt}} + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'x_chips' then + sound = 'talisman_xchip' + amt = amt + text = 'X' .. amt + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'e_chips' then + sound = 'talisman_echip' + amt = amt + text = '^' .. amt + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'ee_chips' then + sound = 'talisman_eechip' + amt = amt + text = '^^' .. amt + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'eee_chips' then + sound = 'talisman_eeechip' + amt = amt + text = '^^^' .. amt + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'hyper_chips' then + sound = 'talisman_eeechip' + text = (amt[1] > 5 and ('{' .. tostring(amt[1]) .. '}') or string.rep('^', amt[1])) .. tostring(amt[2]) + amt = amt[2] + colour = G.C.CHIPS + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'e_mult' then + sound = 'talisman_emult' + amt = amt + text = '^' .. amt .. ' Mult' + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'ee_mult' then + sound = 'talisman_eemult' + amt = amt + text = '^^' .. amt .. ' Mult' + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'eee_mult' then + sound = 'talisman_eeemult' + amt = amt + text = '^^^' .. amt .. ' Mult' + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'hyper_mult' then + sound = 'talisman_eeemult' + text = (amt[1] > 5 and ('{' .. tostring(amt[1]) .. '}') or string.rep('^', amt[1])) .. tostring(amt[2]) .. ' Mult' + amt = amt[2] + colour = G.C.MULT + config.type = 'fade' + config.scale = 0.7 + elseif eval_type == 'dollars' then + sound = 'coin3' + amt = amt + text = (amt <-0.01 and '-' or '')..localize("$")..tostring(math.abs(amt)) + colour = amt <-0.01 and G.C.RED or G.C.MONEY + elseif eval_type == 'swap' then + sound = 'generic1' + amt = amt + text = localize('k_swapped_ex') + colour = G.C.PURPLE + elseif eval_type == 'extra' or eval_type == 'jokers' then + sound = extra.edition and 'foil2' or extra.mult_mod and 'multhit1' or extra.Xmult_mod and 'multhit2' or extra.Xchip_mod and 'talisman_xchip' or extra.Echip_mod and 'talisman_echip' or extra.Emult_mod and 'talisman_emult' or extra.EEchip_mod and 'talisman_eechip' or extra.EEmult_mod and 'talisman_eemult' or (extra.EEEchip_mod or extra.hyperchip_mod) and 'talisman_eeechip' or (extra.EEEmult_mod or extra.hypermult_mod) and 'talisman_eeemult' or 'generic1' + if extra.edition then + colour = G.C.DARK_EDITION + end + volume = extra.edition and 0.3 or sound == 'multhit2' and 0.7 or 1 + delay = extra.delay or 0.75 + amt = 1 + text = extra.message or text + if not text or text == '' then return end + if not extra.edition and (extra.mult_mod or extra.Xmult_mod) then + colour = G.C.MULT + end + if extra.chip_mod then + config.type = 'fall' + colour = G.C.CHIPS + config.scale = 0.7 + elseif extra.swap then + config.type = 'fall' + colour = G.C.PURPLE + config.scale = 0.7 + else + config.type = 'fall' + config.scale = 0.7 + end + end + delay = delay*1.25 + + if amt > 0 or amt < 0 then + if extra and extra.instant then + if extrafunc then extrafunc() end + attention_text({ + text = text, + scale = config.scale or 1, + hold = delay - 0.2, + backdrop_colour = colour, + align = card_aligned, + major = card, + offset = {x = 0, y = y_off} + }) + play_sound(sound, 0.8+percent*0.2, volume) + if not extra or not extra.no_juice then + if card and card.juice_up then card:juice_up(0.6, 0.1) end + G.ROOM.jiggle = G.ROOM.jiggle + 0.7 + end + else + G.E_MANAGER:add_event(Event({ --Add bonus chips from this card + trigger = 'before', + delay = delay, + func = function() + if extrafunc then extrafunc() end + attention_text({ + text = text, + scale = config.scale or 1, + hold = delay - 0.2, + backdrop_colour = colour, + align = card_aligned, + major = card, + offset = {x = 0, y = y_off} + }) + play_sound(sound, 0.8+percent*0.2, volume) + if not extra or not extra.no_juice then + if card and card.juice_up then card:juice_up(0.6, 0.1) end + G.ROOM.jiggle = G.ROOM.jiggle + 0.7 + end + return true + end + })) + end + end + if extra and extra.playing_cards_created then + playing_card_joker_effects(extra.playing_cards_created) + end +end + +function add_round_eval_row(config) + local config = config or {} + local width = G.round_eval.T.w - 0.51 + local num_dollars = config.dollars or 1 + Handy.insta_cash_out.dollars = config.dollars or 1 + local scale = 0.9 + + if config.name ~= 'bottom' then + total_cashout_rows = (total_cashout_rows or 0) + 1 + if total_cashout_rows > 7 then + return + end + if config.name ~= 'blind1' then + if not G.round_eval.divider_added then + G.E_MANAGER:add_event(Event({ + trigger = 'after',delay = 0.25, + func = function() + local spacer = {n=G.UIT.R, config={align = "cm", minw = width}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {'......................................'}, colours = {G.C.WHITE},shadow = true, float = true, y_offset = -30, scale = 0.45, spacing = 13.5, font = G.LANGUAGES['en-us'].font, pop_in = 0})}} + }} + G.round_eval:add_child(spacer,G.round_eval:get_UIE_by_ID(config.bonus and 'bonus_round_eval' or 'base_round_eval')) + return true + end + })) + delay(0.6) + G.round_eval.divider_added = true + end + else + delay(0.2) + end + + delay(0.2) + + G.E_MANAGER:add_event(Event({ + trigger = 'before',delay = 0.5, + func = function() + --Add the far left text and context first: + local left_text = {} + if config.name == 'blind1' then + local stake_sprite = get_stake_sprite(G.GAME.stake or 1, 0.5) + local obj = G.GAME.blind.config.blind + local blind_sprite = AnimatedSprite(0, 0, 1.2, 1.2, G.ANIMATION_ATLAS[obj.atlas] or G.ANIMATION_ATLAS['blind_chips'], copy_table(G.GAME.blind.pos)) + blind_sprite:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'} + }) + table.insert(left_text, {n=G.UIT.O, config={w=1.2,h=1.2 , object = blind_sprite, hover = true, can_collide = false}}) + + table.insert(left_text, + config.saved and + {n=G.UIT.C, config={padding = 0.05, align = 'cm'}, nodes={ + {n=G.UIT.R, config={align = 'cm'}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {' '..(G.GAME.current_round.semicolon and ";" or localize('ph_mr_bones'))..' '}, colours = {(G.GAME.current_round.semicolon and G.C.SET.Code or G.C.FILTER)}, shadow = true, pop_in = 0, scale = 0.5*scale, silent = true})}} + }} + }} + or {n=G.UIT.C, config={padding = 0.05, align = 'cm'}, nodes={ + {n=G.UIT.R, config={align = 'cm'}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {' '..localize('ph_score_at_least')..' '}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4*scale, silent = true})}} + }}, + {n=G.UIT.R, config={align = 'cm', minh = 0.8}, nodes={ + {n=G.UIT.O, config={w=0.5,h=0.5 , object = stake_sprite, hover = true, can_collide = false}}, + {n=G.UIT.T, config={text = G.GAME.blind.chip_text, scale = scale_number(G.GAME.blind.chips, scale, 100000), colour = G.C.RED, shadow = true}} + }} + }}) + elseif string.find(config.name, 'tag') then + local blind_sprite = Sprite(0, 0, 0.7,0.7, G.ASSET_ATLAS['tags'], copy_table(config.pos)) + blind_sprite:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'} + }) + blind_sprite:juice_up() + table.insert(left_text, {n=G.UIT.O, config={w=0.7,h=0.7 , object = blind_sprite, hover = true, can_collide = false}}) + table.insert(left_text, {n=G.UIT.O, config={object = DynaText({string = {config.condition}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4*scale, silent = true})}}) + elseif config.name == 'hands' then + table.insert(left_text, {n=G.UIT.T, config={text = config.disp or config.dollars, scale = 0.8*scale, colour = G.C.BLUE, shadow = true, juice = true}}) + table.insert(left_text, {n=G.UIT.O, config={object = DynaText({string = {" "..localize{type = 'variable', key = 'remaining_hand_money', vars = {G.GAME.modifiers.money_per_hand or 1}}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4*scale, silent = true})}}) + elseif config.name == 'discards' then + table.insert(left_text, {n=G.UIT.T, config={text = config.disp or config.dollars, scale = 0.8*scale, colour = G.C.RED, shadow = true, juice = true}}) + table.insert(left_text, {n=G.UIT.O, config={object = DynaText({string = {" "..localize{type = 'variable', key = 'remaining_discard_money', vars = {G.GAME.modifiers.money_per_discard or 0}}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4*scale, silent = true})}}) + elseif string.find(config.name, 'joker') then + table.insert(left_text, {n=G.UIT.O, config={object = DynaText({string = localize{type = 'name_text', set = config.card.config.center.set, key = config.card.config.center.key}, colours = {G.C.FILTER}, shadow = true, pop_in = 0, scale = 0.6*scale, silent = true})}}) + elseif config.name == 'interest_payload' then + table.insert(left_text, {n=G.UIT.T, config={text = num_dollars, scale = 0.8*scale, colour = G.C.SECONDARY_SET.Code, shadow = true, juice = true}}) + table.insert(left_text,{n=G.UIT.O, config={object = DynaText({string = {" "..localize{type = 'variable', key = 'interest', vars = {G.GAME.interest_amount*config.payload, 5, G.GAME.interest_amount*config.payload*G.GAME.interest_cap/5}}}, colours = {G.C.SECONDARY_SET.Code}, shadow = true, pop_in = 0, scale = 0.4*scale, silent = true})}}) + elseif config.name == 'interest' then + table.insert(left_text, {n=G.UIT.T, config={text = num_dollars, scale = 0.8*scale, colour = G.C.MONEY, shadow = true, juice = true}}) + table.insert(left_text,{n=G.UIT.O, config={object = DynaText({string = {" "..localize{type = 'variable', key = 'interest', vars = {G.GAME.interest_amount, 5, G.GAME.interest_amount*G.GAME.interest_cap/5}}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4*scale, silent = true})}}) + end + local full_row = {n=G.UIT.R, config={align = "cm", minw = 5}, nodes={ + {n=G.UIT.C, config={padding = 0.05, minw = width*0.55, minh = 0.61, align = "cl"}, nodes=left_text}, + {n=G.UIT.C, config={padding = 0.05,minw = width*0.45, align = "cr"}, nodes={{n=G.UIT.C, config={align = "cm", id = 'dollar_'..config.name},nodes={}}}} + }} + + if config.name == 'blind1' then + G.GAME.blind:juice_up() + end + G.round_eval:add_child(full_row,G.round_eval:get_UIE_by_ID(config.bonus and 'bonus_round_eval' or 'base_round_eval')) + play_sound('cancel', config.pitch or 1) + play_sound('highlight1',( 1.5*config.pitch) or 1, 0.2) + if config.card then config.card:juice_up(0.7, 0.46) end + return true + end + })) + local dollar_row = 0 + if num_dollars > 60 or num_dollars < -60 then + local dollar_string + if num_dollars < 0 then --if negative + dollar_string = '-'..localize('$')..(num_dollars*-1) + G.E_MANAGER:add_event(Event({ + trigger = 'before',delay = 0.38, + func = function() + G.round_eval:add_child( + {n=G.UIT.R, config={align = "cm", id = 'dollar_row_'..(dollar_row+1)..'_'..config.name}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('$')..(num_dollars*-1)}, colours = {G.C.MONEY}, shadow = true, pop_in = 0, scale = 0.65, float = true})}} + }}, + G.round_eval:get_UIE_by_ID('dollar_'..config.name)) + play_sound('coin3', 0.9+0.2*math.random(), 0.7) + play_sound('coin6', 1.3, 0.8) + return true + end + })) + else --if positive + dollar_string = localize('$')..num_dollars + G.E_MANAGER:add_event(Event({ + trigger = 'before',delay = 0.38, + func = function() + G.round_eval:add_child( + {n=G.UIT.R, config={align = "cm", id = 'dollar_row_'..(dollar_row+1)..'_'..config.name}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {localize('$')..format_ui_value(num_dollars)}, colours = {G.C.MONEY}, shadow = true, pop_in = 0, scale = 0.65, float = true})}} + }}, + G.round_eval:get_UIE_by_ID('dollar_'..config.name)) + + play_sound('coin3', 0.9+0.2*math.random(), 0.7) + play_sound('coin6', 1.3, 0.8) + return true + end + })) + --asdf + end else + local dollars_to_loop + if num_dollars < 0 then dollars_to_loop = num_dollars*-1 else dollars_to_loop = num_dollars end + for i = 1, dollars_to_loop do + G.E_MANAGER:add_event(Event({ + trigger = 'before',delay = 0.18 - ((num_dollars > 20 and 0.13) or (num_dollars > 9 and 0.1) or 0), + func = function() + if i%30 == 1 then + G.round_eval:add_child( + {n=G.UIT.R, config={align = "cm", id = 'dollar_row_'..(dollar_row+1)..'_'..config.name}, nodes={}}, + G.round_eval:get_UIE_by_ID('dollar_'..config.name)) + dollar_row = dollar_row+1 + end + + local r + if i == 1 and num_dollars < 0 then + r = {n=G.UIT.T, config={text = '-', colour = G.C.RED, scale = ((num_dollars < -20 and 0.28) or (num_dollars < -9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}} + play_sound('coin3', 0.9+0.2*math.random(), 0.7 - (num_dollars < -20 and 0.2 or 0)) + else + if num_dollars < 0 then r = {n=G.UIT.T, config={text = localize('$'), colour = G.C.RED, scale = ((num_dollars > 20 and 0.28) or (num_dollars > 9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}} + else r = {n=G.UIT.T, config={text = localize('$'), colour = G.C.MONEY, scale = ((num_dollars > 20 and 0.28) or (num_dollars > 9 and 0.43) or 0.58), shadow = true, hover = true, can_collide = false, juice = true}} end + end + play_sound('coin3', 0.9+0.2*math.random(), 0.7 - (num_dollars > 20 and 0.2 or 0)) + + if config.name == 'blind1' then + G.GAME.current_round.dollars_to_be_earned = G.GAME.current_round.dollars_to_be_earned:sub(2) + end + + G.round_eval:add_child(r,G.round_eval:get_UIE_by_ID('dollar_row_'..(dollar_row)..'_'..config.name)) + G.VIBRATION = G.VIBRATION + 0.4 + return true + end + })) + end + end + else + delay(0.4) + G.E_MANAGER:add_event(Event({ + trigger = 'before',delay = 0.5, + func = function() + UIBox{ + definition = {n=G.UIT.ROOT, config={align = 'cm', colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={id = 'cash_out_button', align = "cm", padding = 0.1, minw = 7, r = 0.15, colour = G.GAME.current_round.semicolon and G.C.SET.Code or G.C.ORANGE, shadow = true, hover = true, one_press = true, button = 'cash_out', focus_args = {snap_to = true}}, nodes={ + {n=G.UIT.T, config={text = G.GAME.current_round.semicolon and localize('k_end_blind') or (localize('b_cash_out')..": "), scale = 1, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + {n=G.UIT.T, config={text = not G.GAME.current_round.semicolon and localize('$')..format_ui_value(config.dollars) or ';', scale = 1.2*scale, colour = G.C.WHITE, shadow = true, juice = true}} + }},}}, + config = { + align = 'tmi', + offset ={x=0,y=0.4}, + major = G.round_eval} + } + + --local left_text = {n=G.UIT.R, config={id = 'cash_out_button', align = "cm", padding = 0.1, minw = 2, r = 0.15, colour = G.C.ORANGE, shadow = true, hover = true, one_press = true, button = 'cash_out', focus_args = {snap_to = true}}, nodes={ + -- {n=G.UIT.T, config={text = localize('b_cash_out')..": ", scale = 1, colour = G.C.UI.TEXT_LIGHT, shadow = true}}, + -- {n=G.UIT.T, config={text = localize('$')..format_ui_value(config.dollars), scale = 1.3*scale, colour = G.C.WHITE, shadow = true, juice = true}} + --}} + --G.round_eval:add_child(left_text,G.round_eval:get_UIE_by_ID('eval_bottom')) + + Handy.insta_cash_out.is_button_created = true + G.GAME.current_round.dollars = config.dollars + + play_sound('coin6', config.pitch or 1) + G.VIBRATION = G.VIBRATION + 1 + return true + end + })) + end +end + +function change_shop_size(mod) + if not G.GAME.shop then return end + G.GAME.shop.joker_max = G.GAME.shop.joker_max + mod + if G.shop_jokers and G.shop_jokers.cards then + if mod < 0 then + --Remove jokers in shop + for i = #G.shop_jokers.cards, G.GAME.shop.joker_max+1, -1 do + if G.shop_jokers.cards[i] then + G.shop_jokers.cards[i]:remove() + end + end + end + G.shop_jokers.config.card_limit = G.GAME.shop.joker_max + G.shop_jokers.T.w = math.min(G.GAME.shop.joker_max,4)*1.02*G.CARD_W + G.shop:recalculate() + if mod > 0 then + for i = 1, G.GAME.shop.joker_max - #G.shop_jokers.cards do + G.shop_jokers:emplace(create_card_for_shop(G.shop_jokers)) + end + end + end +end + +function juice_card(card) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() if card and card.juice_up then card:juice_up(0.7) end;return true end) + })) +end + +function update_canvas_juice(dt) + G.JIGGLE_VIBRATION = G.ROOM.jiggle or 0 + if not G.SETTINGS.screenshake or (type(G.SETTINGS.screenshake) ~= 'number') then + G.SETTINGS.screenshake = G.SETTINGS.reduced_motion and 0 or 50 + end + local shake_amt = (G.SETTINGS.reduced_motion and 0 or 1)*math.max(0,G.SETTINGS.screenshake-30)/100 + G.ARGS.eased_cursor_pos = G.ARGS.eased_cursor_pos or {x=G.CURSOR.T.x,y=G.CURSOR.T.y, sx = G.CONTROLLER.cursor_position.x, sy = G.CONTROLLER.cursor_position.y} + G.ARGS.eased_cursor_pos.x = G.ARGS.eased_cursor_pos.x*(1-3*dt) + 3*dt*(shake_amt*G.CURSOR.T.x + (1-shake_amt)*G.ROOM.T.w/2) + G.ARGS.eased_cursor_pos.y = G.ARGS.eased_cursor_pos.y*(1-3*dt) + 3*dt*(shake_amt*G.CURSOR.T.y + (1-shake_amt)*G.ROOM.T.h/2) + G.ARGS.eased_cursor_pos.sx = G.ARGS.eased_cursor_pos.sx*(1-3*dt) + 3*dt*(shake_amt*G.CONTROLLER.cursor_position.x + (1-shake_amt)*G.WINDOWTRANS.real_window_w/2) + G.ARGS.eased_cursor_pos.sy = G.ARGS.eased_cursor_pos.sy*(1-3*dt) + 3*dt*(shake_amt*G.CONTROLLER.cursor_position.y + (1-shake_amt)*G.WINDOWTRANS.real_window_h/2) + + shake_amt = (G.SETTINGS.reduced_motion and 0 or 1)*G.SETTINGS.screenshake/100*3 + if shake_amt < 0.05 then shake_amt = 0 end + + G.ROOM.jiggle = (G.ROOM.jiggle or 0)*(1-5*dt)*(shake_amt > 0.05 and 1 or 0) + G.ROOM.T.r = (0.001*math.sin(0.3*G.TIMERS.REAL)+ 0.002*(G.ROOM.jiggle)*math.sin(39.913*G.TIMERS.REAL))*shake_amt + G.ROOM.T.x = G.ROOM_ORIG.x + (shake_amt)*(0.015*math.sin(0.913*G.TIMERS.REAL) + 0.01*(G.ROOM.jiggle*shake_amt)*math.sin(19.913*G.TIMERS.REAL) + (G.ARGS.eased_cursor_pos.x - 0.5*(G.ROOM.T.w + G.ROOM_ORIG.x))*0.01) + G.ROOM.T.y = G.ROOM_ORIG.y + (shake_amt)*(0.015*math.sin(0.952*G.TIMERS.REAL) + 0.01*(G.ROOM.jiggle*shake_amt)*math.sin(21.913*G.TIMERS.REAL) + (G.ARGS.eased_cursor_pos.y - 0.5*(G.ROOM.T.h + G.ROOM_ORIG.y))*0.01) + + G.JIGGLE_VIBRATION = G.JIGGLE_VIBRATION*(1-5*dt) + G.CURR_VIBRATION = G.CURR_VIBRATION or 0 + G.CURR_VIBRATION = math.min(1, G.CURR_VIBRATION + G.VIBRATION + G.JIGGLE_VIBRATION*0.2) + G.VIBRATION = 0 + G.CURR_VIBRATION = (1-15*dt)*G.CURR_VIBRATION + if not G.SETTINGS.rumble then G.CURR_VIBRATION = 0 end + if G.CONTROLLER.GAMEPAD.object and G.F_RUMBLE then G.CONTROLLER.GAMEPAD.object:setVibration(G.CURR_VIBRATION*0.4*G.F_RUMBLE, G.CURR_VIBRATION*0.4*G.F_RUMBLE) end +end + +function juice_card_until(card, eval_func, first, delay) + G.E_MANAGER:add_event(Event({ + trigger = 'after',delay = delay or 0.1, blocking = false, blockable = false, timer = 'REAL', + func = (function() if eval_func(card) then if card and card.juice_up then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end) + })) +end + +function check_for_unlock(args) + if not next(args) then return end + if false then return end + if args.type == 'win_challenge' then + unlock_achievement('rule_bender') + local _c = true + for k, v in pairs(G.CHALLENGES) do + if not G.PROFILES[G.SETTINGS.profile].challenge_progress.completed[v.id] then + _c = false + end + end + if _c then + unlock_achievement('rule_breaker') + end + end + if false then return end + fetch_achievements() -- Refreshes achievements + for k, v in pairs(G.ACHIEVEMENTS) do + if (not v.earned) and (v.unlock_condition and type(v.unlock_condition) == 'function') and v:unlock_condition(args) then + unlock_achievement(k) + end + end + + --|-------------------------------------------- + --|Achievements + --|-------------------------------------------- + if args.type == 'career_stat' then + if args.statname == 'c_cards_played' and G.PROFILES[G.SETTINGS.profile].career_stats[args.statname] >= 2500 then + unlock_achievement('card_player') + end + if args.statname == 'c_cards_discarded' and G.PROFILES[G.SETTINGS.profile].career_stats[args.statname] >= 2500 then + unlock_achievement('card_discarder') + end + end + if args.type == 'ante_up' then + if args.ante >= 4 then + unlock_achievement('ante_up') + end + if args.ante >= 8 then + unlock_achievement('ante_upper') + end + end + if args.type == 'win' then + unlock_achievement('heads_up') + if G.GAME.round <= 12 then + unlock_achievement('speedrunner') + end + if G.GAME.round_scores.times_rerolled.amt <= 0 then + unlock_achievement('you_get_what_you_get') + end + end + if args.type == 'win_stake' then + local highest_win, lowest_win = get_deck_win_stake(nil) + if highest_win >= G.P_STAKES["stake_red"].stake_level then + unlock_achievement('low_stakes') + end + if highest_win >= G.P_STAKES["stake_black"].stake_level then + unlock_achievement('mid_stakes') + end + if highest_win >= G.P_STAKES["stake_gold"].stake_level then + unlock_achievement('high_stakes') + end + if G.PROGRESS and G.PROGRESS.deck_stakes.tally/G.PROGRESS.deck_stakes.of >=1 then + unlock_achievement('completionist_plus') + end + if G.PROGRESS and G.PROGRESS.joker_stickers.tally/G.PROGRESS.joker_stickers.of >=1 then + unlock_achievement('completionist_plus_plus') + end + end + if args.type == 'money' then + if G.GAME.dollars >= 400 then + unlock_achievement('nest_egg') + end + end + if args.type == 'hand' then + if args.handname == 'Flush' and args.scoring_hand then + local _w = 0 + for k, v in ipairs(args.scoring_hand) do + if v.ability.name == 'Wild Card' then + _w = _w + 1 + end + end + if _w == #args.scoring_hand then + unlock_achievement('flushed') + end + end + + if args.disp_text == 'Royal Flush' then + unlock_achievement('royale') + end + end + if args.type == 'shatter' then + if #args.shattered >= 2 then + unlock_achievement('shattered') + end + end + if args.type == 'run_redeem' then + local _v = 0 + _v = _v - (G.GAME.starting_voucher_count or 0) + for k, v in pairs(G.GAME.used_vouchers) do + _v = _v + 1 + end + if _v >= 5 and G.GAME.round_resets.ante <= 4 then + unlock_achievement('roi') + end + end + if args.type == 'upgrade_hand' then + if args.level >= 10 then + unlock_achievement('retrograde') + end + end + if args.type == 'chip_score' then + if to_big(args.chips) >= to_big(10000) then + unlock_achievement('_10k') + end + if to_big(args.chips) >= to_big(1000000) then + unlock_achievement('_1000k') + end + if to_big(args.chips) >= to_big(100000000) then + unlock_achievement('_100000k') + end + end + if args.type == 'modify_deck' then + if G.deck and G.deck.config.card_limit <= 20 then + unlock_achievement('tiny_hands') + end + if G.deck and G.deck.config.card_limit >= 80 then + unlock_achievement('big_hands') + end + end + if args.type == 'spawn_legendary' then + unlock_achievement('legendary') + end + if args.type == 'discover_amount' then + if G.DISCOVER_TALLIES.vouchers.tally/G.DISCOVER_TALLIES.vouchers.of >=1 then + unlock_achievement('extreme_couponer') + end + if G.DISCOVER_TALLIES.spectrals.tally/G.DISCOVER_TALLIES.spectrals.of >=1 then + unlock_achievement('clairvoyance') + end + if G.DISCOVER_TALLIES.tarots.tally/G.DISCOVER_TALLIES.tarots.of >=1 then + unlock_achievement('cartomancy') + end + if G.DISCOVER_TALLIES.planets.tally/G.DISCOVER_TALLIES.planets.of >=1 then + unlock_achievement('astronomy') + end + if G.DISCOVER_TALLIES.total.tally/G.DISCOVER_TALLIES.total.of >=1 then + unlock_achievement('completionist') + end + end + --------------------------------------------- + + local i=1 + while i <= #G.P_LOCKED do + local ret = false + local card = G.P_LOCKED[i] + + local custom_check + if not card.unlocked and card.check_for_unlock and type(card.check_for_unlock) == 'function' then + ret = card:check_for_unlock(args) + if ret then unlock_card(card) end + custom_check = true + end if not custom_check and not card.unlocked and card.unlock_condition and args.type == 'career_stat' then + if args.statname == card.unlock_condition.type and G.PROFILES[G.SETTINGS.profile].career_stats[args.statname] >= card.unlock_condition.extra then + ret = true + unlock_card(card) + end + end + + if not custom_check and not card.unlocked and card.unlock_condition and card.unlock_condition.type == args.type then + if args.type == 'hand' and args.handname == card.unlock_condition.extra then + ret = true + unlock_card(card) + end + if args.type == 'min_hand_size' and G.hand and G.hand.config.card_limit <= card.unlock_condition.extra then + ret = true + unlock_card(card) + end + if args.type == 'interest_streak' and card.unlock_condition.extra <= G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak then + ret = true + unlock_card(card) + end + if args.type == 'run_card_replays' then + for k, v in ipairs(G.playing_cards) do + if v.base.times_played >= card.unlock_condition.extra then + ret = true + unlock_card(card) + break + end + end + end + if args.type == 'play_all_hearts' then + local played = true + for k, v in ipairs(G.deck.cards) do + if (v.ability.name ~= 'Stone Card' and not v.config.center.no_suit) and v.base.suit == 'Hearts' then + played = false + end + end + for k, v in ipairs(G.hand.cards) do + if (v.ability.name ~= 'Stone Card' and not v.config.center.no_suit) and v.base.suit == 'Hearts' then + played = false + end + end + if played then + ret = true + unlock_card(card) + end + end + if args.type == 'run_redeem' then + local vouchers_redeemed = 0 + for k, v in pairs(G.GAME.used_vouchers) do + vouchers_redeemed = vouchers_redeemed + 1 + end + if vouchers_redeemed >= card.unlock_condition.extra then + ret = true + unlock_card(card) + end + end + if args.type == 'have_edition' then + local shiny_jokers = 0 + for k, v in ipairs(G.jokers.cards) do + if v.edition then shiny_jokers = shiny_jokers + 1 end + end + if shiny_jokers >= card.unlock_condition.extra then + ret = true + unlock_card(card) + end + end + if args.type == 'double_gold' then + ret = true + unlock_card(card) + end + if args.type == 'continue_game' then + ret = true + unlock_card(card) + end + if args.type == 'blank_redeems' then + if G.PROFILES[G.SETTINGS.profile].voucher_usage['v_blank'] and G.PROFILES[G.SETTINGS.profile].voucher_usage['v_blank'].count >= card.unlock_condition.extra then + unlock_card(card) + end + end + if args.type == 'modify_deck' then + if card.unlock_condition.extra and card.unlock_condition.extra.suit then + local count = 0 + for _, v in pairs(G.playing_cards) do + if v.base.suit == card.unlock_condition.extra.suit then count = count + 1 end + end + if count >= card.unlock_condition.extra.count then + ret = true + unlock_card(card) + end + end + if card.unlock_condition.extra and card.unlock_condition.extra.enhancement then + local count = 0 + for _, v in pairs(G.playing_cards) do + if v.ability.name == card.unlock_condition.extra.enhancement then count = count + 1 end + end + if count >= card.unlock_condition.extra.count then + ret = true + unlock_card(card) + end + end + if card.unlock_condition.extra and card.unlock_condition.extra.tally then + local count = 0 + for _, v in pairs(G.playing_cards) do + if v.ability.set == 'Enhanced' then count = count + 1 end + end + if count >= card.unlock_condition.extra.count then + ret = true + unlock_card(card) + end + end + end + if args.type == 'discover_amount' then + if card.unlock_condition.amount then + if card.unlock_condition.amount <= args.amount then + ret = true + unlock_card(card) + end + end + if card.unlock_condition.tarot_count then + if #G.P_CENTER_POOLS.Tarot <= args.tarot_count then + ret = true + unlock_card(card) + end + end + if card.unlock_condition.planet_count then + if #G.P_CENTER_POOLS.Planet <= args.planet_count then + ret = true + unlock_card(card) + end + end + end + if args.type == 'win_deck' then + if card.unlock_condition.deck then + if get_deck_win_stake(card.unlock_condition.deck) > 0 then + ret = true + unlock_card(card) + end + end + end + if args.type == 'win_stake' then + if card.unlock_condition.stake then + if get_deck_win_stake() >= card.unlock_condition.stake then + ret = true + unlock_card(card) + end + end + end + if args.type == 'discover_planets' then + local count = 0 + for k, v in pairs(G.P_CENTERS) do + if v.set == 'Planet' and v.discovered then count = count + 1 end + end + if count >= 9 then + ret = true + unlock_card(card) + end + end + if args.type == 'blind_discoveries' then + local discovered_blinds = 0 + for k, v in pairs(G.P_BLINDS) do + if v.discovered then + discovered_blinds = discovered_blinds + 1 + end + end + if discovered_blinds >= card.unlock_condition.extra then + ret = true + unlock_card(card) + end + end + if args.type == 'modify_jokers' and G.jokers then + if card.unlock_condition.extra.count then + local count = 0 + for _, v in pairs(G.jokers.cards) do + if v.ability.set == 'Joker' and v.edition and v.edition.polychrome and card.unlock_condition.extra.polychrome then count = count + 1 end + end + if count >= card.unlock_condition.extra.count then + ret = true + unlock_card(card) + end + end + end + if args.type == 'money' then + if card.unlock_condition.extra <= G.GAME.dollars then + ret = true + unlock_card(card) + end + end + if args.type == 'round_win' then + if card.name == 'Matador' then + if G.GAME.current_round.hands_played == 1 and + G.GAME.current_round.discards_left == G.GAME.round_resets.discards and + G.GAME.blind:get_type() == 'Boss' then + ret = true + unlock_card(card) + end + end + if card.name == 'Troubadour' then + if G.PROFILES[G.SETTINGS.profile].career_stats.c_single_hand_round_streak >= card.unlock_condition.extra then + ret = true + unlock_card(card) + end + end + if card.name == 'Hanging Chad' then + if G.GAME.last_hand_played == card.unlock_condition.extra and G.GAME.blind:get_type() == 'Boss' then + ret = true + unlock_card(card) + end + end + end + if args.type == 'ante_up' then + if card.unlock_condition.ante then + if args.ante == card.unlock_condition.ante then + ret = true + unlock_card(card) + end + end + end + if args.type == 'hand_contents' then + if card.name == 'Seeing Double' then + local tally = 0 + for j = 1, #args.cards do + if args.cards[j]:get_id() == 7 and args.cards[j]:is_suit('Clubs') then + tally = tally+1 + end + end + if tally >= 4 then + ret = true + unlock_card(card) + end + end + + if card.name == 'Golden Ticket' then + local tally = 0 + for j = 1, #args.cards do + if args.cards[j].ability.name == 'Gold Card' then + tally = tally+1 + end + end + if tally >= 5 then + ret = true + unlock_card(card) + end + end + end + if args.type == 'discard_custom' then + if card.name == 'Hit the Road' then + local tally = 0 + for j = 1, #args.cards do + if args.cards[j]:get_id() == 11 then + tally = tally+1 + end + end + if tally >= 5 then + ret = true + unlock_card(card) + end + end + if card.name == 'Brainstorm' then + local eval = evaluate_poker_hand(args.cards) + if next(eval['Straight Flush']) then + local min = 10 + for j = 1, #args.cards do + if args.cards[j]:get_id() < min then min = args.cards[j]:get_id() end + end + if min == 10 then + ret = true + unlock_card(card) + end + end + end + end + if args.type == 'win_no_hand' and G.GAME.hands[card.unlock_condition.extra].played == 0 then + ret = true + unlock_card(card) + end + if args.type == 'win_custom' then + if card.name == 'Invisible Joker' and + G.GAME.max_jokers <= 4 then + ret = true + unlock_card(card) + end + if card.name == 'Blueprint' then + ret = true + unlock_card(card) + end + end + if args.type == 'win' then + if card.unlock_condition.n_rounds >= G.GAME.round then + ret = true + unlock_card(card) + end + end + if args.type == 'chip_score' then + if to_big(card.unlock_condition.chips) <= to_big(args.chips) then + ret = true + G.E_MANAGER:add_event(Event({ + func = function() + G.E_MANAGER:add_event(Event({ + func = function() + unlock_card(card) + return true end + })) + return true end + })) + end + end + end + if ret == true then + table.remove(G.P_LOCKED, i) + else + i = i + 1 + end + end +end + +function unlock_card(card) + if card.unlocked == false then + if not SMODS.seeded_unlocks and (G.GAME.seeded or G.GAME.challenge) then return end + if card.unlocked or card.wip then return end + G:save_notify(card) + card.unlocked = true + if card.set == 'Back' then discover_card(card) end + table.sort(G.P_CENTER_POOLS["Back"], function (a, b) return (a.order - (a.unlocked and 100 or 0)) < (b.order - (b.unlocked and 100 or 0)) end) + G:save_progress() + G.FILE_HANDLER.force = true + notify_alert(card.key, card.set) + end +end + +function fetch_achievements() + G.ACHIEVEMENTS = G.ACHIEVEMENTS or { + ante_up = {order = 1, tier = 3, earned = false, steamid = "BAL_01"}, + ante_upper = {order = 2, tier = 3, earned = false, steamid = "BAL_02"}, + heads_up = {order = 3, tier = 2, earned = false, steamid = "BAL_03"}, + low_stakes = {order = 4, tier = 2, earned = false, steamid = "BAL_04"}, + mid_stakes = {order = 5, tier = 2, earned = false, steamid = "BAL_05"}, + high_stakes = {order = 6, tier = 2, earned = false, steamid = "BAL_06"}, + card_player = {order = 7, tier = 3, earned = false, steamid = "BAL_07"}, + card_discarder = {order = 8, tier = 3, earned = false, steamid = "BAL_08"}, + nest_egg = {order = 9, tier = 2, earned = false, steamid = "BAL_09"}, + flushed = {order = 10, tier = 3, earned = false, steamid = "BAL_10"}, + speedrunner = {order = 11, tier = 2, earned = false, steamid = "BAL_11"}, + roi = {order = 12, tier = 3, earned = false, steamid = "BAL_12"}, + shattered = {order = 13, tier = 3, earned = false, steamid = "BAL_13"}, + royale = {order = 14, tier = 3, earned = false, steamid = "BAL_14"}, + retrograde = {order = 15, tier = 2, earned = false, steamid = "BAL_15"}, + _10k = {order = 16, tier = 3, earned = false, steamid = "BAL_16"}, + _1000k = {order = 17, tier = 2, earned = false, steamid = "BAL_17"}, + _100000k = {order = 18, tier = 1, earned = false, steamid = "BAL_18"}, + tiny_hands = {order = 19, tier = 2, earned = false, steamid = "BAL_19"}, + big_hands = {order = 20, tier = 2, earned = false, steamid = "BAL_20"}, + you_get_what_you_get = {order = 21, tier = 3, earned = false, steamid = "BAL_21"}, + rule_bender = {order = 22, tier = 3, earned = false, steamid = "BAL_22"}, + rule_breaker = {order = 23, tier = 1, earned = false, steamid = "BAL_23"}, + legendary = {order = 24, tier = 3, earned = false, steamid = "BAL_24"}, + astronomy = {order = 25, tier = 3, earned = false, steamid = "BAL_25"}, + cartomancy = {order = 26, tier = 3, earned = false, steamid = "BAL_26"}, + clairvoyance = {order = 27, tier = 2, earned = false, steamid = "BAL_27"}, + extreme_couponer = {order = 28, tier = 1, earned = false, steamid = "BAL_28"}, + completionist = {order = 29, tier = 1, earned = false, steamid = "BAL_29"}, + completionist_plus = {order = 30, tier = 1, earned = false, steamid = "BAL_30"}, + completionist_plus_plus={order = 31, tier = 1, earned = false, steamid = "BAL_31"}, + } + + G.SETTINGS.ACHIEVEMENTS_EARNED = G.SETTINGS.ACHIEVEMENTS_EARNED or {} + for k, v in pairs(G.ACHIEVEMENTS) do + if not v.key then v.key = k end + for kk, vv in pairs(G.SETTINGS.ACHIEVEMENTS_EARNED) do + if G.ACHIEVEMENTS[kk] and G.ACHIEVEMENTS[kk].mod then + G.ACHIEVEMENTS[kk].earned = true + end + end + end if G.F_NO_ACHIEVEMENTS then return end + + --|FROM LOCAL SETTINGS FILE + --|------------------------------------------------------- + if not G.STEAM then --|set this to false if you get this information from elsewhere + G.SETTINGS.ACHIEVEMENTS_EARNED = G.SETTINGS.ACHIEVEMENTS_EARNED or {} + for k, v in pairs(G.SETTINGS.ACHIEVEMENTS_EARNED) do + if G.ACHIEVEMENTS[k] then + G.ACHIEVEMENTS[k].earned = true + end + end + end + --|------------------------------------------------------- + + --|STEAM ACHIEVEMENTS + --|------------------------------------------------------- + if G.STEAM and not G.STEAM.initial_fetch then + for k, v in pairs(G.ACHIEVEMENTS) do + local achievement_name = v.steamid + local success, achieved = G.STEAM.userStats.getAchievement(achievement_name) + if success then + v.earned = not not achieved + end + end + G.STEAM.initial_fetch = true + end + --|------------------------------------------------------- + + --|Other platforms + --|------------------------------------------------------- + + --|------------------------------------------------------- +end + +function unlock_achievement(achievement_name) + if G.PROFILES[G.SETTINGS.profile].all_unlocked and (G.ACHIEVEMENTS and G.ACHIEVEMENTS[achievement_name] and not G.ACHIEVEMENTS[achievement_name].bypass_all_unlocked and SMODS.config.achievements < 3) or (SMODS.config.achievements < 3 and (G.GAME.seeded or G.GAME.challenge)) then return end + G.E_MANAGER:add_event(Event({ + no_delete = true, + blockable = false, + blocking = false, + func = function() + if G.STATE ~= G.STATES.HAND_PLAYED then + if G.PROFILES[G.SETTINGS.profile].all_unlocked and (G.ACHIEVEMENTS and G.ACHIEVEMENTS[achievement_name] and not G.ACHIEVEMENTS[achievement_name].bypass_all_unlocked and SMODS.config.achievements < 3) or (SMODS.config.achievements < 3 and (G.GAME.seeded or G.GAME.challenge)) then return end + local achievement_set = false + if G.F_NO_ACHIEVEMENTS and not (G.ACHIEVEMENTS[achievement_name] or {}).mod then return true end + + --|LOCAL SETTINGS FILE + --|------------------------------------------------------- + if not G.ACHIEVEMENTS then fetch_achievements() end + + G.SETTINGS.ACHIEVEMENTS_EARNED[achievement_name] = true + G:save_progress() + if G.ACHIEVEMENTS[achievement_name] and not G.STEAM then + if not G.ACHIEVEMENTS[achievement_name].earned then + --|THIS IS THE FIRST TIME THIS ACHIEVEMENT HAS BEEN EARNED + achievement_set = true + G.FILE_HANDLER.force = true + end + G.ACHIEVEMENTS[achievement_name].earned = true + end + --|------------------------------------------------------- + + + --|STEAM ACHIEVEMENTS + --|------------------------------------------------------- + if G.STEAM then + if G.ACHIEVEMENTS[achievement_name] then + if not G.ACHIEVEMENTS[achievement_name].earned then + --|THIS IS THE FIRST TIME THIS ACHIEVEMENT HAS BEEN EARNED + achievement_set = true + G.FILE_HANDLER.force = true + local achievement_code = G.ACHIEVEMENTS[achievement_name].steamid + local success, achieved = G.STEAM.userStats.getAchievement(achievement_code) + if not success or not achieved then + G.STEAM.send_control.update_queued = true + G.STEAM.userStats.setAchievement(achievement_code) + end + end + G.ACHIEVEMENTS[achievement_name].earned = true + end + end + --|------------------------------------------------------- + + --|Other platforms + --|------------------------------------------------------- + + --|------------------------------------------------------- + + if achievement_set then notify_alert(achievement_name) end + return true + end + end + }), 'achievement') +end + +function notify_alert(_achievement, _type) + _type = _type or 'achievement' + G.E_MANAGER:add_event(Event({ + no_delete = true, + pause_force = true, + timer = 'UPTIME', + func = function() + if G.achievement_notification then + G.achievement_notification:remove() + G.achievement_notification = nil + end + G.achievement_notification = G.achievement_notification or UIBox{ + definition = create_UIBox_notify_alert(_achievement, _type), + config = {align='cr', offset = {x=20,y=0},major = G.ROOM_ATTACH, bond = 'Weak'} + } + return true + end + }), 'achievement') + G.E_MANAGER:add_event(Event({ + no_delete = true, + trigger = 'after', + pause_force = true, + timer = 'UPTIME', + delay = 0.1, + func = function() + G.achievement_notification.alignment.offset.x = G.ROOM.T.x - G.achievement_notification.UIRoot.children[1].children[1].T.w - 0.8 + return true + end + }), 'achievement') + G.E_MANAGER:add_event(Event({ + no_delete = true, + pause_force = true, + trigger = 'after', + timer = 'UPTIME', + delay = 0.1, + func = function() + play_sound('highlight1', nil, 0.5) + play_sound('foil2', 0.5, 0.4) + return true + end + }), 'achievement') + G.E_MANAGER:add_event(Event({ + no_delete = true, + pause_force = true, + trigger = 'after', + delay = 3, + timer = 'UPTIME', + func = function() + G.achievement_notification.alignment.offset.x = 20 + return true + end + }), 'achievement') + G.E_MANAGER:add_event(Event({ + no_delete = true, + pause_force = true, + trigger = 'after', + delay = 0.5, + timer = 'UPTIME', + func = function() + if G.achievement_notification then + G.achievement_notification:remove() + G.achievement_notification = nil + end + return true + end + }), 'achievement') +end + +function inc_steam_stat(stat_name) + if not G.STEAM then return end + local success, current_stat = G.STEAM.userStats.getStatInt(stat_name) + if success then + G.STEAM.userStats.setStatInt(stat_name, current_stat+1) + G.STEAM.send_control.update_queued = true + end +end + +function unlock_notify() + local _UN = get_compressed(G.SETTINGS.profile..'/'..'unlock_notify.jkr') + if _UN then + for key in string.gmatch(_UN .. "\n", "(.-)\n") do + create_unlock_overlay(key) + end + love.filesystem.remove(G.SETTINGS.profile..'/'..'unlock_notify.jkr') + end +end + +function create_unlock_overlay(key) + if G.P_CENTERS[key] then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + no_delete = true, + func = (function() + if not G.OVERLAY_MENU then + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = G.P_CENTERS[key].set == 'Back' and create_UIBox_deck_unlock(G.P_CENTERS[key]) or create_UIBox_card_unlock(G.P_CENTERS[key]), + } + play_sound('foil1', 0.7, 0.3) + play_sound('gong', 1.4, 0.15) + return true + end + end) + }), 'unlock') + end +end + +function discover_card(card) + if not SMODS.seeded_unlocks and (G.GAME.seeded or G.GAME.challenge) then return end + card = card or {} + if card.discovered or card.wip then return end + if card and not card.discovered then + card.alert = true + G.GAME.round_scores.new_collection.amt = G.GAME.round_scores.new_collection.amt+1 + end + card.discovered = true + set_discover_tallies() + G.E_MANAGER:add_event(Event({ + func = (function() + G:save_progress() + return true end)})) +end + +function get_deck_from_name(_name) + for k, v in pairs(G.P_CENTERS) do + if v.name == _name then return v end + end +end + +function get_next_voucher_key(_from_tag) + local _pool, _pool_key = get_current_pool('Voucher') + if _from_tag then _pool_key = 'Voucher_fromtag' end + local center = pseudorandom_element(_pool, pseudoseed(_pool_key)) + local it = 1 + while center == 'UNAVAILABLE' do + it = it + 1 + center = pseudorandom_element(_pool, pseudoseed(_pool_key..'_resample'..it)) + end + + return center +end + +function get_next_tag_key(append) + if G.FORCE_TAG then return G.FORCE_TAG end + local _pool, _pool_key = get_current_pool('Tag', nil, nil, append) + local _tag = pseudorandom_element(_pool, pseudoseed(_pool_key)) + local it = 1 + while _tag == 'UNAVAILABLE' do + it = it + 1 + _tag = pseudorandom_element(_pool, pseudoseed(_pool_key..'_resample'..it)) + end + + return _tag +end + +function create_playing_card(card_init, area, skip_materialize, silent, colours) + card_init = card_init or {} + card_init.front = card_init.front or pseudorandom_element(G.P_CARDS, pseudoseed('front')) + card_init.center = card_init.center or G.P_CENTERS.c_base + + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local _area = area or G.hand + local card = Card(_area.T.x, _area.T.y, G.CARD_W, G.CARD_H, card_init.front, card_init.center, {playing_card = G.playing_card}) + table.insert(G.playing_cards, card) + card.playing_card = G.playing_card + if G.GAME.modifiers.cry_force_suit then card:change_suit(G.GAME.modifiers.cry_force_suit) end + if G.GAME.modifiers.cry_force_enhancement then card:set_ability(G.P_CENTERS[G.GAME.modifiers.cry_force_enhancement]) end + if G.GAME.modifiers.cry_force_edition then card:set_edition({[G.GAME.modifiers.cry_force_edition]=true},true,true) end + if G.GAME.modifiers.cry_force_seal then card:set_seal(G.GAME.modifiers.cry_force_seal) end + + if area then area:emplace(card) end + if not skip_materialize then card:start_materialize(colours, silent) end + + return card +end + +function get_pack(_key, _type) + if not G.GAME.first_shop_buffoon and not G.GAME.banned_keys['p_buffoon_normal_1'] then + G.GAME.first_shop_buffoon = true + return G.P_CENTERS['p_buffoon_normal_'..(math.random(1, 2))] + end + local cume, it, center = 0, 0, nil + for k, v in ipairs(G.P_CENTER_POOLS['Booster']) do + if (not _type or _type == v.kind) and not G.GAME.banned_keys[v.key] then cume = cume + (v.weight or 1 ) end + end + local poll = pseudorandom(pseudoseed((_key or 'pack_generic')..G.GAME.round_resets.ante))*cume + for k, v in ipairs(G.P_CENTER_POOLS['Booster']) do + if not G.GAME.banned_keys[v.key] then + if not _type or _type == v.kind then it = it + (v.weight or 1) end + if it >= poll and it - (v.weight or 1) <= poll then center = v; break end + end + end + if not center then center = G.P_CENTERS['p_buffoon_normal_1'] end return center +end + +function get_current_pool(_type, _rarity, _legendary, _append) + --create the pool + G.ARGS.TEMP_POOL = EMPTY(G.ARGS.TEMP_POOL) + local _pool, _starting_pool, _pool_key, _pool_size = G.ARGS.TEMP_POOL, nil, '', 0 + + if _type == 'Joker' then +_rarity = (_legendary and 4) or (type(_rarity) == "number" and ((_rarity > 0.95 and 3) or (_rarity > 0.7 and 2) or 1)) or _rarity +local rarity = _rarity or SMODS.poll_rarity("Joker", 'rarity'..G.GAME.round_resets.ante..(_append or '')) + + _starting_pool, _pool_key = G.P_JOKER_RARITY_POOLS[rarity], 'Joker'..rarity..((not _legendary and _append) or '') + elseif SMODS.ObjectTypes[_type] and SMODS.ObjectTypes[_type].rarities then + local rarities = SMODS.ObjectTypes[_type].rarities + local rarity + if _legendary and rarities.legendary then + rarity = rarities.legendary.key + else + rarity = _rarity or SMODS.poll_rarity(_type, 'rarity_'.._type..G.GAME.round_resets.ante..(_append or '')) + end + _starting_pool, _pool_key = SMODS.ObjectTypes[_type].rarity_pools[rarity], _type..rarity..(_append or '') + else _starting_pool, _pool_key = G.P_CENTER_POOLS[_type], _type..(_append or '') + end + + --cull the pool + for k, v in ipairs(_starting_pool) do + local add = nil + local in_pool, pool_opts + if v.in_pool and type(v.in_pool) == 'function' then + in_pool, pool_opts = v:in_pool({ source = _append }) + end + pool_opts = pool_opts or {} + if _type == 'Enhanced' then + add = true + elseif _type == 'Demo' then + if v.pos and v.config then add = true end + elseif _type == 'Tag' then + if (not v.requires or (G.P_CENTERS[v.requires] and G.P_CENTERS[v.requires].discovered)) and + (not v.min_ante or v.min_ante <= G.GAME.round_resets.ante) then + add = true + end + elseif not (G.GAME.used_jokers[v.key] and not pool_opts.allow_duplicates and not next(find_joker("Showman"))) and + (v.unlocked ~= false or v.rarity == 4) then + if v.set == 'Voucher' then + if not G.GAME.cry_owned_vouchers[v.key] then + local include = true + if v.requires then + for kk, vv in pairs(v.requires) do + if not G.GAME.cry_owned_vouchers[vv] then + include = false + end + end + end + if G.shop_vouchers and G.shop_vouchers.cards then + for kk, vv in ipairs(G.shop_vouchers.cards) do + if vv.config.center.key == v.key then include = false end + end + end + if include then + add = true + end + end + elseif v.set == 'Planet' then + local softlocked = true + if not v.config.softlock then + softlocked = false + elseif v.config.hand_type then + softlocked = G.GAME.hands[v.config.hand_type].played == 0 + elseif v.config.hand_types then + for _, h in pairs(v.config.hand_types) do + if G.GAME.hands[h].played > 0 then + softlocked = false + end + end + end + if not softlocked then + add = true + end + elseif v.source_gate then + if v.source_gate ~= _append then + add = nil + else + add = true + end + elseif v.joker_gate then + add = nil + for kk, vv in pairs(G.jokers.cards) do + if vv.ability.name == v.joker_gate then + add = true + end + end + elseif v.enhancement_gate then + add = nil + for kk, vv in pairs(G.playing_cards) do + if vv.config.center.key == v.enhancement_gate then + add = true + end + end + else + add = true + end + if v.name == 'Black Hole' or v.name == 'The Soul' or v.hidden then + add = false + end + end + + if v.no_pool_flag and G.GAME.pool_flags[v.no_pool_flag] then add = nil end + if v.yes_pool_flag and not G.GAME.pool_flags[v.yes_pool_flag] then add = nil end + + if v.in_pool and type(v.in_pool) == 'function' then + add = in_pool and (add or pool_opts.override_base_checks) + end + if add and not G.GAME.banned_keys[v.key] then + _pool[#_pool + 1] = v.key + _pool_size = _pool_size + 1 + else + _pool[#_pool + 1] = 'UNAVAILABLE' + end + end + + --if pool is empty + if G.GAME.oldbpfactor and G.GAME.oldbpfactor >= 2 then + if _type == 'Joker' and (_rarity == nil or type(_rarity) ~= "string") and not _legendary and not (G.GAME.used_jokers["j_blueprint"] and not next(find_joker("Showman"))) then + for i = 1, math.floor(G.GAME.oldbpfactor - 1) do + _pool[#_pool + 1] = "j_blueprint" + end + end + end + if _pool_size == 0 then + _pool = EMPTY(G.ARGS.TEMP_POOL) + if SMODS.ObjectTypes[_type] and SMODS.ObjectTypes[_type].default and G.P_CENTERS[SMODS.ObjectTypes[_type].default] then + _pool[#_pool+1] = SMODS.ObjectTypes[_type].default + elseif _type == 'Tarot' or _type == 'Tarot_Planet' then _pool[#_pool + 1] = "c_strength" + elseif _type == 'Planet' then _pool[#_pool + 1] = "c_pluto" + elseif _type == 'Spectral' then _pool[#_pool + 1] = "c_incantation" + elseif _type == 'Joker' then _pool[#_pool + 1] = "j_joker" + elseif _type == 'Demo' then _pool[#_pool + 1] = "j_joker" + elseif _type == 'Voucher' then _pool[#_pool + 1] = "v_blank" + elseif _type == 'Tag' then _pool[#_pool + 1] = "tag_handy" + elseif _type == 'Consumeables' then _pool[#_pool + 1] = "c_ceres" + else _pool[#_pool + 1] = "j_joker" + end + end + + return _pool, _pool_key..(not _legendary and G.GAME.round_resets.ante or '') +end + +function poll_edition(_key, _mod, _no_neg, _guaranteed) + _mod = _mod or 1 + local edition_poll = pseudorandom(pseudoseed(_key or 'edition_generic')) + if _guaranteed then + if edition_poll > 1 - 0.003*25 and not _no_neg then + return {negative = true} + elseif edition_poll > 1 - 0.006*25 then + return {polychrome = true} + elseif edition_poll > 1 - 0.02*25 then + return {holo = true} + elseif edition_poll > 1 - 0.04*25 then + return {foil = true} + end + else + if edition_poll > 1 - 0.003*_mod and not _no_neg then + return {negative = true} + elseif edition_poll > 1 - 0.006*G.GAME.edition_rate*_mod then + return {polychrome = true} + elseif edition_poll > 1 - 0.02*G.GAME.edition_rate*_mod then + return {holo = true} + elseif edition_poll > 1 - 0.04*G.GAME.edition_rate*_mod then + return {foil = true} + end + end + return nil +end + +function create_card(_type, area, legendary, _rarity, skip_materialize, soulable, forced_key, key_append) + local area = area or G.jokers + local center = G.P_CENTERS.b_red + + + --should pool be skipped with a forced key + if not forced_key and soulable and (not G.GAME.banned_keys['c_soul']) then + for _, v in ipairs(SMODS.Consumable.legendaries) do + if (_type == v.type.key or _type == v.soul_set) and not (G.GAME.used_jokers[v.key] and not next(find_joker("Showman")) and not v.can_repeat_soul) and (not v.in_pool or (type(v.in_pool) ~= "function") or v:in_pool()) then + if pseudorandom('soul_'..v.key.._type..G.GAME.round_resets.ante) > (1 - v.soul_rate) then + forced_key = v.key + end + end + end + if (_type == 'Tarot' or _type == 'Spectral' or _type == 'Tarot_Planet') and + not (G.GAME.used_jokers['c_soul'] and not next(find_joker("Showman"))) then + if pseudorandom('soul_'.._type..G.GAME.round_resets.ante) > 0.997 then + forced_key = 'c_soul' + end + end + if (_type == 'Planet' or _type == 'Spectral') and + not (G.GAME.used_jokers['c_black_hole'] and not next(find_joker("Showman"))) then + if pseudorandom('soul_'.._type..G.GAME.round_resets.ante) > 0.997 then + forced_key = 'c_black_hole' + end + end + end + + if _type == 'Base' then + forced_key = 'c_base' + end + + + + if forced_key and not G.GAME.banned_keys[forced_key] then + center = G.P_CENTERS[forced_key] + _type = (center.set ~= 'Default' and center.set or _type) + else + local _pool, _pool_key = get_current_pool(_type, _rarity, legendary, key_append) + center = pseudorandom_element(_pool, pseudoseed(_pool_key)) + local it = 1 + while center == 'UNAVAILABLE' do + it = it + 1 + center = pseudorandom_element(_pool, pseudoseed(_pool_key..'_resample'..it)) + end + + center = G.P_CENTERS[center] + end + + local front = ((_type=='Base' or _type == 'Enhanced') and pseudorandom_element(G.P_CARDS, pseudoseed('front'..(key_append or '')..G.GAME.round_resets.ante))) or nil + + local card = Card(area.T.x + area.T.w/2, area.T.y, G.CARD_W, G.CARD_H, front, center, + {bypass_discovery_center = area==G.shop_jokers or area == G.pack_cards or area == G.shop_vouchers or (G.shop_demo and area==G.shop_demo) or area==G.jokers or area==G.consumeables, + bypass_discovery_ui = area==G.shop_jokers or area == G.pack_cards or area==G.shop_vouchers or (G.shop_demo and area==G.shop_demo), + discover = area==G.jokers or area==G.consumeables, + bypass_back = G.GAME.selected_back.pos}) + if card.ability.consumeable and not skip_materialize then card:start_materialize() end + for k, v in ipairs(SMODS.Sticker.obj_buffer) do + local sticker = SMODS.Stickers[v] + if sticker.should_apply and type(sticker.should_apply) == 'function' and sticker:should_apply(card, center, area) then + sticker:apply(card, true) + end + end + + if _type == 'Joker' then + if G.GAME.modifiers.all_eternal then + card:set_eternal(true) + end + if (area == G.shop_jokers) or (area == G.pack_cards) then + local eternal_perishable_poll = pseudorandom((area == G.pack_cards and 'packetper' or 'etperpoll')..G.GAME.round_resets.ante) + if G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.7 and not SMODS.Stickers["eternal"].should_apply then + card:set_eternal(true) + elseif G.GAME.modifiers.enable_perishables_in_shop and ((eternal_perishable_poll > 0.4) and (eternal_perishable_poll <= 0.7)) and not SMODS.Stickers["perishable"].should_apply then + card:set_perishable(true) + end + if G.GAME.modifiers.enable_rentals_in_shop and pseudorandom((area == G.pack_cards and 'packssjr' or 'ssjr')..G.GAME.round_resets.ante) > 0.7 and not SMODS.Stickers["rental"].should_apply then + card:set_rental(true) + end + end + + if not SMODS.bypass_create_card_edition then + local edition = poll_edition('edi'..(key_append or '')..G.GAME.round_resets.ante) + card:set_edition(edition) + check_for_unlock({type = 'have_edition'}) + end + end + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({cry_creating_card = true, card = card}) + end + return card +end + +function copy_card(other, new_card, card_scale, playing_card, strip_edition) + local new_card = new_card or Card(other.T.x, other.T.y, G.CARD_W*(card_scale or 1), G.CARD_H*(card_scale or 1), G.P_CARDS.empty, G.P_CENTERS.c_base, {playing_card = playing_card}) + new_card:set_ability(other.config.center) + new_card.ability.type = other.ability.type + new_card:set_base(other.config.card) + for k, v in pairs(other.ability) do + if type(v) == 'table' then + new_card.ability[k] = copy_table(v) + else + new_card.ability[k] = v + end + end + + new_card.checkmonster = true + if not strip_edition then + new_card.from_copy = true + new_card:set_edition(other.edition or {}, nil, true) + end + check_for_unlock({type = 'have_edition'}) + new_card:set_seal(other.seal, true) + if other.params then + new_card.params = other.params + new_card.params.playing_card = playing_card + end + new_card.debuff = other.debuff + new_card.pinned = other.pinned + return new_card +end + +function tutorial_info(args) + local overlay_colour = {0.32,0.36,0.41,0} + ease_value(overlay_colour, 4, 0.6, nil, 'REAL', true,0.4) + G.OVERLAY_TUTORIAL = G.OVERLAY_TUTORIAL or UIBox{ + definition = {n=G.UIT.ROOT, config = {align = "cm", padding = 32.05, r=0.1, colour = overlay_colour, emboss = 0.05}, nodes={ + {n=G.UIT.R, config={align = "tr", minh = G.ROOM.T.h, minw = G.ROOM.T.w}, nodes={ + UIBox_button{label = {localize('b_skip').." >"}, button = "skip_tutorial_section", minw = 1.3, scale = 0.45, colour = G.C.JOKER_GREY} + }} + }}, + config = { + align = "cm", + offset = {x=0,y=3.2}, + major = G.ROOM_ATTACH, + bond = 'Weak' + } + } + G.OVERLAY_TUTORIAL.step = G.OVERLAY_TUTORIAL.step or 1 + G.OVERLAY_TUTORIAL.step_complete = false + local row_dollars_chips = G.HUD:get_UIE_by_ID('row_dollars_chips') + local align = args.align or "tm" + local step = args.step or 1 + local attach = args.attach or {major = row_dollars_chips, type = 'tm', offset = {x=0, y=-0.5}} + local pos = args.pos or {x=attach.major.T.x + attach.major.T.w/2, y=attach.major.T.y + attach.major.T.h/2} + local button = args.button or {button = localize('b_next'), func = 'tut_next'} + args.highlight = args.highlight or {} + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.3, + func = function() + if G.OVERLAY_TUTORIAL and G.OVERLAY_TUTORIAL.step == step and + not G.OVERLAY_TUTORIAL.step_complete then + G.CONTROLLER.interrupt.focus = true + G.OVERLAY_TUTORIAL.Jimbo = G.OVERLAY_TUTORIAL.Jimbo or Card_Character(pos) + if type(args.highlight) == 'function' then args.highlight = args.highlight() end + args.highlight[#args.highlight+1] = G.OVERLAY_TUTORIAL.Jimbo + G.OVERLAY_TUTORIAL.Jimbo:add_speech_bubble(args.text_key, align, args.loc_vars) + G.OVERLAY_TUTORIAL.Jimbo:set_alignment(attach) + if args.hard_set then G.OVERLAY_TUTORIAL.Jimbo:hard_set_VT() end + G.OVERLAY_TUTORIAL.button_listen = nil + if G.OVERLAY_TUTORIAL.content then G.OVERLAY_TUTORIAL.content:remove() end + if args.content then + G.OVERLAY_TUTORIAL.content = UIBox{ + definition = args.content(), + config = { + align = args.content_config and args.content_config.align or "cm", + offset = args.content_config and args.content_config.offset or {x=0,y=0}, + major = args.content_config and args.content_config.major or G.OVERLAY_TUTORIAL.Jimbo, + bond = 'Weak' + } + } + args.highlight[#args.highlight+1] = G.OVERLAY_TUTORIAL.content + end + if args.button_listen then G.OVERLAY_TUTORIAL.button_listen = args.button_listen end + if not args.no_button then G.OVERLAY_TUTORIAL.Jimbo:add_button(button.button, button.func, button.colour, button.update_func, true) end + G.OVERLAY_TUTORIAL.Jimbo:say_stuff(2*(#(G.localization.misc.tutorial[args.text_key] or {}))+1) + if args.snap_to then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + blocking = false, blockable = false, + func = function() + if G.OVERLAY_TUTORIAL and G.OVERLAY_TUTORIAL.Jimbo and not G.OVERLAY_TUTORIAL.Jimbo.talking then + local _snap_to = args.snap_to() + if _snap_to then + G.CONTROLLER.interrupt.focus = false + G.CONTROLLER:snap_to({node = args.snap_to()}) + end + return true + end + end + }), 'tutorial') + end + if args.highlight then G.OVERLAY_TUTORIAL.highlights = args.highlight end + G.OVERLAY_TUTORIAL.step_complete = true + end + return not G.OVERLAY_TUTORIAL or G.OVERLAY_TUTORIAL.step > step or G.OVERLAY_TUTORIAL.skip_steps + end + }), 'tutorial') + return step+1 +end + +function calculate_reroll_cost(skip_increment) + if G.GAME.current_round.free_rerolls < 0 then G.GAME.current_round.free_rerolls = 0 end + if G.GAME.current_round.free_rerolls > 0 then G.GAME.current_round.reroll_cost = 0; return end + G.GAME.current_round.reroll_cost_increase = G.GAME.current_round.reroll_cost_increase or 0 + if not skip_increment then G.GAME.current_round.reroll_cost_increase = G.GAME.current_round.reroll_cost_increase + 1 end + G.GAME.current_round.reroll_cost = (G.GAME.round_resets.temp_reroll_cost or G.GAME.round_resets.reroll_cost) + G.GAME.current_round.reroll_cost_increase +end + +function reset_idol_card() + G.GAME.current_round.idol_card.rank = 'Ace' + G.GAME.current_round.idol_card.suit = 'Spades' + local valid_idol_cards = {} + for k, v in ipairs(G.playing_cards) do + if v.ability.effect ~= 'Stone Card' then + if (not v.config.center.no_suit) and (not v.config.center.no_rank) then + valid_idol_cards[#valid_idol_cards+1] = v + end + end + end + if valid_idol_cards[1] then + local idol_card = pseudorandom_element(valid_idol_cards, pseudoseed('idol'..G.GAME.round_resets.ante)) + G.GAME.current_round.idol_card.rank = idol_card.base.value + G.GAME.current_round.idol_card.suit = idol_card.base.suit + G.GAME.current_round.idol_card.id = idol_card.base.id + end +end + +function reset_mail_rank() + G.GAME.current_round.mail_card.rank = 'Ace' + local valid_mail_cards = {} + for k, v in ipairs(G.playing_cards) do + if v.ability.effect ~= 'Stone Card' then + if not v.config.center.no_rank then + valid_mail_cards[#valid_mail_cards+1] = v + end + end + end + if valid_mail_cards[1] then + local mail_card = pseudorandom_element(valid_mail_cards, pseudoseed('mail'..G.GAME.round_resets.ante)) + G.GAME.current_round.mail_card.rank = mail_card.base.value + G.GAME.current_round.mail_card.id = mail_card.base.id + end +end + +function reset_ancient_card() + local ancient_suits = {} + for k, v in ipairs({'Spades','Hearts','Clubs','Diamonds'}) do + if v ~= G.GAME.current_round.ancient_card.suit then ancient_suits[#ancient_suits + 1] = v end + end + local ancient_card = pseudorandom_element(ancient_suits, pseudoseed('anc'..G.GAME.round_resets.ante)) + G.GAME.current_round.ancient_card.suit = ancient_card +end + +function reset_castle_card() + G.GAME.current_round.castle_card.suit = 'Spades' + local valid_castle_cards = {} + for k, v in ipairs(G.playing_cards) do + if v.ability.effect ~= 'Stone Card' then + if not v.config.center.no_suit then + valid_castle_cards[#valid_castle_cards+1] = v + end + end + end + if valid_castle_cards[1] then + local castle_card = pseudorandom_element(valid_castle_cards, pseudoseed('cas'..G.GAME.round_resets.ante)) + G.GAME.current_round.castle_card.suit = castle_card.base.suit + end +end + +function reset_blinds() + G.GAME.round_resets.blind_states = G.GAME.round_resets.blind_states or {Small = 'Select', Big = 'Upcoming', Boss = 'Upcoming'} + if G.GAME.round_resets.blind_states.Boss == 'Defeated' then + G.GAME.round_resets.blind_states.Small = G.GAME.modifiers.cry_no_small_blind and 'Hide' or 'Upcoming' + G.GAME.round_resets.blind_states.Big = 'Upcoming' + G.GAME.round_resets.blind_states.Boss = 'Upcoming' + G.GAME.blind_on_deck = G.GAME.modifiers.cry_no_small_blind and 'Big' or 'Small' + if G.GAME.modifiers.cry_big_boss_rate and pseudorandom('cry_big_boss') < G.GAME.modifiers.cry_big_boss_rate then + G.GAME.round_resets.blind_choices.Big = get_new_boss() + elseif G.GAME.modifiers.cry_rush_hour_ii then + G.GAME.round_resets.blind_choices.Small = get_new_boss() + G.GAME.round_resets.blind_choices.Big = get_new_boss() + else + G.GAME.round_resets.blind_choices.Big = 'bl_big' + end + G.GAME.round_resets.blind_choices.Boss = get_new_boss() + G.GAME.round_resets.boss_rerolled = false + end +end + +function get_new_boss() + G.GAME.perscribed_bosses = G.GAME.perscribed_bosses or { + } + if G.GAME.perscribed_bosses and G.GAME.perscribed_bosses[G.GAME.round_resets.ante] then + local ret_boss = G.GAME.perscribed_bosses[G.GAME.round_resets.ante] + G.GAME.perscribed_bosses[G.GAME.round_resets.ante] = nil + G.GAME.bosses_used[ret_boss] = G.GAME.bosses_used[ret_boss] + 1 + return ret_boss + end + if G.FORCE_BOSS then return G.FORCE_BOSS end + + local eligible_bosses = {} + for k, v in pairs(G.P_BLINDS) do + if not v.boss then + + elseif v.in_pool and type(v.in_pool) == 'function' then + local res, options = v:in_pool() + if + ( + ((G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2) == + (v.boss.showdown or false) + ) or + (options or {}).ignore_showdown_check + then + eligible_bosses[k] = res and true or nil + end + elseif not v.boss.showdown and (v.boss.min <= math.max(1, G.GAME.round_resets.ante) and ((math.max(1, G.GAME.round_resets.ante))%G.GAME.win_ante ~= 0 or G.GAME.round_resets.ante < 2)) then + eligible_bosses[k] = true + elseif v.boss.showdown and (((G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2) or G.GAME.modifiers.cry_big_showdown ) then + eligible_bosses[k] = true + end + end + for k, v in pairs(G.GAME.banned_keys) do + if eligible_bosses[k] then eligible_bosses[k] = nil end + end + + local min_use = 100 + for k, v in pairs(G.GAME.bosses_used) do + if eligible_bosses[k] then + eligible_bosses[k] = v + if eligible_bosses[k] <= min_use then + min_use = eligible_bosses[k] + end + end + end + for k, v in pairs(eligible_bosses) do + if eligible_bosses[k] then + if eligible_bosses[k] > min_use then + eligible_bosses[k] = nil + end + end + end + local _, boss = pseudorandom_element(eligible_bosses, pseudoseed('boss')) + G.GAME.bosses_used[boss] = G.GAME.bosses_used[boss] + 1 + + return boss +end + +function get_type_colour(_c, card) + return + ((_c.unlocked == false and not (card and card.bypass_lock)) and G.C.BLACK) or + ((_c.unlocked ~= false and (_c.set == 'Joker' or _c.consumeable or _c.set == 'Voucher') and not _c.discoveredand and not ((_c.area ~= G.jokers and _c.area ~= G.consumeables and _c.area) or not _c.area)) and G.C.JOKER_GREY) or + (card and card.debuff and mix_colours(G.C.RED, G.C.GREY, 0.7)) or + (_c.set == 'Joker' and G.C.RARITY[_c.rarity]) or + (_c.set == 'Edition' and G.C.DARK_EDITION) or + (_c.set == 'Booster' and G.C.BOOSTER) or + G.C.SECONDARY_SET[_c.set] or + {0, 1, 1, 1} +end + +function generate_card_ui(_c, full_UI_table, specific_vars, card_type, badges, hide_desc, main_start, main_end, card) + if _c.specific_vars then specific_vars = _c.specific_vars end + if card == nil and card_type then + card = SMODS.compat_0_9_8.generate_UIBox_ability_table_card + end + +if _c.specific_vars then specific_vars = _c.specific_vars end + local first_pass = nil + if not full_UI_table then + first_pass = true + full_UI_table = { + main = {}, + info = {}, + type = {}, + name = nil, + badges = badges or {} + } + end + + local desc_nodes = (not full_UI_table.name and full_UI_table.main) or full_UI_table.info + local name_override = nil + local info_queue = {} + + if full_UI_table.name then + full_UI_table.info[#full_UI_table.info+1] = {} + desc_nodes = full_UI_table.info[#full_UI_table.info] + end + + if not full_UI_table.name then + if specific_vars and specific_vars.no_name then + full_UI_table.name = true + elseif card_type == 'Locked' then + full_UI_table.name = localize{type = 'name', set = 'Other', key = 'locked', nodes = {}} + elseif card_type == 'Undiscovered' then + full_UI_table.name = localize{type = 'name', set = 'Other', key = 'undiscovered_'..(string.lower(_c.set)), name_nodes = {}} + elseif specific_vars and (card_type == 'Default' or card_type == 'Enhanced') then + if _c.name == 'Stone Card' or _c.replace_base_card then full_UI_table.name = true + elseif specific_vars.playing_card then + full_UI_table.name = {} + localize{type = 'other', key = 'playing_card', set = 'Other', nodes = full_UI_table.name, vars = {localize(specific_vars.value, 'ranks'), localize(specific_vars.suit, 'suits_plural'), colours = {specific_vars.colour}}} + full_UI_table.name = full_UI_table.name[1] + end + elseif card_type == 'Booster' then + + else + if not _c.generate_ui or type(_c.generate_ui) ~= 'function' then + full_UI_table.name = localize{type = 'name', set = _c.set, key = _c.key, nodes = full_UI_table.name} + end + end + full_UI_table.card_type = card_type or _c.set + end + + local loc_vars = {} + if main_start then + desc_nodes[#desc_nodes+1] = main_start + end + + if _c.name == 'cry-membershipcard' or _c.name == 'cry-membershipcardtwo' then + if not Cryptid.enabled["HTTPS Module"] then + if G.localization.descriptions.Other.cry_https_disabled then + main_end = {} + localize{type = 'other', key = 'cry_https_disabled', nodes = main_end, vars = {}} + main_end = main_end[1] + end + end + end + if _c.name == 'cry-translucent Joker' then + if G.jokers and G.jokers.cards then + for k, v in ipairs(G.jokers.cards) do + if (v.edition and v.edition.negative) and (G.localization.descriptions.Other.remove_negative)then + main_end = {} + localize{type = 'other', key = 'remove_negative', nodes = main_end, vars = {}} + main_end = main_end[1] + break + end + end + end + end + if _c.name == 'cry-blurred Joker' then + if (SMODS.Mods["sdm0sstuff"] or {}).can_load then + if G.localization.descriptions.Other.blurred_sdm0 then + main_end = {} + localize{type = 'other', key = 'blurred_sdm0', nodes = main_end, vars = {}} + main_end = main_end[1] + end + end + end + local cfg = (card and card.ability) or _c['config'] + if _c.set == 'Other' then + localize{type = 'other', key = _c.key, nodes = desc_nodes, vars = specific_vars or _c.vars} + elseif card_type == 'Locked' then + if _c.wip then localize{type = 'other', key = 'wip_locked', set = 'Other', nodes = desc_nodes, vars = loc_vars} + elseif _c.demo and specific_vars then localize{type = 'other', key = 'demo_shop_locked', nodes = desc_nodes, vars = loc_vars} + elseif _c.demo then localize{type = 'other', key = 'demo_locked', nodes = desc_nodes, vars = loc_vars} + else + if _c.locked_loc_vars and type(_c.locked_loc_vars) == 'function' then + local res = _c:locked_loc_vars(info_queue) or {} + loc_vars = res.vars or {} + specific_vars = specific_vars or {} + specific_vars.not_hidden = res.not_hidden or specific_vars.not_hidden + elseif _c.name == 'Golden Ticket' then + elseif _c.name == 'Mr. Bones' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_losses} + elseif _c.name == 'Acrobat' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_hands_played} + elseif _c.name == 'Sock and Buskin' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_face_cards_played} + elseif _c.name == 'Swashbuckler' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_jokers_sold} + elseif _c.name == 'Troubadour' then loc_vars = {_c.unlock_condition.extra} + elseif _c.name == 'Certificate' then + elseif _c.name == 'Smeared Joker' then loc_vars = {_c.unlock_condition.extra.count,localize{type = 'name_text', key = _c.unlock_condition.extra.e_key, set = 'Enhanced'}} + elseif _c.name == 'Throwback' then + elseif _c.name == 'Hanging Chad' then loc_vars = {localize(_c.unlock_condition.extra, 'poker_hands')} + elseif _c.name == 'Rough Gem' then loc_vars = {_c.unlock_condition.extra.count, localize(_c.unlock_condition.extra.suit, 'suits_singular')} + elseif _c.name == 'Bloodstone' then loc_vars = {_c.unlock_condition.extra.count, localize(_c.unlock_condition.extra.suit, 'suits_singular')} + elseif _c.name == 'Arrowhead' then loc_vars = {_c.unlock_condition.extra.count, localize(_c.unlock_condition.extra.suit, 'suits_singular')} + elseif _c.name == 'Onyx Agate' then loc_vars = {_c.unlock_condition.extra.count, localize(_c.unlock_condition.extra.suit, 'suits_singular')} + elseif _c.name == 'Glass Joker' then loc_vars = {_c.unlock_condition.extra.count, localize{type = 'name_text', key = _c.unlock_condition.extra.e_key, set = 'Enhanced'}} + elseif _c.name == 'Showman' then loc_vars = {_c.unlock_condition.ante} + elseif _c.name == 'Flower Pot' then loc_vars = {_c.unlock_condition.ante} + elseif _c.name == 'Blueprint' then + elseif _c.name == 'Wee Joker' then loc_vars = {_c.unlock_condition.n_rounds} + elseif _c.name == 'Merry Andy' then loc_vars = {_c.unlock_condition.n_rounds} + elseif _c.name == 'Oops! All 6s' then loc_vars = {number_format(_c.unlock_condition.chips)} + elseif _c.name == 'The Idol' then loc_vars = {number_format(_c.unlock_condition.chips)} + elseif _c.name == 'Seeing Double' then loc_vars = {localize("ph_4_7_of_clubs")} + elseif _c.name == 'Matador' then + elseif _c.name == 'Hit the Road' then + elseif _c.name == 'The Duo' then loc_vars = {localize(_c.unlock_condition.extra, 'poker_hands')} + elseif _c.name == 'The Trio' then loc_vars = {localize(_c.unlock_condition.extra, 'poker_hands')} + elseif _c.name == 'The Family' then loc_vars = {localize(_c.unlock_condition.extra, 'poker_hands')} + elseif _c.name == 'The Order' then loc_vars = {localize(_c.unlock_condition.extra, 'poker_hands')} + elseif _c.name == 'The Tribe' then loc_vars = {localize(_c.unlock_condition.extra, 'poker_hands')} + elseif _c.name == 'Stuntman' then loc_vars = {number_format(_c.unlock_condition.chips)} + elseif _c.name == 'Invisible Joker' then + elseif _c.name == 'Brainstorm' then + elseif _c.name == 'Satellite' then loc_vars = {_c.unlock_condition.extra} + elseif _c.name == 'Shoot the Moon' then + elseif _c.name == "Driver's License" then loc_vars = {_c.unlock_condition.extra.count} + elseif _c.name == 'Cartomancer' then loc_vars = {_c.unlock_condition.tarot_count} + elseif _c.name == 'Astronomer' then + elseif _c.name == 'Burnt Joker' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_cards_sold} + elseif _c.name == 'Bootstraps' then loc_vars = {_c.unlock_condition.extra.count} + --Vouchers + elseif _c.name == 'Overstock Plus' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_shop_dollars_spent} + elseif _c.name == 'Liquidation' then loc_vars = {_c.unlock_condition.extra} + elseif _c.name == 'Tarot Tycoon' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_tarots_bought} + elseif _c.name == 'Planet Tycoon' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_planets_bought} + elseif _c.name == 'Glow Up' then loc_vars = {_c.unlock_condition.extra} + elseif _c.name == 'Reroll Glut' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_shop_rerolls} + elseif _c.name == 'Omen Globe' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_tarot_reading_used} + elseif _c.name == 'Observatory' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_planetarium_used} + elseif _c.name == 'Nacho Tong' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_cards_played} + elseif _c.name == 'Recyclomancy' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_cards_discarded} + elseif _c.name == 'Money Tree' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak} + elseif _c.name == 'Antimatter' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].voucher_usage.v_blank and G.PROFILES[G.SETTINGS.profile].voucher_usage.v_blank.count or 0} + elseif _c.name == 'Illusion' then loc_vars = {_c.unlock_condition.extra, G.PROFILES[G.SETTINGS.profile].career_stats.c_playing_cards_bought} + elseif _c.name == 'Petroglyph' then loc_vars = {_c.unlock_condition.extra} + elseif _c.name == 'Retcon' then loc_vars = {_c.unlock_condition.extra} + elseif _c.name == 'Palette' then loc_vars = {_c.unlock_condition.extra} + end + + if _c.rarity and _c.rarity == 4 and specific_vars and not specific_vars.not_hidden then + localize{type = 'unlocks', key = 'joker_locked_legendary', set = 'Other', nodes = desc_nodes, vars = loc_vars} + else + + localize{type = 'unlocks', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars} + end + end + elseif hide_desc then + localize{type = 'other', key = 'undiscovered_'..(string.lower(_c.set)), set = _c.set, nodes = desc_nodes} + elseif _c.generate_ui and type(_c.generate_ui) == 'function' then + _c:generate_ui(info_queue, card, desc_nodes, specific_vars, full_UI_table) + if specific_vars and specific_vars.pinned then info_queue[#info_queue+1] = {key = 'pinned_left', set = 'Other'} end + if specific_vars and specific_vars.sticker then info_queue[#info_queue+1] = {key = string.lower(specific_vars.sticker)..'_sticker', set = 'Other'} end + elseif specific_vars and specific_vars.debuffed then + localize{type = 'other', key = 'debuffed_'..(specific_vars.playing_card and 'playing_card' or 'default'), nodes = desc_nodes} + elseif _c.set == 'Joker' then + if _c.name == 'Stone Joker' or _c.name == 'Marble Joker' then info_queue[#info_queue+1] = G.P_CENTERS.m_stone + elseif _c.name == 'Steel Joker' then info_queue[#info_queue+1] = G.P_CENTERS.m_steel + elseif _c.name == 'Glass Joker' then info_queue[#info_queue+1] = G.P_CENTERS.m_glass + elseif _c.name == 'Golden Ticket' then info_queue[#info_queue+1] = G.P_CENTERS.m_gold + elseif _c.name == 'Lucky Cat' then info_queue[#info_queue+1] = G.P_CENTERS.m_lucky + elseif _c.name == 'Midas Mask' then info_queue[#info_queue+1] = G.P_CENTERS.m_gold + elseif _c.name == 'Invisible Joker' then + if G.jokers and G.jokers.cards then + for k, v in ipairs(G.jokers.cards) do + if (v.edition and v.edition.negative) and (G.localization.descriptions.Other.remove_negative)then + main_end = {} + localize{type = 'other', key = 'remove_negative', nodes = main_end, vars = {}} + main_end = main_end[1] + break + end + end + end + elseif _c.name == 'Diet Cola' then info_queue[#info_queue+1] = {key = 'tag_double', set = 'Tag'} + elseif _c.name == 'Perkeo' then info_queue[#info_queue+1] = {key = 'e_negative_consumable', set = 'Edition', config = {extra = 1}} + end + if specific_vars and specific_vars.pinned then info_queue[#info_queue+1] = {key = 'pinned_left', set = 'Other'} end + if specific_vars and specific_vars.sticker then info_queue[#info_queue+1] = {key = string.lower(specific_vars.sticker)..'_sticker', set = 'Other'} end + localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = specific_vars or {}} + elseif _c.set == 'Tag' then + if _c.name == 'Negative Tag' then info_queue[#info_queue+1] = G.P_CENTERS.e_negative + elseif _c.name == 'Foil Tag' then info_queue[#info_queue+1] = G.P_CENTERS.e_foil + elseif _c.name == 'Holographic Tag' then info_queue[#info_queue+1] = G.P_CENTERS.e_holo + elseif _c.name == 'Polychrome Tag' then info_queue[#info_queue+1] = G.P_CENTERS.e_polychrome + elseif _c.name == 'Charm Tag' then info_queue[#info_queue+1] = G.P_CENTERS.p_arcana_mega_1 + elseif _c.name == 'Meteor Tag' then info_queue[#info_queue+1] = G.P_CENTERS.p_celestial_mega_1 + elseif _c.name == 'Ethereal Tag' then info_queue[#info_queue+1] = G.P_CENTERS.p_spectral_normal_1 + elseif _c.name == 'Standard Tag' then info_queue[#info_queue+1] = G.P_CENTERS.p_standard_mega_1 + elseif _c.name == 'Buffoon Tag' then info_queue[#info_queue+1] = G.P_CENTERS.p_buffoon_mega_1 + end + localize{type = 'descriptions', key = _c.key, set = 'Tag', nodes = desc_nodes, vars = specific_vars or {}} + elseif _c.set == 'Voucher' then + if _c.name == "Overstock" or _c.name == "Overstock Plus" then loc_vars = {cfg.extra} + elseif _c.name == "Tarot Merchant" or _c.name == "Tarot Tycoon" then loc_vars = {cfg.extra_disp} + elseif _c.name == "Planet Merchant" or _c.name == "Planet Tycoon" then loc_vars = {cfg.extra_disp} + elseif _c.name == "Hone" or _c.name == "Glow Up" then loc_vars = {cfg.extra} + elseif _c.name == "Reroll Surplus" or _c.name == "Reroll Glut" then loc_vars = {cfg.extra} + elseif _c.name == "Grabber" or _c.name == "Nacho Tong" then loc_vars = {cfg.extra} + elseif _c.name == "Wasteful" or _c.name == "Recyclomancy" then loc_vars = {cfg.extra} + elseif _c.name == "Seed Money" or _c.name == "Money Tree" then loc_vars = {cfg.extra/5} + elseif _c.name == "Blank" or _c.name == "Antimatter" then loc_vars = {cfg.extra} + elseif _c.name == "Hieroglyph" or _c.name == "Petroglyph" then loc_vars = {cfg.extra} + elseif _c.name == "Director's Cut" or _c.name == "Retcon" then loc_vars = {cfg.extra} + elseif _c.name == "Paint Brush" or _c.name == "Palette" then loc_vars = {cfg.extra} + elseif _c.name == "Telescope" or _c.name == "Observatory" then loc_vars = {cfg.extra} + elseif _c.name == "Clearance Sale" or _c.name == "Liquidation" then loc_vars = {cfg.extra} + elseif _c.name == "Crystal Ball" or _c.name == "Omen Globe" then loc_vars = {cfg.extra} + end + localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars} + elseif _c.set == 'Edition' then + loc_vars = {cfg.extra} + localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars} + elseif _c.set == 'Default' and specific_vars then + if specific_vars.nominal_chips then + localize{type = 'other', key = 'card_chips', nodes = desc_nodes, vars = {specific_vars.nominal_chips}} + end + if specific_vars.bonus_chips then + localize{type = 'other', key = 'card_extra_chips', nodes = desc_nodes, vars = {specific_vars.bonus_chips}} + end + elseif _c.set == 'Enhanced' then + if specific_vars and _c.name ~= 'Stone Card' and specific_vars.nominal_chips then + localize{type = 'other', key = 'card_chips', nodes = desc_nodes, vars = {specific_vars.nominal_chips}} + end + if _c.effect == 'Mult Card' then loc_vars = {cfg.mult} + elseif _c.effect == 'Wild Card' then + elseif _c.effect == 'Glass Card' then loc_vars = {cfg.Xmult, G.GAME.probabilities.normal, cfg.extra} + elseif _c.effect == 'Steel Card' then loc_vars = {cfg.h_x_mult} + elseif _c.effect == 'Stone Card' then loc_vars = {((specific_vars and specific_vars.bonus_chips) or cfg.bonus)} + elseif _c.effect == 'Gold Card' then loc_vars = {cfg.h_dollars} + elseif _c.effect == 'Lucky Card' then loc_vars = {G.GAME.probabilities.normal, cfg.mult, 5, cfg.p_dollars, 15} + end + localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars} + if _c.name ~= 'Stone Card' and ((specific_vars and specific_vars.bonus_chips) or (cfg.bonus ~= 0 and cfg.bonus)) then + localize{type = 'other', key = 'card_extra_chips', nodes = desc_nodes, vars = {((specific_vars and specific_vars.bonus_chips) or cfg.bonus)}} + end + elseif _c.set == 'Booster' then + local desc_override = 'p_arcana_normal' + if _c.name == 'Arcana Pack' then desc_override = 'p_arcana_normal'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Jumbo Arcana Pack' then desc_override = 'p_arcana_jumbo'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Mega Arcana Pack' then desc_override = 'p_arcana_mega'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Celestial Pack' then desc_override = 'p_celestial_normal'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Jumbo Celestial Pack' then desc_override = 'p_celestial_jumbo'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Mega Celestial Pack' then desc_override = 'p_celestial_mega'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Spectral Pack' then desc_override = 'p_spectral_normal'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Jumbo Spectral Pack' then desc_override = 'p_spectral_jumbo'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Mega Spectral Pack' then desc_override = 'p_spectral_mega'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Standard Pack' then desc_override = 'p_standard_normal'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Jumbo Standard Pack' then desc_override = 'p_standard_jumbo'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Mega Standard Pack' then desc_override = 'p_standard_mega'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Buffoon Pack' then desc_override = 'p_buffoon_normal'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Jumbo Buffoon Pack' then desc_override = 'p_buffoon_jumbo'; loc_vars = {cfg.choose, cfg.extra} + elseif _c.name == 'Mega Buffoon Pack' then desc_override = 'p_buffoon_mega'; loc_vars = {cfg.choose, cfg.extra} + end + name_override = desc_override + if not full_UI_table.name then full_UI_table.name = localize{type = 'name', set = 'Other', key = name_override, nodes = full_UI_table.name} end + localize{type = 'other', key = desc_override, nodes = desc_nodes, vars = loc_vars} + elseif _c.set == 'Spectral' then + if _c.name == 'Talisman' or _c.name == 'Medium' or _c.name == 'Trance' or _c.name == 'Deja Vu' then + loc_vars = {cfg.max_highlighted} + end + + if _c.name == 'Familiar' or _c.name == 'Grim' or _c.name == 'Incantation' then loc_vars = {cfg.extra} + elseif _c.name == 'Immolate' then loc_vars = {cfg.extra.destroy, cfg.extra.dollars} + elseif _c.name == 'Hex' then info_queue[#info_queue+1] = G.P_CENTERS.e_polychrome + elseif _c.name == 'Talisman' then info_queue[#info_queue+1] = {key = 'gold_seal', set = 'Other'} + elseif _c.name == 'Deja Vu' then info_queue[#info_queue+1] = {key = 'red_seal', set = 'Other'} + elseif _c.name == 'Trance' then info_queue[#info_queue+1] = {key = 'blue_seal', set = 'Other'} + elseif _c.name == 'Medium' then info_queue[#info_queue+1] = {key = 'purple_seal', set = 'Other'} + elseif _c.name == 'Ankh' then + if G.jokers and G.jokers.cards then + for k, v in ipairs(G.jokers.cards) do + if (v.edition and v.edition.negative) and (G.localization.descriptions.Other.remove_negative)then + info_queue[#info_queue+1] = G.P_CENTERS.e_negative + main_end = {} + localize{type = 'other', key = 'remove_negative', nodes = main_end, vars = {}} + main_end = main_end[1] + break + end + end + end + elseif _c.name == 'Cryptid' then loc_vars = {cfg.extra, cfg.max_highlighted} + end + if _c.name == 'Ectoplasm' then info_queue[#info_queue+1] = G.P_CENTERS.e_negative; loc_vars = {G.GAME.ecto_minus or 1} end + if _c.name == 'Aura' then + info_queue[#info_queue+1] = G.P_CENTERS.e_foil + info_queue[#info_queue+1] = G.P_CENTERS.e_holo + info_queue[#info_queue+1] = G.P_CENTERS.e_polychrome + end + localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars} + elseif _c.set == 'Planet' then + loc_vars = { + G.GAME.hands[cfg.hand_type].level,localize(cfg.hand_type, 'poker_hands'), G.GAME.hands[cfg.hand_type].l_mult, G.GAME.hands[cfg.hand_type].l_chips, + colours = {(G.GAME.hands[cfg.hand_type].level==1 and G.C.UI.TEXT_DARK or G.C.HAND_LEVELS[math.min(7, G.GAME.hands[cfg.hand_type].level)])} + } + localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars} + elseif _c.set == 'Tarot' then + if _c.name == "The Fool" then + local fool_c = G.GAME.last_tarot_planet and G.P_CENTERS[G.GAME.last_tarot_planet] or nil + local last_tarot_planet = fool_c and localize{type = 'name_text', key = fool_c.key, set = fool_c.set} or localize('k_none') + local colour = (not fool_c or fool_c.name == 'The Fool') and G.C.RED or G.C.GREEN + main_end = { + {n=G.UIT.C, config={align = "bm", padding = 0.02}, nodes={ + {n=G.UIT.C, config={align = "m", colour = colour, r = 0.05, padding = 0.05}, nodes={ + {n=G.UIT.T, config={text = ' '..last_tarot_planet..' ', colour = G.C.UI.TEXT_LIGHT, scale = 0.3, shadow = true}}, + }} + }} + } + loc_vars = {last_tarot_planet} + if not (not fool_c or fool_c.name == 'The Fool') then + info_queue[#info_queue+1] = fool_c + end + elseif _c.name == "The Magician" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv] + elseif _c.name == "The High Priestess" then loc_vars = {cfg.planets} + elseif _c.name == "The Empress" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv] + elseif _c.name == "The Emperor" then loc_vars = {cfg.tarots} + elseif _c.name == "The Hierophant" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv] + elseif _c.name == "The Lovers" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv] + elseif _c.name == "The Chariot" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv] + elseif _c.name == "Justice" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv] + elseif _c.name == "The Hermit" then loc_vars = {cfg.extra} + elseif _c.name == "The Wheel of Fortune" then loc_vars = {G.GAME.probabilities.normal, cfg.extra}; info_queue[#info_queue+1] = G.P_CENTERS.e_foil; info_queue[#info_queue+1] = G.P_CENTERS.e_holo; info_queue[#info_queue+1] = G.P_CENTERS.e_polychrome; + elseif _c.name == "Strength" then loc_vars = {cfg.max_highlighted} + elseif _c.name == "The Hanged Man" then loc_vars = {cfg.max_highlighted} + elseif _c.name == "Death" then loc_vars = {cfg.max_highlighted} + elseif _c.name == "Temperance" then + local _money = 0 + if G.jokers then + for i = 1, #G.jokers.cards do + if G.jokers.cards[i].ability.set == 'Joker' then + _money = _money + G.jokers.cards[i].sell_cost + end + end + end + loc_vars = {cfg.extra, math.min(cfg.extra, _money)} + elseif _c.name == "The Devil" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv] + elseif _c.name == "The Tower" then loc_vars = {cfg.max_highlighted, localize{type = 'name_text', set = 'Enhanced', key = cfg.mod_conv}}; info_queue[#info_queue+1] = G.P_CENTERS[cfg.mod_conv] + elseif _c.name == "The Star" then loc_vars = {cfg.max_highlighted, localize(cfg.suit_conv, 'suits_plural'), colours = {G.C.SUITS[cfg.suit_conv]}} + elseif _c.name == "The Moon" then loc_vars = {cfg.max_highlighted, localize(cfg.suit_conv, 'suits_plural'), colours = {G.C.SUITS[cfg.suit_conv]}} + elseif _c.name == "The Sun" then loc_vars = {cfg.max_highlighted, localize(cfg.suit_conv, 'suits_plural'), colours = {G.C.SUITS[cfg.suit_conv]}} + elseif _c.name == "Judgement" then + elseif _c.name == "The World" then loc_vars = {cfg.max_highlighted, localize(cfg.suit_conv, 'suits_plural'), colours = {G.C.SUITS[cfg.suit_conv]}} + end + localize{type = 'descriptions', key = _c.key, set = _c.set, nodes = desc_nodes, vars = loc_vars} + end + + if main_end then + desc_nodes[#desc_nodes+1] = main_end + end + + if card_type == 'Default' or card_type == 'Enhanced' and not _c.replace_base_card and card and card.base then + if not _c.no_suit then + local suit = SMODS.Suits[card.base.suit] or {} + if suit.loc_vars and type(suit.loc_vars) == 'function' then + suit:loc_vars(info_queue, card) + end + end + if not _c.no_rank then + local rank = SMODS.Ranks[card.base.value] or {} + if rank.loc_vars and type(rank.loc_vars) == 'function' then + rank:loc_vars(info_queue, card) + end + end + end + + --Fill all remaining info if this is the main desc + if not ((specific_vars and not specific_vars.sticker) and (card_type == 'Default' or card_type == 'Enhanced')) then + if desc_nodes == full_UI_table.main and not full_UI_table.name then + localize{type = 'name', key = _c.key, set = _c.set, nodes = full_UI_table.name} + if not full_UI_table.name then full_UI_table.name = {} end + elseif desc_nodes ~= full_UI_table.main and not desc_nodes.name then + desc_nodes.name = localize{type = 'name_text', key = name_override or _c.key, set = name_override and 'Other' or _c.set} + end + end + + if first_pass and not (_c.set == 'Edition') and badges then + for k, v in ipairs(badges) do + v = (v == 'holographic' and 'holo' or v) + if v:sub(1,9) == 'negative_' then + info_queue[#info_queue+1] = {key = 'e_'..v, set = 'Edition', config = {extra = G.P_CENTERS['e_negative'].config.card_limit}} + end + if G.P_CENTERS[v] and G.P_CENTERS[v].set == 'Edition' then + info_queue[#info_queue + 1] = G.P_CENTERS[v] + end + if G.P_CENTERS['e_'..v] and G.P_CENTERS['e_'..v].set == 'Edition' then + info_queue[#info_queue + 1] = G.P_CENTERS['e_'..v] + end + if v == 'cry_pinned_booster' then info_queue[#info_queue+1] = {key = 'cry_pinned_booster', set = 'Other'} end + if v == 'cry_pinned_voucher' then info_queue[#info_queue+1] = {key = 'cry_pinned_voucher', set = 'Other'} end + if v == 'cry_pinned_consumeable' then info_queue[#info_queue+1] = {key = 'cry_pinned_consumeable', set = 'Other'} end + local seal = SMODS.Seals[v] or SMODS.Seal.badge_to_key[v] and SMODS.Seals[SMODS.Seal.badge_to_key[v]] + if seal and seal.generate_ui ~= 0 then + local t = { key = v, set = 'Other' } + info_queue[#info_queue+1] = t + if seal.loc_vars and type(seal.loc_vars) == 'function' then + local res = seal:loc_vars(info_queue, card) or {} + t.vars = res.vars + t.key = res.key or t.key + end + else + if v == 'gold_seal' then info_queue[#info_queue+1] = {key = 'gold_seal', set = 'Other'} end + if v == 'blue_seal' then info_queue[#info_queue+1] = {key = 'blue_seal', set = 'Other'} end + if v == 'red_seal' then info_queue[#info_queue+1] = {key = 'red_seal', set = 'Other'} end + if v == 'purple_seal' then info_queue[#info_queue+1] = {key = 'purple_seal', set = 'Other'} end + end + local sticker = SMODS.Stickers[v] + if sticker then + local t = { key = v, set = 'Other' } + local res = {} + if sticker.loc_vars and type(sticker.loc_vars) == 'function' then + res = sticker:loc_vars(info_queue, card) or {} + t.vars = res.vars or {} + t.key = res.key or t.key + end + info_queue[#info_queue+1] = t + else + if v == 'eternal' then info_queue[#info_queue+1] = {key = 'eternal', set = 'Other'} end + if v == 'perishable' then info_queue[#info_queue+1] = {key = 'perishable', set = 'Other', vars = {G.GAME.perishable_rounds or 1, specific_vars.perish_tally or G.GAME.perishable_rounds}} end + if v == 'rental' then info_queue[#info_queue+1] = {key = 'rental', set = 'Other', vars = {G.GAME.rental_rate or 1}} end + end + if v == 'pinned_left' then info_queue[#info_queue+1] = {key = 'pinned_left', set = 'Other'} end + end + end + + SMODS.compat_0_9_8.generate_UIBox_ability_table_card = nil + for _, v in ipairs(info_queue) do + generate_card_ui(v, full_UI_table) + end + + return full_UI_table +end diff --git a/lovely/dump/functions/misc_functions.lua b/lovely/dump/functions/misc_functions.lua new file mode 100644 index 0000000..287df59 --- /dev/null +++ b/lovely/dump/functions/misc_functions.lua @@ -0,0 +1,2180 @@ +LOVELY_INTEGRITY = '3b2e5120d569763446e3bfe099ac89fa7ef4750a6936e9b65da90e4121f5aa22' + +--Updates all display information for all displays for a given screenmode. Returns the key for the resolution option cycle +-- +---@param screenmode string optional arg for one of 'Windowed', 'Fullscreen', or 'Borderless' +---@param display number optional arg to match fullscreen resolution with only the current display +function GET_DISPLAYINFO(screenmode, display) + --default the display to the current display, otherwise we need to return all available modes for the selected display + display = display or G.SETTINGS.WINDOW.selcted_display or 1 + + --default to current mode/'windowed', otherwise return all available options for specified mode + screenmode = screenmode or G.SETTINGS.WINDOW.screenmode or 'Windowed' + + --Get a count of the connected displays and iterate through them to update our window setting options accordingly + local display_count = love.window.getDisplayCount() + local res_option = 1 --This is the mode that the fullscreen option will default to for resolution, if we can find it in the list for the current monitor + local realw, realh = love.window.getMode() --the actual render resolution the window is currently using + local current_res_values = {w = realw, h = realh} + + --reset names to populate for displays + G.SETTINGS.WINDOW.display_names = {} + + for i = 1, display_count do + --reset the screen resolution table for this display and set the name + G.SETTINGS.WINDOW.DISPLAYS[i] = {} + G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions = { + strings = {}, + values = {}, + } + G.SETTINGS.WINDOW.display_names[i] = ''..i + + --Get the render resolution and desktop resolution, this is a workaround to a known OpenGL issue where windows doesn't supply the correct DPI scaling factor + local render_w, render_h = love.window.getDesktopDimensions(i) + local unscaled_dims = love.window.getFullscreenModes(i)[1] + + --determine the DPI scale here, this can be used to determine the actual render resolution per display + G.SETTINGS.WINDOW.DISPLAYS[i].DPI_scale = 1--math.floor((0.5*unscaled_dims.width/render_w + 0.5*unscaled_dims.height/render_h)*500 + 0.5)/500 + G.SETTINGS.WINDOW.DISPLAYS[i].MONITOR_DIMS = unscaled_dims + + if screenmode == 'Fullscreen' then --Iterate through all possible screenmodes and populate the screen_resolutions table + --if the real resolution of the window is found, make sure we return that value + for _, v in ipairs(love.window.getFullscreenModes(i)) do + local _w, _h = v.width*G.SETTINGS.WINDOW.DISPLAYS[i].DPI_scale, v.height*G.SETTINGS.WINDOW.DISPLAYS[i].DPI_scale + if _w <= G.SETTINGS.WINDOW.DISPLAYS[i].MONITOR_DIMS.width and _h <= G.SETTINGS.WINDOW.DISPLAYS[i].MONITOR_DIMS.height then + G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.strings[#G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.strings+1] = ''..v.width..' X '..v.height + G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.values[#G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.values+1] = {w = v.width, h = v.height} + if i == G.SETTINGS.WINDOW.selected_display and i == display and current_res_values.w == v.width and current_res_values.h == v.height then + res_option = #G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.values + end + end + end + elseif screenmode == 'Windowed' then + G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.strings[1] = '-' + G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.values[1] = {w = 1280, h = 720} + elseif screenmode == 'Borderless' then + G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.strings[1] = + ''..G.SETTINGS.WINDOW.DISPLAYS[i].MONITOR_DIMS.width/G.SETTINGS.WINDOW.DISPLAYS[i].DPI_scale..' X ' + ..G.SETTINGS.WINDOW.DISPLAYS[i].MONITOR_DIMS.height/G.SETTINGS.WINDOW.DISPLAYS[i].DPI_scale + G.SETTINGS.WINDOW.DISPLAYS[i].screen_resolutions.values[1] = current_res_values + end + end + return res_option +end + +function timer_checkpoint(label, type, reset) + G.PREV_GARB = G.PREV_GARB or 0 + if not G.F_ENABLE_PERF_OVERLAY then return end + G.check = G.check or { + draw = { + checkpoint_list = {}, + checkpoints = 0, + last_time = 0, + }, + update = { + checkpoint_list = {}, + checkpoints = 0, + last_time = 0, + } + } + local cp = G.check[type] + if reset then + cp.last_time = love.timer.getTime() + cp.checkpoints = 0 + return + end + + cp.checkpoint_list[cp.checkpoints+1] = cp.checkpoint_list[cp.checkpoints+1] or {} + cp.checkpoints = cp.checkpoints+1 + cp.checkpoint_list[cp.checkpoints].label = label..': '..(collectgarbage( "count" ) - G.PREV_GARB) + cp.checkpoint_list[cp.checkpoints].time = love.timer.getTime() + cp.checkpoint_list[cp.checkpoints].TTC = cp.checkpoint_list[cp.checkpoints].time - cp.last_time + cp.checkpoint_list[cp.checkpoints].trend = cp.checkpoint_list[cp.checkpoints].trend or {} + cp.checkpoint_list[cp.checkpoints].states = cp.checkpoint_list[cp.checkpoints].states or {} + table.insert(cp.checkpoint_list[cp.checkpoints].trend, 1, cp.checkpoint_list[cp.checkpoints].TTC) + table.insert(cp.checkpoint_list[cp.checkpoints].states, 1, G.STATE) + cp.checkpoint_list[cp.checkpoints].trend[401] = nil + cp.checkpoint_list[cp.checkpoints].states[401] = nil + cp.last_time = cp.checkpoint_list[cp.checkpoints].time + G.PREV_GARB = collectgarbage( "count" ) + local av = 0 + for k, v in ipairs(cp.checkpoint_list[cp.checkpoints].trend) do + av = av + v/#cp.checkpoint_list[cp.checkpoints].trend + end + cp.checkpoint_list[cp.checkpoints].average = av +end + +function boot_timer(_label, _next, progress) + progress = progress or 0 + G.LOADING = G.LOADING or { + font = love.graphics.setNewFont("resources/fonts/m6x11plus.ttf", 20), + love.graphics.dis + } + local realw, realh = love.window.getMode() + love.graphics.setCanvas() + love.graphics.push() + love.graphics.setShader() + love.graphics.clear(0,0,0,1) + love.graphics.setColor(0.6, 0.8, 0.9,1) + if progress > 0 then love.graphics.rectangle('fill', realw/2 - 150, realh/2 - 15, progress*300, 30, 5) end + love.graphics.setColor(1, 1, 1,1) + love.graphics.setLineWidth(3) + love.graphics.rectangle('line', realw/2 - 150, realh/2 - 15, 300, 30, 5) + if G.F_VERBOSE and not _RELEASE_MODE then love.graphics.print("LOADING: ".._next, realw/2 - 150, realh/2 +40) end + love.graphics.pop() + love.graphics.present() + + G.ARGS.bt = G.ARGS.bt or love.timer.getTime() + G.ARGS.bt = love.timer.getTime() +end + +function EMPTY(t) + if not t then return {} end + for k, v in pairs(t) do + t[k] = nil + end + return t +end + +function interp(per, max, min) +min = min or 0 + if per and max then + return per*(max - min) + min + end +end + +function remove_all(t) + for i=#t, 1, -1 do + local v=t[i] + table.remove(t, i) + if v and v.children then + remove_all(v.children) + end + if v then v:remove() end + v = nil + end + for _, v in pairs(t) do + if v.children then remove_all(v.children) end + v:remove() + v = nil + end +end + +function Vector_Dist(trans1, trans2, mid) + local x = trans1.x - trans2.x + (mid and 0.5*(trans1.w-trans2.w) or 0) + local y = trans1.y - trans2.y + (mid and 0.5*(trans1.h-trans2.h) or 0) + return math.sqrt((x)*(x) + (y)*(y)) +end + +function Vector_Len(trans1) + return math.sqrt((trans1.x)*(trans1.x) + (trans1.y)*(trans1.y)) +end + +function Vector_Sub(trans1, trans2) + return {x = trans1.x - trans2.x, y = trans1.y - trans2.y} +end + +function get_index(t, val) + local index = nil + for i, v in pairs(t) do + if v == val then + index = i + end + end + return index +end + +function table_length(t) + local count = 0 + for _ in pairs(t) do count = count + 1 end + return count +end + +function remove_nils(t) + local ans = {} + for _,v in pairs(t) do + ans[ #ans+1 ] = v + end + return ans + end + +function SWAP(t, i, j) + if not t or not i or not j then return end + local temp = t[i] + t[i] = t[j] + t[j] = temp +end + +function pseudoshuffle(list, seed) + if seed then math.randomseed(seed) end + + if list[1] and list[1].sort_id then + table.sort(list, function (a, b) return (a.sort_id or 1) < (b.sort_id or 2) end) + end + + for i = #list, 2, -1 do + local j = math.random(i) + list[i], list[j] = list[j], list[i] + end +end + +function generate_starting_seed() + if G.GAME.stake >= #G.P_CENTER_POOLS['Stake'] then + local r_leg, r_tally = {}, 0 + local g_leg, g_tally = {}, 0 + for k, v in pairs(G.P_JOKER_RARITY_POOLS[4]) do + local win_ante = get_joker_win_sticker(v, true) + if win_ante and (win_ante >= 8) or (v.in_pool and type(v.in_pool) == 'function' and not v:in_pool()) then + g_leg[v.key] = true + g_tally = g_tally + 1 + else + r_leg[v.key] = true + r_tally = r_tally + 1 + end + end + if r_tally > 0 and g_tally > 0 then + local seed_found = nil + local extra_num = 0 + while not seed_found do + extra_num = extra_num + 0.561892350821 + seed_found = random_string(8, extra_num + G.CONTROLLER.cursor_hover.T.x*0.33411983 + G.CONTROLLER.cursor_hover.T.y*0.874146 + 0.412311010*G.CONTROLLER.cursor_hover.time) + if not r_leg[get_first_legendary(seed_found)] then seed_found = nil end + end + return seed_found + end + end + + return random_string(8, G.CONTROLLER.cursor_hover.T.x*0.33411983 + G.CONTROLLER.cursor_hover.T.y*0.874146 + 0.412311010*G.CONTROLLER.cursor_hover.time) +end + +function get_first_legendary(_key) + local _t, key = pseudorandom_element(G.P_JOKER_RARITY_POOLS[4], pseudoseed('Joker4', _key)) + return _t.key +end + +function pseudorandom_element(_t, seed, args) + -- TODO special cases for now + -- Preserves reverse nominal order for Suits, nominal+face_nominal order for Ranks + -- for vanilla RNG + if _t == SMODS.Suits then + _t = SMODS.Suit:obj_list(true) + end + if _t == SMODS.Ranks then + _t = SMODS.Rank:obj_list() + end + if seed then math.randomseed(seed) end + local keys = {} + for k, v in pairs(_t) do + local keep = true + local in_pool_func = + args and args.in_pool + or type(v) == 'table' and type(v.in_pool) == 'function' and v.in_pool + or _t == G.P_CARDS and function(c) + --Handles special case for Erratic Deck + local initial_deck = args and args.starting_deck or false + + return not ( + type(SMODS.Ranks[c.value].in_pool) == 'function' and not SMODS.Ranks[c.value]:in_pool({initial_deck = initial_deck}) + or type(SMODS.Suits[c.suit].in_pool) == 'function' and not SMODS.Suits[c.suit]:in_pool({initial_deck = initial_deck}) + ) + end + if in_pool_func then + keep = in_pool_func(v, args) + end + if keep then + keys[#keys+1] = {k = k,v = v} + end + end + + if keys[1] and keys[1].v and type(keys[1].v) == 'table' and keys[1].v.sort_id then + table.sort(keys, function (a, b) return a.v.sort_id < b.v.sort_id end) + else + table.sort(keys, function (a, b) return a.k < b.k end) + end + + if #keys == 0 then return nil, nil end + local key = keys[math.random(#keys)].k + return _t[key], key +end + +function random_string(length, seed) + if seed then math.randomseed(seed) end + local ret = '' + for i = 1, length do + ret = ret..string.char(math.random() > 0.7 and math.random(string.byte('1'),string.byte('9')) or (math.random() > 0.45 and math.random(string.byte('A'),string.byte('N')) or math.random(string.byte('P'),string.byte('Z')))) + end + return string.upper(ret) +end + +function pseudohash(str) + if true then + local num = 1 + for i=#str, 1, -1 do + num = ((1.1239285023/num)*string.byte(str, i)*math.pi + math.pi*i)%1 + end + return num + else + str = string.sub(string.format("%-16s",str), 1, 24) + + local h = 0 + + for i=#str, 1, -1 do + h = bit.bxor(h, bit.lshift(h, 7) + bit.rshift(h, 3) + string.byte(str, i)) + end + return tonumber(string.format("%.13f",math.sqrt(math.abs(h))%1)) + end +end + +function pseudoseed(key, predict_seed) + if key == 'seed' then return math.random() end + + if predict_seed then + local _pseed = pseudohash(key..(predict_seed or '')) + _pseed = math.abs(tonumber(string.format("%.13f", (2.134453429141+_pseed*1.72431234)%1))) + return (_pseed + (pseudohash(predict_seed) or 0))/2 + end + + if not G.GAME.pseudorandom[key] then + G.GAME.pseudorandom[key] = pseudohash(key..(G.GAME.pseudorandom.seed or '')) + end + + G.GAME.pseudorandom[key] = math.abs(tonumber(string.format("%.13f", (2.134453429141+G.GAME.pseudorandom[key]*1.72431234)%1))) + return (G.GAME.pseudorandom[key] + (G.GAME.pseudorandom.hashed_seed or 0))/2 +end + +function pseudorandom(seed, min, max) + if type(seed) == 'string' then seed = pseudoseed(seed) end + math.randomseed(seed) + if min and max then return math.random(min, max) + else return math.random() end +end + +function tprint(tbl, indent) + if not indent then indent = 0 end + local toprint = string.rep(" ", indent) .. "{\r\n" + indent = indent + 2 + for k, v in pairs(tbl) do + toprint = toprint .. string.rep(" ", indent) + if (type(k) == "number") then + toprint = toprint .. "[" .. k .. "] = " + elseif (type(k) == "string") then + toprint = toprint .. k .. "= " + end + if (type(v) == "number") then + toprint = toprint .. v .. ",\r\n" + elseif (type(v) == "string") then + toprint = toprint .. "\"" .. v .. "\",\r\n" + elseif (type(v) == "table") then + if indent>=10 then + toprint = toprint .. tostring(v) .. ",\r\n" + else + toprint = toprint .. tostring(v) .. tprint(v, indent + 1) .. ",\r\n" + end + else + toprint = toprint .. "\"" .. tostring(v) .. "\",\r\n" + end + end + toprint = toprint .. string.rep(" ", indent-2) .. "}" + return toprint +end + +function sortingFunction(e1, e2) + return e1.order < e2.order +end + +function HEX(hex) + if #hex <= 6 then hex = hex.."FF" end + local _,_,r,g,b,a = hex:find('(%x%x)(%x%x)(%x%x)(%x%x)') + local color = {tonumber(r,16)/255,tonumber(g,16)/255,tonumber(b,16)/255,tonumber(a,16)/255 or 255} + return color +end + +function get_blind_main_colour(blind) --either in the form of the blind key for the P_BLINDS table or type + local disabled = false + blind = blind or '' + if blind == 'Boss' or blind == 'Small' or blind == 'Big' then + G.GAME.round_resets.blind_states = G.GAME.round_resets.blind_states or {} + if G.GAME.round_resets.blind_states[blind] == 'Defeated' or G.GAME.round_resets.blind_states[blind] == 'Skipped' then disabled = true end + blind = G.GAME.round_resets.blind_choices[blind] + end + return (disabled or not G.P_BLINDS[blind]) and G.C.BLACK or + G.P_BLINDS[blind].boss_colour or + (blind == 'bl_small' and mix_colours(G.C.BLUE, G.C.BLACK, 0.6) or + blind == 'bl_big' and mix_colours(G.C.ORANGE, G.C.BLACK, 0.6)) or G.C.BLACK +end + +function evaluate_poker_hand(hand) + + local results = { + ["Flush Five"] = {}, + ["Flush House"] = {}, + ["Five of a Kind"] = {}, + ["Straight Flush"] = {}, + ["Four of a Kind"] = {}, + ["Full House"] = {}, + ["Flush"] = {}, + ["Straight"] = {}, + ["Three of a Kind"] = {}, + ["Two Pair"] = {}, + ["Pair"] = {}, + ["High Card"] = {}, + top = nil + } + + for _,v in ipairs(SMODS.PokerHand.obj_buffer) do + results[v] = {} + end + local parts = { + _5 = get_X_same(5,hand), + _4 = get_X_same(4,hand), + _3 = get_X_same(3,hand), + _2 = get_X_same(2,hand), + _flush = get_flush(hand), + _straight = get_straight(hand), + _highest = get_highest(hand) + } + + for _,_hand in pairs(SMODS.PokerHands) do + if _hand.atomic_part and type(_hand.atomic_part) == 'function' then + parts[_hand.key] = _hand.atomic_part(hand) + end + end + if next(parts._5) and next(parts._flush) then + results["Flush Five"] = parts._5 + if not results.top then results.top = results["Flush Five"] end + end + + if next(parts._3) and next(parts._2) and next(parts._flush) then + local fh_hand = {} + local fh_3 = parts._3[1] + local fh_2 = parts._2[1] + for i=1, #fh_3 do + fh_hand[#fh_hand+1] = fh_3[i] + end + for i=1, #fh_2 do + fh_hand[#fh_hand+1] = fh_2[i] + end + table.insert(results["Flush House"], fh_hand) + if not results.top then results.top = results["Flush House"] end + end + + if next(parts._5) then + results["Five of a Kind"] = parts._5 + if not results.top then results.top = results["Five of a Kind"] end + end + + if next(parts._flush) and next(parts._straight) then + local _s, _f, ret = parts._straight, parts._flush, {} + for _, v in ipairs(_f[1]) do + ret[#ret+1] = v + end + for _, v in ipairs(_s[1]) do + local in_straight = nil + for _, vv in ipairs(_f[1]) do + if vv == v then in_straight = true end + end + if not in_straight then ret[#ret+1] = v end + end + + results["Straight Flush"] = {ret} + if not results.top then results.top = results["Straight Flush"] end + end + + if next(parts._4) then + results["Four of a Kind"] = parts._4 + if not results.top then results.top = results["Four of a Kind"] end + end + + if next(parts._3) and next(parts._2) then + local fh_hand = {} + local fh_3 = parts._3[1] + local fh_2 = parts._2[1] + for i=1, #fh_3 do + fh_hand[#fh_hand+1] = fh_3[i] + end + for i=1, #fh_2 do + fh_hand[#fh_hand+1] = fh_2[i] + end + table.insert(results["Full House"], fh_hand) + if not results.top then results.top = results["Full House"] end + end + + if next(parts._flush) then + results["Flush"] = parts._flush + if not results.top then results.top = results["Flush"] end + end + + if next(parts._straight) then + results["Straight"] = parts._straight + if not results.top then results.top = results["Straight"] end + end + + if next(parts._3) then + results["Three of a Kind"] = parts._3 + if not results.top then results.top = results["Three of a Kind"] end + end + + if (#parts._2 == 2) or (#parts._3 == 1 and #parts._2 == 1) then + local fh_hand = {} + local r = parts._2 + local fh_2a = r[1] + local fh_2b = r[2] + if not fh_2b then + fh_2b = parts._3[1] + end + for i=1, #fh_2a do + fh_hand[#fh_hand+1] = fh_2a[i] + end + for i=1, #fh_2b do + fh_hand[#fh_hand+1] = fh_2b[i] + end + table.insert(results["Two Pair"], fh_hand) + if not results.top then results.top = results["Two Pair"] end + end + + if next(parts._2) then + results["Pair"] = parts._2 + if not results.top then results.top = results["Pair"] end + end + + if next(parts._highest) then + results["High Card"] = parts._highest + if not results.top then results.top = results["High Card"] end + end + + if results["Five of a Kind"][1] then + results["Four of a Kind"] = {results["Five of a Kind"][1], results["Five of a Kind"][2], results["Five of a Kind"][3], results["Five of a Kind"][4]} + end + + if results["Four of a Kind"][1] then + results["Three of a Kind"] = {results["Four of a Kind"][1], results["Four of a Kind"][2], results["Four of a Kind"][3]} + end + + if results["Three of a Kind"][1] then + results["Pair"] = {results["Three of a Kind"][1], results["Three of a Kind"][2]} + end + + for _,_hand in pairs(SMODS.PokerHands) do + if _hand.composite and type(_hand.composite) == 'function' then + local other_hands + results[_hand.key], other_hands = _hand.composite(parts) + results[_hand.key] = results[_hand.key] or {} + if other_hands and type(other_hands) == 'table' then + for k, v in pairs(other_hands) do + results[k] = v + end + end + else + results[_hand.key] = parts[_hand.key] + end + end + results.top = nil + for _, v in ipairs(G.handlist) do + if not results.top and results[v] then + results.top = results[v] + break + end + end + return results +end + +function get_flush(hand) + local ret = {} + local four_fingers = next(find_joker('Four Fingers')) + local suits = SMODS.Suit.obj_buffer + if #hand < (5 - (four_fingers and 1 or 0)) then return ret else + for j = 1, #suits do + local t = {} + local suit = suits[j] + local flush_count = 0 + for i=1, #hand do + if hand[i]:is_suit(suit, nil, true) then flush_count = flush_count + 1; t[#t+1] = hand[i] end + end + if flush_count >= (5 - (four_fingers and 1 or 0)) then + table.insert(ret, t) + return ret + end + end + return {} + end +end + +function get_straight(hand) + local ret = {} + local four_fingers = next(find_joker('Four Fingers')) + if #hand > 5 or #hand < (5 - (four_fingers and 1 or 0)) then return ret else + local t = {} + local IDS = {} + for i=1, #hand do + local id = hand[i]:get_id() + if id > 1 and id < 15 then + if IDS[id] then + IDS[id][#IDS[id]+1] = hand[i] + else + IDS[id] = {hand[i]} + end + end + end + + local straight_length = 0 + local straight = false + local can_skip = next(find_joker('Shortcut')) + local skipped_rank = false + for j = 1, 14 do + if IDS[j == 1 and 14 or j] then + straight_length = straight_length + 1 + skipped_rank = false + for k, v in ipairs(IDS[j == 1 and 14 or j]) do + t[#t+1] = v + end + elseif can_skip and not skipped_rank and j ~= 14 then + skipped_rank = true + else + straight_length = 0 + skipped_rank = false + if not straight then t = {} end + if straight then break end + end + if straight_length >= (5 - (four_fingers and 1 or 0)) then straight = true end + end + if not straight then return ret end + table.insert(ret, t) + return ret + end +end + +function get_X_same(num, hand, or_more) + local vals = {} + for i = 1, SMODS.Rank.max_id.value do + vals[i] = {} + end + for i=#hand, 1, -1 do + local curr = {} + table.insert(curr, hand[i]) + for j=1, #hand do + if hand[i]:get_id() == hand[j]:get_id() and i ~= j then + table.insert(curr, hand[j]) + end + end + if or_more and (#curr >= num) or (#curr == num) then + vals[curr[1]:get_id()] = curr + end + end + local ret = {} + for i=#vals, 1, -1 do + if next(vals[i]) then table.insert(ret, vals[i]) end + end + return ret +end + +function get_highest(hand) + local highest = nil + for k, v in ipairs(hand) do + if not highest or v:get_nominal() > highest:get_nominal() then + highest = v + end + end + if #hand > 0 then return {{highest}} else return {} end +end + +function reset_drawhash() + G.DRAW_HASH = EMPTY(G.DRAW_HASH) +end + +--Copyright 2021 Max Cahill (Zlib license) +-- +--This software is provided 'as-is', without any express or implied +--warranty. In no event will the authors be held liable for any damages +--arising from the use of this software. +-- +--Permission is granted to anyone to use this software for any purpose, +--including commercial applications, and to alter it and redistribute it +--freely, subject to the following restrictions: +-- +--1. The origin of this software must not be misrepresented; you must not +-- claim that you wrote the original software. If you use this software +-- in a product, an acknowledgment in the product documentation would be +-- appreciated but is not required. +--2. Altered source versions must be plainly marked as such, and must not be +-- misrepresented as being the original software. +--3. This notice may not be removed or altered from any source distribution. +--This function was slightly modified from it's original state +function nuGC(time_budget, memory_ceiling, disable_otherwise) + time_budget = time_budget or 3e-4 + memory_ceiling = memory_ceiling or 300 + local max_steps = 1000 + local steps = 0 + local start_time = love.timer.getTime() + while + love.timer.getTime() - start_time < time_budget and + steps < max_steps + do + collectgarbage("step", 1) + steps = steps + 1 + end + --safety net + if collectgarbage("count") / 1024 > memory_ceiling then + collectgarbage("collect") + end + --don't collect gc outside this margin + if disable_otherwise then + collectgarbage("stop") + end +end + +--The drawhash is a hash table of all drawn nodes and all nodes that may be invisible but still collide with the cursor +function add_to_drawhash(obj) + if obj then + G.DRAW_HASH[#G.DRAW_HASH+1] = obj + end +end + +function mix_colours(C1, C2, proportionC1) + return { + (C1[1] or 0.5)*proportionC1 + (C2[1] or 0.5)*(1-proportionC1), + (C1[2] or 0.5)*proportionC1 + (C2[2] or 0.5)*(1-proportionC1), + (C1[3] or 0.5)*proportionC1 + (C2[3] or 0.5)*(1-proportionC1), + (C1[4] or 1)*proportionC1 + (C2[4] or 1)*(1-proportionC1), + } +end + +function mod_chips(_chips) + if G.GAME.modifiers.chips_dollar_cap then + _chips = math.min(_chips, math.max(G.GAME.dollars, 0)) + end + return _chips +end + +function mod_mult(_mult) + return _mult +end + +function play_sound(sound_code, per, vol) + if G.F_MUTE then return end + if sound_code and G.SETTINGS.SOUND.volume > 0.001 then + G.ARGS.play_sound = G.ARGS.play_sound or {} + G.ARGS.play_sound.type = 'sound' + G.ARGS.play_sound.time = G.TIMERS.REAL + G.ARGS.play_sound.crt = G.SETTINGS.GRAPHICS.crt + G.ARGS.play_sound.sound_code = sound_code + G.ARGS.play_sound.per = per + G.ARGS.play_sound.vol = vol + G.ARGS.play_sound.pitch_mod = G.PITCH_MOD + G.ARGS.play_sound.state = G.STATE + G.ARGS.play_sound.music_control = G.SETTINGS.music_control + G.ARGS.play_sound.sound_settings = G.SETTINGS.SOUND + G.ARGS.play_sound.splash_vol = G.SPLASH_VOL + G.ARGS.play_sound.overlay_menu = not (not G.OVERLAY_MENU) + if G.F_SOUND_THREAD then + G.SOUND_MANAGER.channel:push(G.ARGS.play_sound) + else + PLAY_SOUND(G.ARGS.play_sound) + end + end +end + +function modulate_sound(dt) + --volume of the splash screen is set here + G.SPLASH_VOL = 2*dt*(G.STATE == G.STATES.SPLASH and 1 or 0) + (G.SPLASH_VOL or 1)*(1-2*dt) + + --Control the music here + local desired_track = + G.video_soundtrack or + (G.STATE == G.STATES.SPLASH and '') or + SMODS.Sound:get_current_music() or + (G.booster_pack_sparkles and not G.booster_pack_sparkles.REMOVED and 'music2') or + (G.booster_pack_meteors and not G.booster_pack_meteors.REMOVED and 'music3') or + (G.booster_pack and not G.booster_pack.REMOVED and 'music2') or + (G.shop and not G.shop.REMOVED and 'music4') or + (G.GAME.blind and G.GAME.blind.boss and 'music5') or + ('music1') + + G.PITCH_MOD = (G.PITCH_MOD or 1)*(1 - dt) + dt*((not G.normal_music_speed and G.STATE == G.STATES.GAME_OVER) and 0.5 or 1) + + --For ambient sound control + G.SETTINGS.ambient_control = G.SETTINGS.ambient_control or {} + G.ARGS.score_intensity = G.ARGS.score_intensity or {} + if not is_number(G.GAME.current_round.current_hand.chips) or not is_number(G.GAME.current_round.current_hand.mult) then + G.ARGS.score_intensity.earned_score = 0 + else + G.ARGS.score_intensity.earned_score = G.GAME.current_round.current_hand.chips*G.GAME.current_round.current_hand.mult + end + G.ARGS.score_intensity.required_score = G.GAME.blind and G.GAME.blind.chips or 0 + if G.cry_flame_override and G.cry_flame_override['duration'] > 0 then + G.cry_flame_override['duration'] = math.max(0, G.cry_flame_override['duration'] - dt) + end + G.ARGS.score_intensity.flames = math.min(1, (G.STAGE == G.STAGES.RUN and 1 or 0)*( + (G.ARGS.chip_flames and (G.ARGS.chip_flames.real_intensity + G.ARGS.chip_flames.change) or 0))/10) + G.ARGS.score_intensity.organ = G.video_organ or to_big(G.ARGS.score_intensity.required_score) > to_big(0) and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0 + + local AC = G.SETTINGS.ambient_control + G.ARGS.ambient_sounds = G.ARGS.ambient_sounds or { + ambientFire2 = {volfunc = function(_prev_volume) return _prev_volume*(1 - dt) + dt*0.9*((G.ARGS.score_intensity.flames > 0.3) and 1 or G.ARGS.score_intensity.flames/0.3) end}, + ambientFire1 = {volfunc = function(_prev_volume) return _prev_volume*(1 - dt) + dt*0.8*((G.ARGS.score_intensity.flames > 0.3) and (G.ARGS.score_intensity.flames-0.3)/0.7 or 0) end}, + ambientFire3 = {volfunc = function(_prev_volume) return _prev_volume*(1 - dt) + dt*0.4*((G.ARGS.chip_flames and G.ARGS.chip_flames.change or 0) + (G.ARGS.mult_flames and G.ARGS.mult_flames.change or 0)) end}, + ambientOrgan1 = {volfunc = function(_prev_volume) return _prev_volume*(1 - dt) + dt*0.6*(G.SETTINGS.SOUND.music_volume + 100)/200*(G.ARGS.score_intensity.organ) end}, + } + + for k, v in pairs(G.ARGS.ambient_sounds) do + AC[k] = AC[k] or {} + AC[k].per = (k == 'ambientOrgan1') and 0.7 or (k == 'ambientFire1' and 1.1) or (k == 'ambientFire2' and 1.05) or 1 + AC[k].vol = Cartomancer.handle_flames_volume((not G.video_organ and G.STATE == G.STATES.SPLASH) and 0 or AC[k].vol and v.volfunc(AC[k].vol) or 0) + end + + G.ARGS.push = G.ARGS.push or {} + G.ARGS.push.type = 'modulate' + G.ARGS.push.pitch_mod = G.PITCH_MOD + G.ARGS.push.state = G.STATE + G.ARGS.push.time = G.TIMERS.REAL + G.ARGS.push.dt = dt + G.ARGS.push.desired_track = desired_track + G.ARGS.push.sound_settings = G.SETTINGS.SOUND + G.ARGS.push.splash_vol = G.SPLASH_VOL + G.ARGS.push.overlay_menu = not (not G.OVERLAY_MENU) + G.ARGS.push.ambient_control = G.SETTINGS.ambient_control + if SMODS.remove_replace_sound and SMODS.remove_replace_sound ~= desired_track then + SMODS.Sound.replace_sounds[SMODS.remove_replace_sound] = nil + SMODS.remove_replace_sound = nil + end + local replace_sound = SMODS.Sound.replace_sounds[desired_track] + if replace_sound then + local replaced_track = desired_track + desired_track = replace_sound.key + G.ARGS.push.desired_track = desired_track + if SMODS.previous_track ~= desired_track then + if replace_sound.times > 0 then replace_sound.times = replace_sound.times - 1 end + if replace_sound.times == 0 then SMODS.remove_replace_sound = replaced_track end + end + end + local stop_sound = SMODS.Sound.stop_sounds[desired_track] + if SMODS.Sound.stop_sounds[desired_track] then + if SMODS.previous_track ~= '' and stop_sound > 0 then stop_sound = stop_sound - 1 end + SMODS.Sound.stop_sounds[desired_track] = stop_sound ~= 0 and stop_sound or nil + SMODS.previous_track = '' + return + end + + if G.F_SOUND_THREAD then + G.SOUND_MANAGER.channel:push(G.ARGS.push) + SMODS.previous_track = SMODS.previous_track or '' + local in_sync = (SMODS.Sounds[desired_track] or {}).sync + local out_sync = (SMODS.Sounds[SMODS.previous_track] or {}).sync + local should_sync = true + if (type(in_sync) == 'table' and not in_sync[SMODS.previous_track]) or in_sync == false then should_sync = false end + if (type(out_sync) == 'table' and not out_sync[desired_track]) or out_sync == false then should_sync = false end + if + SMODS.previous_track and SMODS.previous_track ~= desired_track and + not should_sync + then + G.ARGS.push.type = 'restart_music' + G.SOUND_MANAGER.channel:push(G.ARGS.push) + end + SMODS.previous_track = desired_track + else + MODULATE(G.ARGS.push) + end +end + +function count_of_suit(area, suit) + local num = 0 + for _,c in pairs(area.cards) do + if c.base.suit == suit then + num = num +1 + end + end + return num +end + +function prep_draw(moveable, scale, rotate, offset) +if Big and G.STATE == G.STATES.MENU then moveable.VT.x = to_big(moveable.VT.x):to_number() +moveable.VT.y = to_big(moveable.VT.y):to_number() +moveable.VT.w = to_big(moveable.VT.w):to_number() +moveable.VT.h = to_big(moveable.VT.h):to_number() end + love.graphics.push() + love.graphics.scale(G.TILESCALE*G.TILESIZE) + love.graphics.translate( + moveable.VT.x+moveable.VT.w/2 + (offset and offset.x or 0) + ((moveable.layered_parallax and moveable.layered_parallax.x) or ((moveable.parent and moveable.parent.layered_parallax and moveable.parent.layered_parallax.x)) or 0), + moveable.VT.y+moveable.VT.h/2 + (offset and offset.y or 0) + ((moveable.layered_parallax and moveable.layered_parallax.y) or ((moveable.parent and moveable.parent.layered_parallax and moveable.parent.layered_parallax.y)) or 0)) + if moveable.VT.r ~= 0 or moveable.juice or rotate then love.graphics.rotate(moveable.VT.r + (rotate or 0)) end + love.graphics.translate( + -scale*moveable.VT.w*(moveable.VT.scale)/2, + -scale*moveable.VT.h*(moveable.VT.scale)/2) + love.graphics.scale(moveable.VT.scale*scale) +end + +function get_chosen_triangle_from_rect(x, y, w, h, vert) + local scale = 2 + if vert then + x = x + math.min(0.6*math.sin(G.TIMERS.REAL*9)*scale+0.2, 0) + return { + x-3.5*scale, y+h/2 - 1.5*scale, + x-0.5*scale, y+h/2 + 0, + x-3.5*scale,y+h/2 + 1.5*scale + } + else + y = y + math.min(0.6*math.sin(G.TIMERS.REAL*9)*scale+0.2, 0) + return { + x+w/2 - 1.5*scale, y-4*scale, + x+w/2 + 0, y-1.1*scale, + x+w/2 + 1.5*scale, y-4*scale + } + end +end + +function point_translate(_T, delta) + _T.x = (_T.x + delta.x) or 0 + _T.y = (_T.y + delta.y) or 0 +end + +function point_rotate(_T, angle) + local _cos, _sin, _ox, _oy = math.cos(angle+math.pi/2), math.sin(angle+math.pi/2), _T.x , _T.y + _T.x = -_oy*_cos + _ox*_sin + _T.y = _oy*_sin + _ox*_cos +end + +function lighten(colour, percent, no_tab) + if no_tab then + return + colour[1]*(1-percent)+percent, + colour[2]*(1-percent)+percent, + colour[3]*(1-percent)+percent, + colour[4] + end + return { + colour[1]*(1-percent)+percent, + colour[2]*(1-percent)+percent, + colour[3]*(1-percent)+percent, + colour[4] + } +end + +function darken(colour, percent, no_tab) + if no_tab then + return + colour[1]*(1-percent), + colour[2]*(1-percent), + colour[3]*(1-percent), + colour[4] + end + return { + colour[1]*(1-percent), + colour[2]*(1-percent), + colour[3]*(1-percent), + colour[4] + } +end + +function adjust_alpha(colour, new_alpha, no_tab) + if no_tab then + return + colour[1], + colour[2], + colour[3], + new_alpha + end + return { + colour[1], + colour[2], + colour[3], + new_alpha + } +end + +function alert_no_space(card, area) + G.CONTROLLER.locks.no_space = true + attention_text({ + scale = 0.9, text = localize('k_no_space_ex'), hold = 0.9, align = 'cm', + cover = area, cover_padding = 0.1, cover_colour = adjust_alpha(G.C.BLACK, 0.7) + }) + card:juice_up(0.3, 0.2) + for i = 1, #area.cards do + area.cards[i]:juice_up(0.15) + end + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + play_sound('tarot2', 1, 0.4) + + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.5*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, + func = function() + G.CONTROLLER.locks.no_space = nil + return true end})) +end + +function find_joker(name, non_debuff) + local jokers = {} + if not G.jokers or not G.jokers.cards then return {} end + for k, v in pairs(G.jokers.cards) do + if v and type(v) == 'table' and v.ability.name == name and (non_debuff or not v.debuff) then + table.insert(jokers, v) + end + end + for k, v in pairs(G.consumeables.cards) do + if v and type(v) == 'table' and v.ability.name == name and (non_debuff or not v.debuff) then + table.insert(jokers, v) + end + end + return jokers +end + +function get_blind_amount(ante) +if G.GAME.modifiers.scaling and G.GAME.modifiers.scaling > 3 then return SMODS.get_blind_amount(ante) end + local k = 0.75 + if not G.GAME.modifiers.scaling or G.GAME.modifiers.scaling == 1 then + local amounts = { + 300, 800, 2000, 5000, 11000, 20000, 35000, 50000 + } + if ante < 1 then return 100 end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = math.floor(a*(b+(k*c)^d)^c) + amount = amount - amount%(10^math.floor(math.log10(amount)-1)) + return amount + elseif G.GAME.modifiers.scaling == 2 then + local amounts = { + 300, 900, 2600, 8000, 20000, 36000, 60000, 100000 + --300, 900, 2400, 7000, 18000, 32000, 56000, 90000 + } + if ante < 1 then return 100 end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = math.floor(a*(b+(k*c)^d)^c) + amount = amount - amount%(10^math.floor(math.log10(amount)-1)) + return amount + elseif G.GAME.modifiers.scaling == 3 then + local amounts = { + 300, 1000, 3200, 9000, 25000, 60000, 110000, 200000 + --300, 1000, 3000, 8000, 22000, 50000, 90000, 180000 + } + if ante < 1 then return 100 end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = math.floor(a*(b+(k*c)^d)^c) + amount = amount - amount%(10^math.floor(math.log10(amount)-1)) + return amount + end +end + +function number_format(num, e_switch_point) + if type(num) ~= 'number' then return num end + + local sign = (num >= 0 and "") or "-" + num = math.abs(num) + G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 + if not num or type(num) ~= 'number' then return num or '' end + if num >= (e_switch_point or G.E_SWITCH_POINT) then + local x = string.format("%.4g",num) + local fac = math.floor(math.log(tonumber(x), 10)) + if num == math.huge then + return sign.."naneinf" + end + + local mantissa = round_number(x/(10^fac), 3) + if mantissa >= 10 then + mantissa = mantissa / 10 + fac = fac + 1 + end + return sign..(string.format(fac >= 100 and "%.1fe%i" or fac >= 10 and "%.2fe%i" or "%.3fe%i", mantissa, fac)) + end + local formatted + if num ~= math.floor(num) and num < 100 then + formatted = string.format(num >= 10 and "%.1f" or "%.2f", num) + if formatted:sub(-1) == "0" then + formatted = formatted:gsub("%.?0+$", "") + end + -- Return already to avoid comas being added + if num < 0.01 then return tostring(num) end + else + formatted = string.format("%.0f", num) + end + return sign..(formatted:reverse():gsub("(%d%d%d)", "%1,"):gsub(",$", ""):reverse()) +end + +function score_number_scale(scale, amt) + G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 + if type(amt) ~= 'number' then return 0.7*(scale or 1) end + if amt >= G.E_SWITCH_POINT then + return 0.7*(scale or 1) + elseif amt >= 1000000 then + return 14*0.75/(math.floor(math.log(amt))+4)*(scale or 1) + else + return 0.75*(scale or 1) + end +end + +function copy_table(O) + local O_type = type(O) + local copy + if O_type == 'table' then + copy = {} + for k, v in next, O, nil do + copy[copy_table(k)] = copy_table(v) + end + setmetatable(copy, copy_table(getmetatable(O))) + else + copy = O + end + return copy +end + +function send_score(_score) + if G.F_HTTP_SCORES and G.SETTINGS.COMP and G.F_STREAMER_EVENT then + G.HTTP_MANAGER.out_channel:push({ + set_score = true, + score = _score, + username = G.SETTINGS.COMP.name, + uid = tostring(G.STEAM.user.getSteamID()), + version = G.VERSION + }) + end +end + +function send_name() + if G.F_HTTP_SCORES and G.SETTINGS.COMP and G.F_STREAMER_EVENT then + G.HTTP_MANAGER.out_channel:push({ + set_name = true, + username = G.SETTINGS.COMP.name, + uid = tostring(G.STEAM.user.getSteamID()), + version = G.VERSION + }) + end +end + +function check_and_set_high_score(score, amt) + if not amt or type(amt) ~= 'number' then return end + if G.GAME.round_scores[score] and math.floor(amt) > (G.GAME.round_scores[score].amt or 0) then + G.GAME.round_scores[score].amt = math.floor(amt) + end + if G.GAME.seeded then return end + if score == 'hand' and G.SETTINGS.COMP and ((not G.SETTINGS.COMP.score) or (G.SETTINGS.COMP.score < math.floor(amt))) then + G.SETTINGS.COMP.score = amt + send_score(math.floor(amt)) + end + if G.PROFILES[G.SETTINGS.profile].high_scores[score] and math.floor(amt) > G.PROFILES[G.SETTINGS.profile].high_scores[score].amt then + if G.GAME.round_scores[score] then G.GAME.round_scores[score].high_score = true end + G.PROFILES[G.SETTINGS.profile].high_scores[score].amt = math.floor(amt) + G:save_settings() + end +end + +function set_joker_usage() + for k, v in pairs(G.jokers.cards) do + if v.config.center_key and v.ability.set == 'Joker' then + if G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] then + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].count = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].count + 1 + else + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = {count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} + end + end + end + G:save_settings() +end + +function set_joker_win() + for k, v in pairs(G.jokers.cards) do + if v.config.center_key and v.ability.set == 'Joker' then + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] or {count = 1, order = v.config.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} + if G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] then + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins or {} + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins[G.GAME.stake] or 0) + 1 + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins_by_key[SMODS.stake_from_index(G.GAME.stake)] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].wins_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1 + end + end + end + G:save_settings() +end + +function get_joker_win_sticker(_center, index) + if G.PROFILES[G.SETTINGS.profile].joker_usage[_center.key] and + G.PROFILES[G.SETTINGS.profile].joker_usage[_center.key].wins then + local _w = 0 + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].joker_usage[_center.key].wins) do + _w = math.max(k, _w) + end + if index then return _w end + if _w > 0 then return G.sticker_map[_w] end + end + if index then return 0 end +end + +function set_joker_loss() + for k, v in pairs(G.jokers.cards) do + if v.config.center_key and v.ability.set == 'Joker' then + if G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key] then + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses = G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses or {} + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses[G.GAME.stake] or 0) + 1 + G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses_by_key[SMODS.stake_from_index(G.GAME.stake)] = (G.PROFILES[G.SETTINGS.profile].joker_usage[v.config.center_key].losses_by_key[SMODS.stake_from_index(G.GAME.stake)] or 0) + 1 + end + end + end + G:save_settings() +end + +function set_deck_usage() + if G.GAME.selected_back and G.GAME.selected_back.effect and G.GAME.selected_back.effect.center and G.GAME.selected_back.effect.center.key then + local deck_key = G.GAME.selected_back.effect.center.key + if G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then + G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].count = G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].count + 1 + else + G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} + end + G:save_settings() + end +end + +function set_deck_win() + if G.GAME.selected_back and G.GAME.selected_back.effect and G.GAME.selected_back.effect.center and G.GAME.selected_back.effect.center.key then + local deck_key = G.GAME.selected_back.effect.center.key + if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} end + if G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then + G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].wins[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].wins[G.GAME.stake] or 0) + 1 + for i = 1, + (G.P_CENTER_POOLS["Stake"][G.GAME.stake].unlocked_stake) and + (G.P_STAKES[G.P_CENTER_POOLS["Stake"][G.GAME.stake].unlocked_stake].stake_level-1) or (G.GAME.stake-1) + do + G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].wins[i] = (G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].wins[i] or 1) + end + end + set_challenge_unlock() + G:save_settings() + end +end + +function set_challenge_unlock() + if G.PROFILES[G.SETTINGS.profile].all_unlocked then return end + if G.PROFILES[G.SETTINGS.profile].challenges_unlocked then + local _ch_comp, _ch_tot = 0,#G.CHALLENGES + for k, v in ipairs(G.CHALLENGES) do + if v.id and G.PROFILES[G.SETTINGS.profile].challenge_progress.completed[v.id or ''] then + _ch_comp = _ch_comp + 1 + end + end + G.PROFILES[G.SETTINGS.profile].challenges_unlocked = math.min(_ch_tot, _ch_comp+5) + else + local deck_wins = 0 + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage) do + if v.wins and v.wins[1] then + deck_wins = deck_wins + 1 + end + end + if deck_wins >= G.CHALLENGE_WINS and not G.PROFILES[G.SETTINGS.profile].challenges_unlocked then + G.PROFILES[G.SETTINGS.profile].challenges_unlocked = 5 + notify_alert('b_challenge', "Back") + end + end +end + +function get_deck_win_stake(_deck_key) + if not _deck_key then + local _w, _w_low = 0, nil + local deck_count = 0 + for _, deck in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage) do + local deck_won_with = nil + for k, v in pairs(deck.wins) do + deck_won_with = true + _w = math.max(k, _w) + end + if deck_won_with then deck_count = deck_count + 1 end + _w_low = _w_low and (math.min(_w_low, _w)) or _w + end + return _w, ((deck_count >= #G.P_CENTER_POOLS.Back) and _w_low or 0) + end + if G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key] and + G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key].wins then + local _w = 0 + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage[_deck_key].wins) do + _w = math.max(k, _w) + end + return _w + end + return 0 +end + +function get_deck_win_sticker(_center) + if G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key] and + G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key].wins then + local _w = -1 + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].deck_usage[_center.key].wins) do + _w = math.max(k, _w) + end + if _w > 0 then return G.sticker_map[_w] end + end +end + +function set_deck_loss() + if G.GAME.selected_back and G.GAME.selected_back.effect and G.GAME.selected_back.effect.center and G.GAME.selected_back.effect.center.key then + local deck_key = G.GAME.selected_back.effect.center.key + if not G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] = {count = 1, order = G.GAME.selected_back.effect.center.order, wins = {}, losses = {}, wins_by_key = {}, losses_by_key = {}} end + if G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key] then + G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].losses[G.GAME.stake] = (G.PROFILES[G.SETTINGS.profile].deck_usage[deck_key].losses[G.GAME.stake] or 0) + 1 + end + G:save_settings() + end +end + +function set_consumeable_usage(card) + if card.config.center_key and card.ability.consumeable then + if G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key] then + G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key].count = G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key].count + 1 + else + G.PROFILES[G.SETTINGS.profile].consumeable_usage[card.config.center_key] = {count = 1, order = card.config.center.order} + end + if G.GAME.consumeable_usage[card.config.center_key] then + G.GAME.consumeable_usage[card.config.center_key].count = G.GAME.consumeable_usage[card.config.center_key].count + 1 + else + G.GAME.consumeable_usage[card.config.center_key] = {count = 1, order = card.config.center.order, set = card.ability.set} + end + G.GAME.consumeable_usage_total = G.GAME.consumeable_usage_total or {tarot = 0, planet = 0, spectral = 0, tarot_planet = 0, all = 0} + if card.config.center.set == 'Tarot' then + G.GAME.consumeable_usage_total.tarot = G.GAME.consumeable_usage_total.tarot + 1 + G.GAME.consumeable_usage_total.tarot_planet = G.GAME.consumeable_usage_total.tarot_planet + 1 + elseif card.config.center.set == 'Planet' then + G.GAME.consumeable_usage_total.planet = G.GAME.consumeable_usage_total.planet + 1 + G.GAME.consumeable_usage_total.tarot_planet = G.GAME.consumeable_usage_total.tarot_planet + 1 + elseif card.config.center.set == 'Spectral' then G.GAME.consumeable_usage_total.spectral = G.GAME.consumeable_usage_total.spectral + 1 + end + + G.GAME.consumeable_usage_total.all = G.GAME.consumeable_usage_total.all + 1 + + if not card.config.center.discovered then + discover_card(card) + end + + if card.config.center.set == 'Tarot' or card.config.center.set == 'Planet' then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.GAME.last_tarot_planet = card.config.center_key + return true + end + })) + return true + end + })) + end + + end + G:save_settings() +end + +function set_voucher_usage(card) + if card.config.center_key and card.ability.set == 'Voucher' then + if G.PROFILES[G.SETTINGS.profile].voucher_usage[card.config.center_key] then + G.PROFILES[G.SETTINGS.profile].voucher_usage[card.config.center_key].count = G.PROFILES[G.SETTINGS.profile].voucher_usage[card.config.center_key].count + 1 + else + G.PROFILES[G.SETTINGS.profile].voucher_usage[card.config.center_key] = {count = 1, order = card.config.center.order} + end + end + G:save_settings() +end + +function set_hand_usage(hand) + local hand_label = hand + hand = hand:gsub("%s+", "") + if G.PROFILES[G.SETTINGS.profile].hand_usage[hand] then + G.PROFILES[G.SETTINGS.profile].hand_usage[hand].count = G.PROFILES[G.SETTINGS.profile].hand_usage[hand].count + 1 + else + G.PROFILES[G.SETTINGS.profile].hand_usage[hand] = {count = 1, order = hand_label} + end + if G.GAME.hand_usage[hand] then + G.GAME.hand_usage[hand].count = G.GAME.hand_usage[hand].count + 1 + else + G.GAME.hand_usage[hand] = {count = 1, order = hand_label} + end + G:save_settings() +end + +function set_profile_progress() + G.PROGRESS = G.PROGRESS or { + joker_stickers = {tally = 0, of = 0}, + deck_stakes = {tally = 0, of = 0}, + challenges = {tally = 0, of = 0}, + } + for _, v in pairs(G.PROGRESS) do + if type(v) == 'table' then + v.tally = 0 + v.of = 0 + end + end + + for _, v in pairs(G.P_CENTERS) do + if v.set == 'Back' and not v.omit then + G.PROGRESS.deck_stakes.of = G.PROGRESS.deck_stakes.of + #G.P_CENTER_POOLS.Stake + G.PROGRESS.deck_stakes.tally = G.PROGRESS.deck_stakes.tally + get_deck_win_stake(v.key) + end + if v.set == 'Joker' then + G.PROGRESS.joker_stickers.of = G.PROGRESS.joker_stickers.of + #G.P_CENTER_POOLS.Stake + G.PROGRESS.joker_stickers.tally = G.PROGRESS.joker_stickers.tally + get_joker_win_sticker(v, true) + end + end + + for _, v in pairs(G.CHALLENGES) do + G.PROGRESS.challenges.of = G.PROGRESS.challenges.of + 1 + if G.PROFILES[G.SETTINGS.profile].challenge_progress.completed[v.id] then + G.PROGRESS.challenges.tally = G.PROGRESS.challenges.tally + 1 + end + end + + G.PROFILES[G.SETTINGS.profile].progress.joker_stickers = copy_table(G.PROGRESS.joker_stickers) + G.PROFILES[G.SETTINGS.profile].progress.deck_stakes = copy_table(G.PROGRESS.deck_stakes) + G.PROFILES[G.SETTINGS.profile].progress.challenges = copy_table(G.PROGRESS.challenges) +end + +function set_discover_tallies() + G.DISCOVER_TALLIES = G.DISCOVER_TALLIES or { + blinds = {tally = 0, of = 0}, + tags = {tally = 0, of = 0}, + jokers = {tally = 0, of = 0}, + consumeables = {tally = 0, of = 0}, + vouchers = {tally = 0, of = 0}, + boosters = {tally = 0, of = 0}, + editions = {tally = 0, of = 0}, + backs = {tally = 0, of = 0}, + total = {tally = 0, of = 0}, + } + for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do + G.DISCOVER_TALLIES[v:lower()..'s'] = {tally = 0, of = 0} + end for _, v in pairs(G.DISCOVER_TALLIES) do + v.tally = 0 + v.of = 0 + end + + for _, v in pairs(G.P_CENTERS) do + if not v.omit then + if v.set and ((v.set == 'Joker') or v.consumeable or (v.set == 'Edition') or (v.set == 'Voucher') or (v.set == 'Back') or (v.set == 'Booster')) then + G.DISCOVER_TALLIES.total.of = G.DISCOVER_TALLIES.total.of+1 + if v.discovered then + G.DISCOVER_TALLIES.total.tally = G.DISCOVER_TALLIES.total.tally+1 + end + end + if v.set and v.set == 'Joker' then + G.DISCOVER_TALLIES.jokers.of = G.DISCOVER_TALLIES.jokers.of+1 + if v.discovered then + G.DISCOVER_TALLIES.jokers.tally = G.DISCOVER_TALLIES.jokers.tally+1 + end + end + if v.set and v.set == 'Back' then + G.DISCOVER_TALLIES.backs.of = G.DISCOVER_TALLIES.backs.of+1 + if v.unlocked then + G.DISCOVER_TALLIES.backs.tally = G.DISCOVER_TALLIES.backs.tally+1 + end + end + if v.set and v.consumeable then + G.DISCOVER_TALLIES.consumeables.of = G.DISCOVER_TALLIES.consumeables.of+1 + if v.discovered then + G.DISCOVER_TALLIES.consumeables.tally = G.DISCOVER_TALLIES.consumeables.tally+1 + end + local tally = G.DISCOVER_TALLIES[v.set:lower()..'s'] + if tally then + tally.of = tally.of + 1 + if v.discovered then + tally.tally = tally.tally + 1 + end + end + end + if v.set and v.set == 'Voucher' then + G.DISCOVER_TALLIES.vouchers.of = G.DISCOVER_TALLIES.vouchers.of+1 + if v.discovered then + G.DISCOVER_TALLIES.vouchers.tally = G.DISCOVER_TALLIES.vouchers.tally+1 + end + end + if v.set and v.set == 'Booster' then + G.DISCOVER_TALLIES.boosters.of = G.DISCOVER_TALLIES.boosters.of+1 + if v.discovered then + G.DISCOVER_TALLIES.boosters.tally = G.DISCOVER_TALLIES.boosters.tally+1 + end + end + if v.set and v.set == 'Edition' then + G.DISCOVER_TALLIES.editions.of = G.DISCOVER_TALLIES.editions.of+1 + if v.discovered then + G.DISCOVER_TALLIES.editions.tally = G.DISCOVER_TALLIES.editions.tally+1 + end + end + end + end + for _, v in pairs(G.P_BLINDS) do + G.DISCOVER_TALLIES.total.of = G.DISCOVER_TALLIES.total.of+1 + G.DISCOVER_TALLIES.blinds.of = G.DISCOVER_TALLIES.blinds.of+1 + if v.discovered then + G.DISCOVER_TALLIES.blinds.tally = G.DISCOVER_TALLIES.blinds.tally+1 + G.DISCOVER_TALLIES.total.tally = G.DISCOVER_TALLIES.total.tally+1 + end + end + for _, v in pairs(G.P_TAGS) do + G.DISCOVER_TALLIES.total.of = G.DISCOVER_TALLIES.total.of+1 + G.DISCOVER_TALLIES.tags.of = G.DISCOVER_TALLIES.tags.of+1 + if v.discovered then + G.DISCOVER_TALLIES.tags.tally = G.DISCOVER_TALLIES.tags.tally+1 + G.DISCOVER_TALLIES.total.tally = G.DISCOVER_TALLIES.total.tally+1 + end + end + G.PROFILES[G.SETTINGS.profile].high_scores.collection.amt = G.DISCOVER_TALLIES.total.tally + G.PROFILES[G.SETTINGS.profile].high_scores.collection.tot = G.DISCOVER_TALLIES.total.of + G.PROFILES[G.SETTINGS.profile].progress.discovered = copy_table(G.DISCOVER_TALLIES.total) + + if check_for_unlock then check_for_unlock({type = 'discover_amount', + amount = G.DISCOVER_TALLIES.total.tally, + planet_count = G.DISCOVER_TALLIES.planets.tally, + tarot_count = G.DISCOVER_TALLIES.tarots.tally}) + end +end + +function stop_use() + G.GAME.STOP_USE = (G.GAME.STOP_USE or 0) + 1 + dec_stop_use(6) +end + +function dec_stop_use(_depth) + if _depth > 0 then + G.E_MANAGER:add_event(Event({ + blocking = false, + no_delete = true, + func = (function() + dec_stop_use(_depth - 1) + return true end)})) + else + G.E_MANAGER:add_event(Event({ + blocking = false, + no_delete = true, + func = (function() + G.GAME.STOP_USE = math.max(G.GAME.STOP_USE - 1, 0) + return true end)})) + end +end + +function inc_career_stat(stat, mod) + if G.GAME.seeded or G.GAME.challenge then return end + if not G.PROFILES[G.SETTINGS.profile].career_stats[stat] then G.PROFILES[G.SETTINGS.profile].career_stats[stat] = 0 end + G.PROFILES[G.SETTINGS.profile].career_stats[stat] = G.PROFILES[G.SETTINGS.profile].career_stats[stat] + (mod or 0) + G:save_settings() +end + +function recursive_table_cull(t) + local ret_t = {} + for k, v in pairs(t) do + if type(v) == 'table' then + if v.is and v:is(Object) then ret_t[k] = [["]].."MANUAL_REPLACE"..[["]] + else ret_t[k] = recursive_table_cull(v) + end + else ret_t[k] = v end + end + return ret_t +end + +function save_with_action(action) + G.action = action + save_run() + G.action = nil +end + +function save_run() + if G.F_NO_SAVING == true then return end + local cardAreas = {} + for k, v in pairs(G) do + if (type(v) == "table") and v.is and v:is(CardArea) then + local cardAreaSer = v:save() + if cardAreaSer then cardAreas[k] = cardAreaSer end + end + end + + local tags = {} + for k, v in ipairs(G.GAME.tags) do + if (type(v) == "table") and v.is and v:is(Tag) then + local tagSer = v:save() + if tagSer then tags[k] = tagSer end + end + end + + G.culled_table = recursive_table_cull{ + cardAreas = cardAreas, + tags = tags, + GAME = G.GAME, + STATE = G.STATE, + ACTION = G.action or nil, + BLIND = G.GAME.blind:save(), + BACK = G.GAME.selected_back:save(), + VERSION = G.VERSION + } + G.ARGS.save_run = G.culled_table + + G.FILE_HANDLER = G.FILE_HANDLER or {} + G.FILE_HANDLER.run = true + G.FILE_HANDLER.update_queued = true +end + +function remove_save() + love.filesystem.remove(G.SETTINGS.profile..'/save.jkr') + G.SAVED_GAME = nil + G.FILE_HANDLER.run = nil +end + +function loc_colour(_c, _default) + G.ARGS.LOC_COLOURS = G.ARGS.LOC_COLOURS or { + red = G.C.RED, + mult = G.C.MULT, + blue = G.C.BLUE, + chips = G.C.CHIPS, + green = G.C.GREEN, + money = G.C.MONEY, + gold = G.C.GOLD, + attention = G.C.FILTER, + purple = G.C.PURPLE, + white = G.C.WHITE, + inactive = G.C.UI.TEXT_INACTIVE, + spades = G.C.SUITS.Spades, + hearts = G.C.SUITS.Hearts, + clubs = G.C.SUITS.Clubs, + diamonds = G.C.SUITS.Diamonds, + tarot = G.C.SECONDARY_SET.Tarot, + planet = G.C.SECONDARY_SET.Planet, + spectral = G.C.SECONDARY_SET.Spectral, + edition = G.C.EDITION, + dark_edition = G.C.DARK_EDITION, + legendary = G.C.RARITY[4], + enhanced = G.C.SECONDARY_SET.Enhanced + } + for _, v in ipairs(SMODS.Rarity.obj_buffer) do + G.ARGS.LOC_COLOURS[v:lower()] = G.C.RARITY[v] + end + for _, v in ipairs(SMODS.ConsumableType.ctype_buffer) do + G.ARGS.LOC_COLOURS[v:lower()] = G.C.SECONDARY_SET[v] + end + for _, v in ipairs(SMODS.Suit.obj_buffer) do + G.ARGS.LOC_COLOURS[v:lower()] = G.C.SUITS[v] + end + return G.ARGS.LOC_COLOURS[_c] or _default or G.C.UI.TEXT_DARK +end + +function init_localization() + G.localization.misc.v_dictionary_parsed = {} + for k, v in pairs(G.localization.misc.v_dictionary) do + if type(v) == 'table' then + G.localization.misc.v_dictionary_parsed[k] = {multi_line = true} + for kk, vv in ipairs(v) do + G.localization.misc.v_dictionary_parsed[k][kk] = loc_parse_string(vv) + end + else + G.localization.misc.v_dictionary_parsed[k] = loc_parse_string(v) + end + end + G.localization.misc.v_text_parsed = {} + for k, v in pairs(G.localization.misc.v_text) do + G.localization.misc.v_text_parsed[k] = {} + for kk, vv in ipairs(v) do + G.localization.misc.v_text_parsed[k][kk] = loc_parse_string(vv) + end + end + G.localization.tutorial_parsed = {} + for k, v in pairs(G.localization.misc.tutorial) do + G.localization.tutorial_parsed[k] = {multi_line = true} + for kk, vv in ipairs(v) do + G.localization.tutorial_parsed[k][kk] = loc_parse_string(vv) + end + end + G.localization.quips_parsed = {} + for k, v in pairs(G.localization.misc.quips or {}) do + G.localization.quips_parsed[k] = {multi_line = true} + for kk, vv in ipairs(v) do + G.localization.quips_parsed[k][kk] = loc_parse_string(vv) + end + end + for g_k, group in pairs(G.localization) do + if g_k == 'descriptions' then + for _, set in pairs(group) do + for _, center in pairs(set) do + center.text_parsed = {} + if not center.text then else + for _, line in ipairs(center.text) do + center.text_parsed[#center.text_parsed+1] = loc_parse_string(line) + end + center.name_parsed = {} + for _, line in ipairs(type(center.name) == 'table' and center.name or {center.name}) do + center.name_parsed[#center.name_parsed+1] = loc_parse_string(line) + end + if center.unlock then + center.unlock_parsed = {} + for _, line in ipairs(center.unlock) do + center.unlock_parsed[#center.unlock_parsed+1] = loc_parse_string(line) + end + end + end + end + end + end + end +end + +function playing_card_joker_effects(cards) + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({playing_card_added = true, cards = cards}) + end +end + +function convert_save_to_meta() + if love.filesystem.getInfo(G.SETTINGS.profile..'/'..'unlocked_jokers.jkr') then + local _meta = { + unlocked = {}, + alerted = {}, + discovered = {} + } + if love.filesystem.getInfo(G.SETTINGS.profile..'/'..'unlocked_jokers.jkr') then + for line in string.gmatch( (get_compressed(G.SETTINGS.profile..'/'..'unlocked_jokers.jkr') or '').. "\n", "([^\n]*)\n") do + local key = line:gsub("%s+", "") + if key and (key ~= '') then + _meta.unlocked[key] = true + end + end + end + if love.filesystem.getInfo(G.SETTINGS.profile..'/'..'discovered_jokers.jkr') then + for line in string.gmatch( (get_compressed(G.SETTINGS.profile..'/'..'discovered_jokers.jkr') or '').. "\n", "([^\n]*)\n") do + local key = line:gsub("%s+", "") + if key and (key ~= '') then + _meta.discovered[key] = true + end + end + end + if love.filesystem.getInfo(G.SETTINGS.profile..'/'..'alerted_jokers.jkr') then + for line in string.gmatch( (get_compressed(G.SETTINGS.profile..'/'..'alerted_jokers.jkr') or '').. "\n", "([^\n]*)\n") do + local key = line:gsub("%s+", "") + if key and (key ~= '') then + _meta.alerted[key] = true + end + end + end + love.filesystem.remove(G.SETTINGS.profile..'/'..'unlocked_jokers.jkr') + love.filesystem.remove(G.SETTINGS.profile..'/'..'discovered_jokers.jkr') + love.filesystem.remove(G.SETTINGS.profile..'/'..'alerted_jokers.jkr') + + compress_and_save( G.SETTINGS.profile..'/'..'meta.jkr', STR_PACK(_meta)) + end +end + +function card_from_control(control) + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local _card = Card(G.deck.T.x, G.deck.T.y, G.CARD_W, G.CARD_H, G.P_CARDS[control.s..'_'..control.r], G.P_CENTERS[control.e or 'c_base'], {playing_card = G.playing_card}) + if control.d then _card:set_edition({[control.d] = true}, true, true) end + if control.g then _card:set_seal(control.g, true, true) end + G.deck:emplace(_card) + table.insert(G.playing_cards, _card) +end + +function loc_parse_string(line) + local parsed_line = {} + local control = {} + local _c, _c_name, _c_val, _c_gather = nil, nil, nil, nil + local _s_gather, _s_ref = nil, nil + local str_parts, str_it = {}, 1 + for i = 1, #line do + local char = line:sub(i,i) + if char == '{' then --Start of a control section, extract all controls + if str_parts[1] then parsed_line[#parsed_line+1] = {strings = str_parts, control = control or {}} end + str_parts, str_it = {}, 1 + control, _c, _c_name, _c_val, _c_gather = {}, nil, nil, nil, nil + _s_gather, _s_ref = nil, nil + _c = true + elseif _c and not (char == ':' or char == '}') and not _c_gather then _c_name = (_c_name or '')..char + elseif _c and char == ':' then _c_gather = true + elseif _c and not (char == ',' or char == '}') and _c_gather then _c_val = (_c_val or '')..char + elseif _c and (char == ',' or char == '}') then _c_gather = nil; if _c_name then control[_c_name] = _c_val end; _c_name = nil; _c_val = nil; if char == '}' then _c = nil end + + elseif not _c and char ~= '#' and not _s_gather then str_parts[str_it] = (str_parts[str_it] or '')..(control['X'] and char:gsub("%s+", "") or char) + elseif not _c and char == '#' and not _s_gather then _s_gather = true; if str_parts[str_it] then str_it = str_it + 1 end + elseif not _c and char == '#' and _s_gather then _s_gather = nil; if _s_ref then str_parts[str_it] = {_s_ref}; str_it = str_it + 1; _s_ref = nil end + elseif not _c and _s_gather then _s_ref = (_s_ref or '')..char + end + if i == #line then + if str_parts[1] then parsed_line[#parsed_line+1] = {strings = str_parts, control = control or {}} end + return parsed_line + end + end +end + +--UTF8 handler for special characters, from https://github.com/blitmap/lua-utf8-simple +utf8 = {pattern = '[%z\1-\127\194-\244][\128-\191]*'} +utf8.map = + function (s, f, no_subs) + local i = 0 + + if no_subs then + for b, e in s:gmatch('()' .. utf8.pattern .. '()') do + i = i + 1 + local c = e - b + f(i, c, b) + end + else + for b, c in s:gmatch('()(' .. utf8.pattern .. ')') do + i = i + 1 + f(i, c, b) + end + end + end +utf8.chars = + function (s, no_subs) + return coroutine.wrap(function () return utf8.map(s, coroutine.yield, no_subs) end) + end + +function localize(args, misc_cat) +if args and args.vars then + local reset = {} + for i, j in pairs(args.vars) do + if type(j) == 'table' then + if (j.new and type(j.new) == "function") and ((j.m and j.e) or (j.array and j.sign and (type(j.array) == "table"))) then + reset[i] = number_format(j) + end + end + end + for i, j in pairs(reset) do + args.vars[i] = j + end +end + if args and not (type(args) == 'table') then + if misc_cat and G.localization.misc[misc_cat] then return G.localization.misc[misc_cat][args] or 'ERROR' end + return G.localization.misc.dictionary[args] or 'ERROR' + end + + local loc_target = nil + local ret_string = nil + if args.type == 'other' then + loc_target = G.localization.descriptions.Other[args.key] + elseif args.type == 'descriptions' or args.type == 'unlocks' then + loc_target = G.localization.descriptions[args.set][args.key] + elseif args.type == 'tutorial' then + loc_target = G.localization.tutorial_parsed[args.key] + elseif args.type == 'quips' then + loc_target = G.localization.quips_parsed[args.key] + elseif args.type == 'raw_descriptions' then + loc_target = G.localization.descriptions[args.set][args.key] + local multi_line = {} + if loc_target then + for _, lines in ipairs(args.type == 'unlocks' and loc_target.unlock_parsed or args.type == 'name' and loc_target.name_parsed or args.type == 'text' and loc_target or loc_target.text_parsed) do + local final_line = '' + for _, part in ipairs(lines) do + local assembled_string = '' + for _, subpart in ipairs(part.strings) do + assembled_string = assembled_string..(type(subpart) == 'string' and subpart or format_ui_value(args.vars[tonumber(subpart[1])]) or 'ERROR') + end + final_line = final_line..assembled_string + end + multi_line[#multi_line+1] = final_line + end + end + return multi_line + elseif args.type == 'text' then + loc_target = G.localization.misc.v_text_parsed[args.key] + elseif args.type == 'variable' then + loc_target = G.localization.misc.v_dictionary_parsed[args.key] + if not loc_target then return 'ERROR' end + if loc_target.multi_line then + local assembled_strings = {} + for k, v in ipairs(loc_target) do + local assembled_string = '' + for _, subpart in ipairs(v[1].strings) do + assembled_string = assembled_string..(type(subpart) == 'string' and subpart or format_ui_value(args.vars[tonumber(subpart[1])])) + end + assembled_strings[k] = assembled_string + end + return assembled_strings or {'ERROR'} + else + local assembled_string = '' + for _, subpart in ipairs(loc_target[1].strings) do + assembled_string = assembled_string..(type(subpart) == 'string' and subpart or format_ui_value(args.vars[tonumber(subpart[1])])) + end + ret_string = assembled_string or 'ERROR' + end + elseif args.type == 'name_text' then + if pcall(function() ret_string = G.localization.descriptions[(args.set or args.node.config.center.set)][args.key or args.node.config.center.key].name end) then + else ret_string = "ERROR" end + elseif args.type == 'name' then + loc_target = G.localization.descriptions[(args.set or args.node.config.center.set)][args.key or args.node.config.center.key] + end + + if ret_string then return ret_string end + + if loc_target then + for _, lines in ipairs(args.type == 'unlocks' and loc_target.unlock_parsed or args.type == 'name' and loc_target.name_parsed or (args.type == 'text' or args.type == 'tutorial' or args.type == 'quips') and loc_target or loc_target.text_parsed) do + local final_line = {} + for _, part in ipairs(lines) do + local assembled_string = '' + for _, subpart in ipairs(part.strings) do + assembled_string = assembled_string..(type(subpart) == 'string' and subpart or format_ui_value(args.vars[tonumber(subpart[1])]) or 'ERROR') + end + local desc_scale = G.LANG.font.DESCSCALE + if G.F_MOBILE_UI then desc_scale = desc_scale*1.5 end + if args.type == 'name' then + final_line[#final_line+1] = {n=G.UIT.O, config={ + object = DynaText({string = {assembled_string}, + colours = {(part.control.V and args.vars.colours[tonumber(part.control.V)]) or (part.control.C and loc_colour(part.control.C)) or args.text_colour or G.C.UI.TEXT_LIGHT}, + bump = true, + silent = true, + pop_in = 0, + pop_in_rate = 4, + maxw = 5, + shadow = true, + y_offset = -0.6, + spacing = math.max(0, 0.32*(17 - #assembled_string)), + scale = (0.55 - 0.004*#assembled_string)*(part.control.s and tonumber(part.control.s) or args.scale or 1) + }) + }} + elseif part.control.E then + local _float, _silent, _pop_in, _bump, _spacing = nil, true, nil, nil, nil + if part.control.E == '1' then + _float = true; _silent = true; _pop_in = 0 + elseif part.control.E == '2' then + _bump = true; _spacing = 1 + end + final_line[#final_line+1] = {n=G.UIT.O, config={ + object = DynaText({string = {assembled_string}, colours = {part.control.V and args.vars.colours[tonumber(part.control.V)] or loc_colour(part.control.C or nil)}, + float = _float, + silent = _silent, + pop_in = _pop_in, + bump = _bump, + spacing = _spacing, + scale = 0.32*(part.control.s and tonumber(part.control.s) or args.scale or 1)*desc_scale}) + }} + elseif part.control.X then + final_line[#final_line+1] = {n=G.UIT.C, config={align = "m", colour = loc_colour(part.control.X), r = 0.05, padding = 0.03, res = 0.15}, nodes={ + {n=G.UIT.T, config={ + text = assembled_string, + colour = loc_colour(part.control.C or nil), + scale = 0.32*(part.control.s and tonumber(part.control.s) or args.scale or 1)*desc_scale}}, + }} + else + final_line[#final_line+1] = {n=G.UIT.T, config={ + detailed_tooltip = part.control.T and (G.P_CENTERS[part.control.T] or G.P_TAGS[part.control.T]) or nil, + text = assembled_string, + shadow = args.shadow, + colour = part.control.V and args.vars.colours[tonumber(part.control.V)] or not part.control.C and args.text_colour or loc_colour(part.control.C or nil, args.default_col), + scale = 0.32*(part.control.s and tonumber(part.control.s) or args.scale or 1)*desc_scale},} + end + end + if args.type == 'name' or args.type == 'text' then return final_line end + args.nodes[#args.nodes+1] = final_line + end + end +end + +function get_stake_sprite(_stake, _scale) + _stake = _stake or 1 + _scale = _scale or 1 + local stake_sprite = Sprite(0,0,_scale*1,_scale*1,G.ASSET_ATLAS[G.P_CENTER_POOLS.Stake[_stake].atlas], G.P_CENTER_POOLS.Stake[_stake].pos) + stake_sprite.states.drag.can = false + if G.P_CENTER_POOLS['Stake'][_stake].shiny then + stake_sprite.draw = function(_sprite) + _sprite.ARGS.send_to_shader = _sprite.ARGS.send_to_shader or {} + _sprite.ARGS.send_to_shader[1] = math.min(_sprite.VT.r*3, 1) + G.TIMERS.REAL/(18) + (_sprite.juice and _sprite.juice.r*20 or 0) + 1 + _sprite.ARGS.send_to_shader[2] = G.TIMERS.REAL + + Sprite.draw_shader(_sprite, 'dissolve') + Sprite.draw_shader(_sprite, 'voucher', nil, _sprite.ARGS.send_to_shader) + end + end + return stake_sprite +end + +function get_front_spriteinfo(_front) + if _front and _front.suit and G.SETTINGS.CUSTOM_DECK and G.SETTINGS.CUSTOM_DECK.Collabs then + local collab = G.SETTINGS.CUSTOM_DECK.Collabs[_front.suit] + if collab and collab ~= 'default' then + local deckSkin = SMODS.DeckSkins[collab] + if deckSkin then + local hasRank = false + for i = 1, #deckSkin.ranks do + if deckSkin.ranks[i] == _front.value then hasRank = true break end + end + if hasRank then + local atlas = G.ASSET_ATLAS[G.SETTINGS.colourblind_option and deckSkin.hc_atlas or deckSkin.lc_atlas] + if atlas then + if deckSkin.posStyle == 'collab' then + return atlas, G.COLLABS.pos[_front.value] + elseif deckSkin.posStyle == 'suit' then + return atlas, { x = _front.pos.x, y = 0} + elseif deckSkin.posStyle == 'deck' then + return atlas, _front.pos + end + end + end + end + end +end + + return G.ASSET_ATLAS[G.SETTINGS.colourblind_option and _front.hc_atlas or _front.lc_atlas or {}] or G.ASSET_ATLAS[_front.atlas] or G.ASSET_ATLAS["cards_"..(G.SETTINGS.colourblind_option and 2 or 1)], _front.pos +end + +function get_stake_col(_stake) + G.C.STAKES = G.C.STAKES or { + G.C.WHITE, + G.C.RED, + G.C.GREEN, + G.C.BLACK, + G.C.BLUE, + G.C.PURPLE, + G.C.ORANGE, + G.C.GOLD + } + return G.C.STAKES[_stake] +end + +function get_challenge_int_from_id(_id) + for k, v in pairs(G.CHALLENGES) do + if v.id == _id then return k end + end + return 0 +end + +function get_starting_params() +return { + dollars = 4, + hand_size = 8, + discards = 3, + hands = 4, + reroll_cost = 5, + joker_slots = 5, + ante_scaling = 1, + consumable_slots = 2, + no_faces = false, + erratic_suits_and_ranks = false, + } +end + +function get_challenge_rule(_challenge, _type, _id) + if _challenge and _challenge.rules and _challenge.rules[_type] then + for k, v in ipairs(_challenge.rules[_type]) do + if _id == v.id then return v.value end + end + end +end + +--SOUND +function PLAY_SOUND(args) + args.per = args.per or 1 + args.vol = args.vol or 1 + SOURCES[args.sound_code] = SOURCES[args.sound_code] or {} + + local should_stream = (string.find(args.sound_code,'music') or string.find(args.sound_code,'ambient')) + local s = {sound = love.audio.newSource("resources/sounds/"..args.sound_code..'.ogg', should_stream and "stream" or 'static')} + table.insert(SOURCES[args.sound_code], s) + s.sound_code = args.sound_code + s.original_pitch = args.per or 1 + s.original_volume = args.vol or 1 + s.created_on_pause = (args.overlay_menu and true or false) + s.created_on_state = args.state + s.sfx_handled = 0 + s.transition_timer = 0 + SET_SFX(s, args) + love.audio.play(s.sound) + return s +end + +function STOP_AUDIO() + for _, source in pairs(SOURCES) do + for _, s in pairs(source) do + if s.sound:isPlaying() then + s.sound:stop() + end + end + end +end + +function SET_SFX(s, args) + if string.find(s.sound_code,'music') then + if s.sound_code == args.desired_track then + s.current_volume = s.current_volume or 1 + s.current_volume = 1*(args.dt*3) + (1-(args.dt*3))*s.current_volume + else + s.current_volume = s.current_volume or 0 + s.current_volume = 0*(args.dt*3) + (1-(args.dt*3))*s.current_volume + end + s.sound:setVolume(s.current_volume*s.original_volume*(args.sound_settings.volume/100.0)*(args.sound_settings.music_volume/100.0)) + s.sound:setPitch(s.original_pitch*args.pitch_mod) + else + if s.temp_pitch ~= s.original_pitch then + s.sound:setPitch(s.original_pitch) + s.temp_pitch = s.original_pitch + end + local sound_vol = s.original_volume*(args.sound_settings.volume/100.0)*(args.sound_settings.game_sounds_volume/100.0) + if s.created_on_state == 13 then sound_vol = sound_vol*args.splash_vol end + if sound_vol <= 0 then + s.sound:stop() + else + s.sound:setVolume(sound_vol) + end + end +end + +function MODULATE(args) + for k, v in pairs(SOURCES) do + if (string.find(k,'music') and (args.desired_track ~= '')) then + if v[1] and v[1].sound and v[1].sound:isPlaying() then + else + RESTART_MUSIC(args) + break; + end + end + end + + for k, v in pairs(SOURCES) do + local i=1 + while i <= #v do + if not v[i].sound:isPlaying() then + table.remove(v, i) + else + i = i + 1 + end + end + + for i, s in ipairs(v) do + if s.sound and s.sound:isPlaying() and s.original_volume then + SET_SFX(s, args) + end + end + end +end + +function RESTART_MUSIC(args) + for k, v in pairs(SOURCES) do + if string.find(k,'music') then + for i, s in ipairs(v) do + s.sound:stop() + end + SOURCES[k] = {} + args.per = 0.7 + args.vol = 0.6 + args.sound_code = k + local s = PLAY_SOUND(args) + s.initialized = true + end + end +end + +function AMBIENT(args) + for k, v in pairs(SOURCES) do + if args.ambient_control[k] then + local start_ambient = args.ambient_control[k].vol > 0 + + for i, s in ipairs(v) do + if s.sound and s.sound:isPlaying() and s.original_volume then + s.original_volume = args.ambient_control[k].vol + SET_SFX(s, args) + start_ambient = false + end + end + + if start_ambient then + args.sound_code = k + args.vol = args.ambient_control[k].vol + args.per = args.ambient_control[k].per + PLAY_SOUND(args) + end + end + end +end + +function RESET_STATES(state) + for k, v in pairs(SOURCES) do + for i, s in ipairs(v) do + s.created_on_state = state + end + end +end diff --git a/lovely/dump/functions/state_events.lua b/lovely/dump/functions/state_events.lua new file mode 100644 index 0000000..efe3b58 --- /dev/null +++ b/lovely/dump/functions/state_events.lua @@ -0,0 +1,2459 @@ +LOVELY_INTEGRITY = 'b4654caa2a691eb71616565578a811b620a40e430e1f2ea8525b31ba25c1f8e1' + +function win_game() + if not G.GAME.seeded and not G.GAME.challenge then + set_joker_win() + set_deck_win() + + check_and_set_high_score('win_streak', G.PROFILES[G.SETTINGS.profile].high_scores.current_streak.amt+1) + check_and_set_high_score('current_streak', G.PROFILES[G.SETTINGS.profile].high_scores.current_streak.amt+1) + check_for_unlock({type = 'win_no_hand'}) + check_for_unlock({type = 'win_no'}) + check_for_unlock({type = 'win_custom'}) + check_for_unlock({type = 'win_deck'}) + check_for_unlock({type = 'win_stake'}) + check_for_unlock({type = 'win'}) + inc_career_stat('c_wins', 1) + end + + set_profile_progress() + + if G.GAME.challenge then + G.PROFILES[G.SETTINGS.profile].challenge_progress.completed[G.GAME.challenge] = true + set_challenge_unlock() + check_for_unlock({type = 'win_challenge'}) + G:save_settings() + end + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + for k, v in pairs(G.I.CARD) do + v.sticker_run = nil + end + + play_sound('win') + G.SETTINGS.paused = true + + G.FUNCS.overlay_menu{ + definition = create_UIBox_win(), + config = {no_esc = true} + } + local Jimbo = nil + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 2.5, + blocking = false, + func = (function() + if G.OVERLAY_MENU and G.OVERLAY_MENU:get_UIE_by_ID('jimbo_spot') then + Jimbo = Card_Character({x = 0, y = 5}) + local spot = G.OVERLAY_MENU:get_UIE_by_ID('jimbo_spot') + spot.config.object:remove() + spot.config.object = Jimbo + Jimbo.ui_object_updated = true + Jimbo:add_speech_bubble('wq_'..math.random(1,7), nil, {quip = true}) + Jimbo:say_stuff(5) + if G.F_JAN_CTA then + G.E_MANAGER:add_event(Event({ + func = function() + Jimbo:add_button(localize('b_wishlist'), 'wishlist_steam', G.C.DARK_EDITION, nil, true, 1.6) + return true + end})) + end + end + return true + end) + })) + + return true + end) + })) + + if not G.GAME.seeded and not G.GAME.challenge then + G.PROFILES[G.SETTINGS.profile].stake = math.max(G.PROFILES[G.SETTINGS.profile].stake or 1, (G.GAME.stake or 1)+1) + end + G:save_progress() + G.FILE_HANDLER.force = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + if not G.SETTINGS.paused then + G.GAME.current_round.round_text = 'Endless Round ' + return true + end + end) + })) +end + +function end_round() +G.GAME.cry_exploit_override = nil + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.2, + func = function() + G.GAME.blind.in_blind = false + local game_over = true + local game_won = false + G.RESET_BLIND_STATES = true + G.RESET_JIGGLES = true + if G.GAME.current_round.semicolon then + game_over = false + end + if to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) then + game_over = false + end + for i = 1, #G.playing_cards do + local CARD = G.playing_cards[i] + CARD.cry_debuff_immune = false + end + for i = 1, #G.jokers.cards do + local eval = nil + eval = G.jokers.cards[i]:calculate_joker({end_of_round = true, game_over = game_over, callback = function(card, eval) + if eval then + if eval.saved then + game_over = false + end + card_eval_status_text(card, 'jokers', nil, nil, nil, eval) + end + end}) + + G.jokers.cards[i]:calculate_rental() + G.jokers.cards[i]:calculate_perishable() + end + if G.GAME.voucher_sticker_index then + if G.GAME.voucher_sticker_index.perishable then + for k, v in pairs(G.GAME.voucher_sticker_index.perishable) do + if v > 1 then + G.GAME.voucher_sticker_index.perishable[k] = v - 1 + end + if v == 1 then + G.GAME.voucher_sticker_index.perishable[k] = v - 1 + for kk, vv in pairs(G.P_CENTERS) do + if k == vv.name then + cry_debuff_voucher(kk) + G.GAME.used_vouchers.vv = nil + G.GAME.used_vouchers[kk] = nil + break + end + end + end + end + end + if G.GAME.voucher_sticker_index.rental then + local cumulative_rental = 0 + for k, v in pairs(G.GAME.voucher_sticker_index.rental) do + cumulative_rental = cumulative_rental + G.GAME.cry_voucher_rental_rate + end + if cumulative_rental > 0 then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + blocking = false, + blockable = false, + func = (function() + ease_dollars(-cumulative_rental) + return true + end)})) + end + end + if G.GAME.voucher_sticker_index.banana then -- i'm pretty sure unredeem breaks if spectrals are disabled? unsure though + local voucher_queue = {} + local current_round_voucher=G.GAME.current_round.voucher + for k, v in pairs(G.GAME.voucher_sticker_index.banana) do + if (pseudorandom('byebyevoucher') < G.GAME.probabilities.normal/G.GAME.cry_voucher_banana_odds) then + area = G.play + local unredeemed_voucher = '' + for kk, vv in pairs(G.P_CENTERS) do + if k == vv.name then + unredeemed_voucher = kk + break + end + end + local card = create_card('Voucher', area, nil, nil, nil, nil, unredeemed_voucher) + if G.GAME.voucher_edition_index[card.ability.name] then -- i made this bullshit a function + local edition = cry_edition_to_table(G.GAME.voucher_edition_index[card.ability.name]) + if edition then + card:set_edition(edition, true, true) + end + end + if G.GAME.voucher_sticker_index.eternal[card.ability.name] then + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.voucher_sticker_index.perishable[card.ability.name] then + card:set_perishable(true) + card.ability.perish_tally = G.GAME.voucher_sticker_index.perishable[card.ability.name] + card.ability.perishable = true + if G.GAME.voucher_sticker_index.perishable[card.ability.name] == 0 then + card.debuff = true + end + end + if G.GAME.voucher_sticker_index.rental[card.ability.name] then + card:set_rental(true) + card.ability.rental = true + end + if G.GAME.voucher_sticker_index.pinned[card.ability.name] then + card.pinned = true + end + if G.GAME.voucher_sticker_index.banana[card.ability.name] then + card.ability.banana = true + end + card:start_materialize() + area:emplace(card) + card.cost=0 + card.shop_voucher=false + voucher_queue[#voucher_queue+1] = card + end + end + for k, v in pairs(voucher_queue) do + v:unredeem() + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0, + func = function() + v:start_dissolve() + return true + end})) + end + G.GAME.current_round.voucher=current_round_voucher + end + end + local i = 1 + while i <= #G.jokers.cards do + local gone = G.jokers.cards[i]:calculate_banana() + if not gone then i = i + 1 end + end + i = 1 + while i <= #G.consumeables.cards do + G.consumeables.cards[i]:cry_calculate_consumeable_rental() + G.consumeables.cards[i]:cry_calculate_consumeable_perishable() + local gone = nil + if not gone then i = i + 1 end + end + if G.GAME.round_resets.ante >= G.GAME.win_ante and G.GAME.blind_on_deck == 'Boss' then + game_won = true + G.GAME.won = true + end + if game_over then + G.STATE = G.STATES.GAME_OVER + if not G.GAME.won and not G.GAME.seeded and not G.GAME.challenge then + G.PROFILES[G.SETTINGS.profile].high_scores.current_streak.amt = 0 + end + G:save_settings() + G.FILE_HANDLER.force = true + G.STATE_COMPLETE = false + else + G.GAME.unused_discards = (G.GAME.unused_discards or 0) + G.GAME.current_round.discards_left + if G.GAME.blind and G.GAME.blind.config.blind then + discover_card(G.GAME.blind.config.blind) + end + + if G.GAME.blind_on_deck == 'Boss' then + local _handname, _played, _order = 'High Card', -1, 100 + for k, v in pairs(G.GAME.hands) do + if v.played > _played or (v.played == _played and _order > v.order) then + _played = v.played + _handname = k + end + end + G.GAME.current_round.most_played_poker_hand = _handname + end + + if G.GAME.blind:get_type() == 'Boss' and not G.GAME.seeded and not G.GAME.challenge then + G.GAME.current_boss_streak = G.GAME.current_boss_streak + 1 + check_and_set_high_score('boss_streak', G.GAME.current_boss_streak) + end + + if G.GAME.current_round.hands_played == 1 then + inc_career_stat('c_single_hand_round_streak', 1) + else + if not G.GAME.seeded and not G.GAME.challenge then + G.PROFILES[G.SETTINGS.profile].career_stats.c_single_hand_round_streak = 0 + G:save_settings() + end + end + + check_for_unlock({type = 'round_win'}) + set_joker_usage() + if game_won and not G.GAME.win_notified then + G.GAME.win_notified = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + blocking = false, + blockable = false, + func = (function() + if G.STATE == G.STATES.ROUND_EVAL then + win_game() + G.GAME.won = true + return true + end + end) + })) + end + if scoring_hand then + local unscoring_hand = {} + for i = 1, #G.play.cards do + local is_scoring = false + for j = 1, #scoring_hand do + if G.play.cards[i] == scoring_hand[j] then + is_scoring = true + end + end + if not is_scoring then + unscoring_hand[#unscoring_hand+1] = G.play.cards[i] + end + end + for i = 1, #unscoring_hand do + unscoring_hand[i]:calculate_seal{unscoring = true} + end + end + for i=1, #G.hand.cards do + --Check for hand doubling + local reps = {1} + local j = 1 + while j <= #reps do + local percent = (i-0.999)/(#G.hand.cards-0.998) + (j-1)*0.1 + if reps[j] ~= 1 then card_eval_status_text((reps[j].jokers or reps[j].seals).card, 'jokers', nil, nil, nil, (reps[j].jokers or reps[j].seals)) end + + --calculate the hand effects + local effects = {G.hand.cards[i]:get_end_of_round_effect()} + G.hand.cards[i]:calculate_rental() + G.hand.cards[i]:calculate_perishable() + for k=1, #G.jokers.cards do + --calculate the joker individual card effects + local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.hand, other_card = G.hand.cards[i], individual = true, end_of_round = true, callback = function(card, eval, retrigger) + if eval then + table.insert(effects, eval) +effects[#effects].from_retrigger = retrigger +end end, no_retrigger_anim = true}) + + end + + if reps[j] == 1 then + --Check for hand doubling + --From Red seal + local eval = eval_card(G.hand.cards[i], {end_of_round = true,cardarea = G.hand, repetition = true, repetition_only = true}) + if next(eval) and (next(effects[1]) or #effects > 1) then + for h = 1, eval.seals.repetitions do + reps[#reps+1] = eval + end + end + + --from Jokers + for j=1, #G.jokers.cards do + --calculate the joker effects + local eval = eval_card(G.jokers.cards[j], {cardarea = G.hand, other_card = G.hand.cards[i], repetition = true, end_of_round = true, card_effects = effects, callback = function(card, ret) eval = {jokers = ret} + if next(eval) then + for h = 1, eval.jokers.repetitions do + reps[#reps+1] = eval + end + end end}) + end + end + + for ii = 1, #effects do + --if this effect came from a joker + if effects[ii].card and not Talisman.config_file.disable_anims then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() effects[ii].card:juice_up(0.7);return true end) + })) + end + + --If dollars + if effects[ii].h_dollars then + ease_dollars(effects[ii].h_dollars) + card_eval_status_text(G.hand.cards[i], 'dollars', effects[ii].h_dollars, percent) + end + + --Any extras + if effects[ii].extra then + card_eval_status_text(G.hand.cards[i], 'extra', nil, percent, nil, effects[ii].extra) + end + end + j = j + 1 + end + end + delay(0.3) + + + local i = 1 + while i <= #G.hand.cards do + local gone = G.hand.cards[i]:calculate_banana() + if not gone then i = i + 1 end + end + for i = 1, #G.discard.cards do + G.discard.cards[i]:calculate_perishable() + end + i = 1 + while i <= #G.deck.cards do + G.deck.cards[i]:calculate_perishable() + local gone = G.deck.cards[i]:calculate_banana() + if not gone then i = i + 1 end + end + if G.GAME.used_vouchers.v_cry_double_down then + local function update_dbl(area) + for i = 1, #area.cards do + if area.cards[i].dbl_side then + --tweak to do deck effects with on the flip side + cry_misprintize(area.cards[i].dbl_side, {min = 1.5, max = 1.5}, nil, true) + card_eval_status_text(area.cards[i], "extra", nil, nil, nil, { message = localize("k_upgrade_ex") }) + end + end + end + update_dbl(G.jokers) + update_dbl(G.consumeables) + update_dbl(G.hand) + update_dbl(G.discard) + update_dbl(G.deck) + end + G.FUNCS.draw_from_hand_to_discard() + if G.GAME.blind_on_deck == 'Boss' then + G.GAME.voucher_restock = nil + if G.GAME.modifiers.set_eternal_ante and (G.GAME.round_resets.ante == G.GAME.modifiers.set_eternal_ante) then + for k, v in ipairs(G.jokers.cards) do + v:set_eternal(true) + end + end + if G.GAME.modifiers.set_joker_slots_ante and (G.GAME.round_resets.ante == G.GAME.modifiers.set_joker_slots_ante) then + G.jokers.config.card_limit = 0 + end + delay(0.4); ease_ante(G.GAME.blind and G.GAME.blind:cry_calc_ante_gain() or 1); cry_apply_ante_tax(); delay(0.4); check_for_unlock({type = 'ante_up', ante = G.GAME.round_resets.ante + 1}) + end + G.FUNCS.draw_from_discard_to_deck() + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.3, + func = function() + G.STATE = G.STATES.ROUND_EVAL + G.STATE_COMPLETE = false + + if G.GAME.blind_on_deck == 'Small' then + G.GAME.round_resets.blind_states.Small = 'Defeated' + elseif G.GAME.blind_on_deck == 'Big' then + G.GAME.round_resets.blind_states.Big = 'Defeated' + else + if G.GAME.current_round.cry_voucher_stickers.pinned == false then + if not G.GAME.modifiers.cry_no_vouchers then + if not G.GAME.modifiers.cry_voucher_restock_antes or G.GAME.round_resets.ante % G.GAME.modifiers.cry_voucher_restock_antes == 0 then + G.GAME.current_round.voucher = get_next_voucher_key() + end + else + very_fair_quip = pseudorandom_element(G.localization.misc.very_fair_quips, pseudoseed("cry_very_fair")) + end + G.GAME.current_round.cry_voucher_edition = cry_get_next_voucher_edition() + G.GAME.current_round.cry_voucher_stickers = cry_get_next_voucher_stickers() + end + G.GAME.round_resets.blind_states.Boss = 'Defeated' + for k, v in ipairs(G.playing_cards) do + v.ability.played_this_ante = nil + end + end + + if G.GAME.round_resets.temp_handsize then G.hand:change_size(-G.GAME.round_resets.temp_handsize); G.GAME.round_resets.temp_handsize = nil end + if G.GAME.round_resets.temp_reroll_cost then G.GAME.round_resets.temp_reroll_cost = nil; calculate_reroll_cost(true) end + for _, v in pairs(find_joker("cry-loopy")) do + if v.ability.extra.retrigger ~= 0 then + v.ability.extra.retrigger = 0 + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize("k_reset"), colour = G.C.GREEN}) + end + end + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({end_of_round2 = true}) + end + + reset_idol_card() + reset_mail_rank() + reset_ancient_card() + reset_castle_card() for _, mod in ipairs(SMODS.mod_list) do + if mod.reset_game_globals and type(mod.reset_game_globals) == 'function' then + mod.reset_game_globals(false) + end + end + for k, v in ipairs(G.playing_cards) do + v.ability.discarded = nil + v.ability.forced_selection = nil + end + return true + end + })) + end + return true + end + })) + end + +function new_round() + G.RESET_JIGGLES = nil + delay(0.4) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.GAME.current_round.discards_left = math.max(0, G.GAME.round_resets.discards + G.GAME.round_bonus.discards) + G.GAME.current_round.hands_left = (math.max(1, G.GAME.round_resets.hands + G.GAME.round_bonus.next_hands)) + G.GAME.current_round.hands_played = 0 + G.GAME.current_round.discards_used = 0 + G.GAME.current_round.reroll_cost_increase = 0 + G.GAME.current_round.used_packs = {} + + for k, v in pairs(G.GAME.hands) do + v.played_this_round = 0 + end + + for k, v in pairs(G.playing_cards) do + v.ability.wheel_flipped = nil + end + + local chaos = find_joker('Chaos the Clown') + G.GAME.current_round.free_rerolls = #chaos + calculate_reroll_cost(true) + + G.GAME.round_bonus.next_hands = 0 + G.GAME.round_bonus.discards = 0 + + local blhash = '' + if G.GAME.blind_on_deck == 'Small' then + G.GAME.round_resets.blind_states.Small = 'Current' + G.GAME.current_boss_streak = 0 + blhash = 'S' + elseif G.GAME.blind_on_deck == 'Big' then + G.GAME.round_resets.blind_states.Big = 'Current' + G.GAME.current_boss_streak = 0 + blhash = 'B' + else + G.GAME.round_resets.blind_states.Boss = 'Current' + blhash = 'L' + end + G.GAME.subhash = (G.GAME.round_resets.ante)..(blhash) + + G.GAME.blind:set_blind(G.GAME.round_resets.blind) + if G.GAME.modifiers.cry_card_each_round then + G.E_MANAGER:add_event(Event({ + func = function() + local front = pseudorandom_element(G.P_CARDS, pseudoseed('cry_horizon')) + G.playing_card = (G.playing_card and G.playing_card + 1) or 1 + local edition = G.P_CENTERS.c_base + local card = Card(G.play.T.x + G.play.T.w/2, G.play.T.y, G.CARD_W, G.CARD_H, front, G.P_CENTERS.c_base, {playing_card = G.playing_card}) + card:start_materialize() + if G.GAME.selected_back.effect.config.cry_force_edition and G.GAME.selected_back.effect.config.cry_force_edition ~= "random" then + local edition = {} + edition[G.GAME.selected_back.effect.config.cry_force_edition] = true + card:set_edition(edition, true, true); + end + G.play:emplace(card) + table.insert(G.playing_cards, card) + playing_card_joker_effects({true}) + return true + end})) + G.E_MANAGER:add_event(Event({ + func = function() + G.deck.config.card_limit = G.deck.config.card_limit + 1 + return true + end})) + draw_card(G.play,G.deck, 90,'up', nil) + end + if G.GAME.modifiers.cry_conveyor and #G.jokers.cards>0 then + local duplicated_joker = copy_card(G.jokers.cards[#G.jokers.cards]) + duplicated_joker:add_to_deck() + G.jokers:emplace(duplicated_joker) + G.jokers.cards[1]:start_dissolve() + end + G.GAME.current_round.semicolon = false + + for i = 1, #G.playing_cards do + local CARD = G.playing_cards[i] + CARD.cry_debuff_immune = false + end + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({setting_blind = true, blind = G.GAME.round_resets.blind}) + end + delay(0.4) + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.STATE = G.STATES.DRAW_TO_HAND + G.deck:shuffle('nr'..G.GAME.round_resets.ante) + G.deck:hard_set_T() +if G.SCORING_COROUTINE then return false end + G.STATE_COMPLETE = false + return true + end + })) + return true + end + })) +end + +G.FUNCS.draw_from_deck_to_hand = function(e) + if not (G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK or G.STATE == G.STATES.SMODS_BOOSTER_OPENED) and + G.hand.config.card_limit <= 0 and #G.hand.cards == 0 then + G.STATE = G.STATES.GAME_OVER; G.STATE_COMPLETE = false + return true + end + + local hand_space = e + if not hand_space then + local limit = G.hand.config.card_limit - #G.hand.cards + local n = 0 + while n < #G.deck.cards do + local card = G.deck.cards[#G.deck.cards-n] + limit = limit - 1 + (card.edition and card.edition.card_limit or 0) + if limit < 0 then break end + n = n + 1 + end + hand_space = n + end + if G.GAME.modifiers.cry_forced_draw_amount and (G.GAME.current_round.hands_played > 0 or G.GAME.current_round.discards_used > 0) then + hand_space = math.min(#G.deck.cards, G.GAME.modifiers.cry_forced_draw_amount) + end + if G.GAME.blind.name == 'The Serpent' and + not G.GAME.blind.disabled and + (G.GAME.current_round.hands_played > 0 or + G.GAME.current_round.discards_used > 0) then + hand_space = math.min(#G.deck.cards, 3) + end + delay(0.3) + for i=1, hand_space do --draw cards from deckL + if G.STATE == G.STATES.TAROT_PACK or G.STATE == G.STATES.SPECTRAL_PACK then + draw_card(G.deck,G.hand, i*100/hand_space,'up', true) + else + draw_card(G.deck,G.hand, i*100/hand_space,'up', true) + end + end +end + +G.FUNCS.discard_cards_from_highlighted = function(e, hook) + stop_use() + G.CONTROLLER.interrupt.focus = true + G.CONTROLLER:save_cardarea_focus('hand') + + for k, v in ipairs(G.playing_cards) do + v.ability.forced_selection = nil + end + + if G.CONTROLLER.focused.target and G.CONTROLLER.focused.target.area == G.hand then G.card_area_focus_reset = {area = G.hand, rank = G.CONTROLLER.focused.target.rank} end + local highlighted_count = math.min(#G.hand.highlighted, G.discard.config.card_limit - #G.play.cards) + if highlighted_count > 0 then + update_hand_text({immediate = true, nopulse = true, delay = 0}, {mult = 0, chips = 0, level = '', handname = ''}) + table.sort(G.hand.highlighted, function(a,b) return a.T.x < b.T.x end) + inc_career_stat('c_cards_discarded', highlighted_count) + for i = 1, #G.hand.cards do + eval_card(G.hand.cards[i], {pre_discard = true, full_hand = G.hand.highlighted, hook = hook}) + end + for j = 1, #G.jokers.cards do + G.jokers.cards[j]:calculate_joker({pre_discard = true, full_hand = G.hand.highlighted, hook = hook}) + end + local cards = {} + local destroyed_cards = {} + for i=1, highlighted_count do + local removed = false + local eval = nil + eval = eval_card(G.hand.highlighted[i], {discard = true, full_hand = G.hand.highlighted}) + if eval and eval.remove then + removed = true + card_eval_status_text(G.hand.highlighted[i], 'jokers', nil, 1, nil, eval) + end + for j = 1, #G.jokers.cards do + local eval = nil + eval = G.jokers.cards[j]:calculate_joker({discard = true, other_card = G.hand.highlighted[i], full_hand = G.hand.highlighted, callback = function(card, eval) + if eval then + if eval.remove then removed = true end + card_eval_status_text(card, 'jokers', nil, 1, nil, eval) + end + end}) + + end + table.insert(cards, G.hand.highlighted[i]) + if removed then + destroyed_cards[#destroyed_cards + 1] = G.hand.highlighted[i] + if G.hand.highlighted[i].ability.name == 'Glass Card' then + G.hand.highlighted[i]:shatter() + else + G.hand.highlighted[i]:start_dissolve() + end + else + G.hand.highlighted[i].ability.discarded = true + draw_card(G.hand, G.discard, i*100/highlighted_count, 'down', false, G.hand.highlighted[i]) + end + end + + if destroyed_cards[1] then + for j=1, #G.jokers.cards do + eval_card(G.jokers.cards[j], {cardarea = G.jokers, remove_playing_cards = true, removed = destroyed_cards}) + end + end + + G.GAME.round_scores.cards_discarded.amt = G.GAME.round_scores.cards_discarded.amt + #cards + check_for_unlock({type = 'discard_custom', cards = cards}) + if not hook then + if G.GAME.modifiers.discard_cost then + ease_dollars(-G.GAME.modifiers.discard_cost) + end + ease_discard(-1) + G.GAME.current_round.discards_used = G.GAME.current_round.discards_used + 1 + G.STATE = G.STATES.DRAW_TO_HAND + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() +if G.SCORING_COROUTINE then return false end + G.STATE_COMPLETE = false + return true + end + })) + end + end +end + +G.FUNCS.play_cards_from_highlighted = function(e) + if G.play and G.play.cards[1] then return end + --check the hand first + + stop_use() + G.GAME.blind.triggered = false + G.CONTROLLER.interrupt.focus = true + G.CONTROLLER:save_cardarea_focus('hand') + + for k, v in ipairs(G.playing_cards) do + v.ability.forced_selection = nil + end + + table.sort(G.hand.highlighted, function(a,b) return a.T.x < b.T.x end) + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.STATE = G.STATES.HAND_PLAYED + G.STATE_COMPLETE = true + return true + end + })) + inc_career_stat('c_cards_played', #G.hand.highlighted) + inc_career_stat('c_hands_played', 1) + ease_hands_played(-1) + delay(0.4) + + for i=1, #G.hand.highlighted do + if G.hand.highlighted[i]:is_face() then inc_career_stat('c_face_cards_played', 1) end + G.hand.highlighted[i].base.times_played = G.hand.highlighted[i].base.times_played + 1 + G.hand.highlighted[i].ability.played_this_ante = true + G.GAME.round_scores.cards_played.amt = G.GAME.round_scores.cards_played.amt + 1 + draw_card(G.hand, G.play, i*100/#G.hand.highlighted, 'up', nil, G.hand.highlighted[i]) + end + + check_for_unlock({type = 'run_card_replays'}) + + if G.GAME.blind:press_play() then + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + SMODS.juice_up_blind() + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + play_sound('tarot2', 1, 0.4) + return true + end) + })) + delay(0.4) + end + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + check_for_unlock({type = 'hand_contents', cards = G.play.cards}) + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.FUNCS.evaluate_play() + return true + end + })) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = function() + if G.SCORING_COROUTINE then return false end + check_for_unlock({type = 'play_all_hearts'}) + G.FUNCS.draw_from_play_to_discard() + G.GAME.hands_played = G.GAME.hands_played + 1 + G.GAME.current_round.hands_played = G.GAME.current_round.hands_played + 1 + return true + end + })) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() +if G.SCORING_COROUTINE then return false end + G.STATE_COMPLETE = false + return true + end + })) + return true + end) + })) +end + +G.FUNCS.get_poker_hand_info = function(_cards) + local poker_hands = evaluate_poker_hand(_cards) + local scoring_hand = {} + local text,disp_text,loc_disp_text = 'NULL','NULL', 'NULL' + if next(poker_hands["Flush Five"]) then text = "Flush Five"; scoring_hand = poker_hands["Flush Five"][1] + elseif next(poker_hands["Flush House"]) then text = "Flush House"; scoring_hand = poker_hands["Flush House"][1] + elseif next(poker_hands["Five of a Kind"]) then text = "Five of a Kind"; scoring_hand = poker_hands["Five of a Kind"][1] + elseif next(poker_hands["Straight Flush"]) then text = "Straight Flush"; scoring_hand = poker_hands["Straight Flush"][1] + elseif next(poker_hands["Four of a Kind"]) then text = "Four of a Kind"; scoring_hand = poker_hands["Four of a Kind"][1] + elseif next(poker_hands["Full House"]) then text = "Full House"; scoring_hand = poker_hands["Full House"][1] + elseif next(poker_hands["Flush"]) then text = "Flush"; scoring_hand = poker_hands["Flush"][1] + elseif next(poker_hands["Straight"]) then text = "Straight"; scoring_hand = poker_hands["Straight"][1] + elseif next(poker_hands["Three of a Kind"]) then text = "Three of a Kind"; scoring_hand = poker_hands["Three of a Kind"][1] + elseif next(poker_hands["Two Pair"]) then text = "Two Pair"; scoring_hand = poker_hands["Two Pair"][1] + elseif next(poker_hands["Pair"]) then text = "Pair"; scoring_hand = poker_hands["Pair"][1] + elseif next(poker_hands["High Card"]) then text = "High Card"; scoring_hand = poker_hands["High Card"][1] end + + disp_text = text + if text =='Straight Flush' then + local min = 10 + for j = 1, #scoring_hand do + if scoring_hand[j]:get_id() < min then min =scoring_hand[j]:get_id() end + end + if min >= 10 then + disp_text = 'Royal Flush' + end + end + loc_disp_text = localize(disp_text, 'poker_hands') + return text, loc_disp_text, poker_hands, scoring_hand, disp_text +end + +G.FUNCS.evaluate_play = function(e) + local text,disp_text,poker_hands,scoring_hand,non_loc_disp_text = G.FUNCS.get_poker_hand_info(G.play.cards) + + G.GAME.hands[text].played = G.GAME.hands[text].played + 1 + if G.GAME.current_round.current_hand.cry_asc_num > 0 then G.GAME.cry_asc_played = G.GAME.cry_asc_played and G.GAME.cry_asc_played+1 or 1 end + G.GAME.hands[text].played_this_round = G.GAME.hands[text].played_this_round + 1 + G.GAME.last_hand_played = text + set_hand_usage(text) + G.GAME.hands[text].visible = true + + --Add all the pure bonus cards to the scoring hand + local pures = {} + for i=1, #G.play.cards do + if next(find_joker('Splash')) then + scoring_hand[i] = G.play.cards[i] + else + if G.play.cards[i].ability.effect == 'Stone Card' or G.play.cards[i].config.center.always_scores then + local inside = false + for j=1, #scoring_hand do + if scoring_hand[j] == G.play.cards[i] then + inside = true + end + end + if not inside then table.insert(pures, G.play.cards[i]) end + end + end + end + for i=1, #pures do + table.insert(scoring_hand, pures[i]) + end + table.sort(scoring_hand, function (a, b) return a.T.x < b.T.x end ) + delay(0.2) + for i=1, #scoring_hand do + --Highlight all the cards used in scoring and play a sound indicating highlight + highlight_card(scoring_hand[i],(i-0.999)/5,'up') + end + + local percent = 0.3 + local percent_delta = 0.08 + + if G.GAME.current_round.current_hand.handname ~= disp_text then delay(0.3) end + update_hand_text({sound = G.GAME.current_round.current_hand.handname ~= disp_text and 'button' or nil, volume = 0.4, immediate = true, nopulse = nil, + delay = G.GAME.current_round.current_hand.handname ~= disp_text and 0.4 or 0}, {handname=disp_text, level=G.GAME.hands[text].level, mult = cry_ascend(G.GAME.hands[text].mult), chips = cry_ascend(G.GAME.hands[text].chips)}) + + if not G.GAME.blind:debuff_hand(G.play.cards, poker_hands, text) then + mult = mod_mult(cry_ascend(G.GAME.hands[text].mult)) + hand_chips = mod_chips(cry_ascend(G.GAME.hands[text].chips)) + + check_for_unlock({type = 'hand', handname = text, disp_text = non_loc_disp_text, scoring_hand = scoring_hand, full_hand = G.play.cards}) + + delay(0.4) + + if G.GAME.first_used_hand_level and G.GAME.first_used_hand_level > 0 then + level_up_hand(G.deck.cards[1], text, nil, G.GAME.first_used_hand_level) + G.GAME.first_used_hand_level = nil + end + + local hand_text_set = false + for i=1, #G.jokers.cards do + --calculate the joker effects + local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, before = true, callback = function(card, ret) effects = {jokers = ret} + if effects.jokers then + card_eval_status_text(card, 'jokers', nil, percent, nil, effects.jokers) + percent = percent + percent_delta + if effects.jokers.level_up then + level_up_hand(card, text) + end + end + end}) + end + + mult = mod_mult(cry_ascend(G.GAME.hands[text].mult)) + hand_chips = mod_chips(cry_ascend(G.GAME.hands[text].chips)) + + local modded = false + + mult, hand_chips, modded = G.GAME.blind:modify_hand(G.play.cards, poker_hands, text, mult, hand_chips) + mult, hand_chips = mod_mult(mult), mod_chips(hand_chips) + if modded then update_hand_text({sound = 'chips2', modded = modded}, {chips = hand_chips, mult = mult}) end + for i=1, #scoring_hand do + --add cards played to list + if scoring_hand[i].ability.effect ~= 'Stone Card' and not scoring_hand[i].config.center.no_rank then + G.GAME.cards_played[scoring_hand[i].base.value].total = G.GAME.cards_played[scoring_hand[i].base.value].total + 1 + if not scoring_hand[i].config.center.no_suit then + G.GAME.cards_played[scoring_hand[i].base.value].suits[scoring_hand[i].base.suit] = true + end + end + --if card is debuffed + if scoring_hand[i].debuff then + G.GAME.blind.triggered = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() SMODS.juice_up_blind();return true end) + })) + card_eval_status_text(scoring_hand[i], 'debuff') + else + --Check for play doubling + local reps = {1} + + --From Red seal + local eval = eval_card(scoring_hand[i], {repetition_only = true,cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, repetition = true}) + if next(eval) then + for h = 1, eval.seals.repetitions do + reps[#reps+1] = eval + end + end + --From jokers + for j=1, #G.jokers.cards do + --calculate the joker effects + local eval = eval_card(G.jokers.cards[j], {cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = scoring_hand[i], repetition = true, callback = function(card, ret) eval = {jokers = ret} + if next(eval) and eval.jokers then + if not eval.jokers.repetitions then eval.jokers.repetitions = 0 end + for h = 1, eval.jokers.repetitions do + reps[#reps+1] = eval + end + end end}) + end + --From edition + if scoring_hand[i].edition and scoring_hand[i].edition.key then + local ed = SMODS.Centers[scoring_hand[i].edition.key] + if ed.config and ed.config.retriggers then + for h = 1, ed.config.retriggers do + reps[#reps+1] = {seals = { + message = localize("k_again_ex"), + card = scoring_hand[i] + }} + end + end + if ed.calculate and type(ed.calculate) == 'function' then + local check = ed:calculate(scoring_hand[i], {retrigger_edition_check = true, cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = scoring_hand[i], repetition = true}) + if check and type(check) == 'table' and next(check) then + for j = 1, check.repetitions do + reps[#reps+1] = {seals = check} + end + end + end + end + for j=1,#reps do + percent = percent + percent_delta + if reps[j] ~= 1 and (not scoring_hand or not scoring_hand[i] or not scoring_hand[i].will_shatter) then + card_eval_status_text((reps[j].jokers or reps[j].seals).card, 'jokers', nil, nil, nil, (reps[j].jokers or reps[j].seals)) + end + + --calculate the hand effects + local effects = {eval_card(scoring_hand[i], {cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, poker_hand = text})} + for k=1, #G.jokers.cards do + --calculate the joker individual card effects + local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.play, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = scoring_hand[i], individual = true, callback = function(card, eval, retrigger) + if eval then + table.insert(effects, eval) +effects[#effects].from_retrigger = retrigger +end end, no_retrigger_anim = true}) + + end + scoring_hand[i].lucky_trigger = nil + + for ii = 1, #effects do + --If chips added, do chip add event and add the chips to the total + if effects[ii].chips then + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips + effects[ii].chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'chips', effects[ii].chips, percent) + end + + --If mult added, do mult add event and add the mult to the total + if effects[ii].mult then + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult + effects[ii].mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'mult', effects[ii].mult, percent) + end + + --If play dollars added, add dollars to total + if effects[ii].p_dollars then + if effects[ii].card then juice_card(effects[ii].card) end + ease_dollars(effects[ii].p_dollars) + card_eval_status_text(scoring_hand[i], 'dollars', effects[ii].p_dollars, percent) + end + + --If dollars added, add dollars to total + if effects[ii].dollars then + if effects[ii].card then juice_card(effects[ii].card) end + ease_dollars(effects[ii].dollars) + card_eval_status_text(scoring_hand[i], 'dollars', effects[ii].dollars, percent) + end + + --Any extra effects + if effects[ii].extra then + if effects[ii].card then juice_card(effects[ii].card) end + local extras = {mult = false, hand_chips = false} + if effects[ii].extra.mult_mod then mult =mod_mult( mult + effects[ii].extra.mult_mod);extras.mult = true end + if effects[ii].extra.chip_mod then hand_chips = mod_chips(hand_chips + effects[ii].extra.chip_mod);extras.hand_chips = true end + if effects[ii].extra.swap then + local old_mult = mult + mult = mod_mult(hand_chips) + hand_chips = mod_chips(old_mult) + extras.hand_chips = true; extras.mult = true + end + if effects[ii].extra.func then effects[ii].extra.func() end + update_hand_text({delay = 0}, {chips = extras.hand_chips and hand_chips, mult = extras.mult and mult}) + card_eval_status_text(scoring_hand[i], 'extra', nil, percent, nil, effects[ii].extra) + end + + if effects[ii].seals then + if effects[ii].seals.chips then + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips + effects[ii].seals.chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'chips', effects[ii].seals.chips, percent) + end + + if effects[ii].seals.mult then + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult + effects[ii].seals.mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'mult', effects[ii].seals.mult, percent) + end + + if effects[ii].seals.p_dollars then + if effects[ii].card then juice_card(effects[ii].card) end + ease_dollars(effects[ii].seals.p_dollars) + card_eval_status_text(scoring_hand[i], 'dollars', effects[ii].seals.p_dollars, percent) + end + + if effects[ii].seals.dollars then + if effects[ii].card then juice_card(effects[ii].card) end + ease_dollars(effects[ii].seals.dollars) + card_eval_status_text(scoring_hand[i], 'dollars', effects[ii].seals.dollars, percent) + end + + if effects[ii].seals.x_mult then + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult*effects[ii].seals.x_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'x_mult', effects[ii].seals.x_mult, percent) + end + + if effects[ii].seals.func then + effects[ii].seals.func() + end + end + + --If x_mult added, do mult add event and mult the mult to the total + if effects[ii].x_mult then + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult*effects[ii].x_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'x_mult', effects[ii].x_mult, percent) + if next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + end + + if effects[ii].x_chips then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips*effects[ii].x_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'x_chips', effects[ii].x_chips, percent) + end + if effects[ii].e_chips then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips^effects[ii].e_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'e_chips', effects[ii].e_chips, percent) + end + if effects[ii].ee_chips then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips:arrow(2, effects[ii].ee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'ee_chips', effects[ii].ee_chips, percent) + end + if effects[ii].eee_chips then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips:arrow(3, effects[ii].eee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'eee_chips', effects[ii].eee_chips, percent) + end + if effects[ii].hyper_chips and type(effects[ii].hyper_chips) == 'table' then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips:arrow(effects[ii].hyper_chips[1], effects[ii].hyper_chips[2])) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(scoring_hand[i], 'hyper_chips', effects[ii].hyper_chips, percent) + end + if effects[ii].e_mult then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult^effects[ii].e_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'e_mult', effects[ii].e_mult, percent) + end + if effects[ii].ee_mult then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult:arrow(2, effects[ii].ee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'ee_mult', effects[ii].ee_mult, percent) + end + if effects[ii].eee_mult then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult:arrow(3, effects[ii].eee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'eee_mult', effects[ii].eee_mult, percent) + end + if effects[ii].hyper_mult and type(effects[ii].hyper_mult) == 'table' then + mod_percent = true + if effects[ii].card then juice_card(effects[ii].card) end + mult = mod_mult(mult:arrow(effects[ii].hyper_mult[1], effects[ii].hyper_mult[2])) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(scoring_hand[i], 'hyper_mult', effects[ii].hyper_mult, percent) + end + + --calculate the card edition effects + if effects[ii].edition then + if effects[ii].edition.chip_mod then + hand_chips = mod_chips(hand_chips + effects[ii].edition.chip_mod) + local key_switch = (effects[ii].edition.chip_mod > 0 and 'a_chips' or 'a_chips_minus') + card_eval_status_text(scoring_hand[i], 'extra', nil, percent, nil, { + message = localize{type='variable', key=key_switch, vars={math.abs(effects[ii].edition.chip_mod)}}, + chip_mod = true, + colour = G.C.DARK_EDITION, + edition = true + }) + update_hand_text({delay = 0}, {chips = hand_chips}) + end + if effects[ii].edition.mult_mod then + mult = mult + effects[ii].edition.mult_mod + card_eval_status_text(scoring_hand[i], 'extra', nil, percent, nil, { + message = localize{type='variable', key='a_mult', vars={effects[ii].edition.mult_mod}}, + mult_mod = true, + colour = G.C.DARK_EDITION, + edition = true + }) + update_hand_text({delay = 0}, {mult = mult}) + end + if effects[ii].edition.x_mult_mod then + mult = mult * effects[ii].edition.x_mult_mod + card_eval_status_text(scoring_hand[i], 'extra', nil, percent, nil, { + message = localize{type='variable', key='a_xmult', vars={effects[ii].edition.x_mult_mod}}, + x_mult_mod = true, + colour = G.C.DARK_EDITION, + edition = true + }) + update_hand_text({delay = 0}, {mult = mult}) + end + if scoring_hand and scoring_hand[i] and scoring_hand[i].edition then + local trg = scoring_hand[i] + local edi = trg.edition + if edi.x_chips then + hand_chips = mod_chips(hand_chips * edi.x_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = 'X'.. edi.x_chips ..' Chips', + edition = true, + x_chips = true}) + end + if edi.e_chips then + hand_chips = mod_chips(hand_chips ^ edi.e_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^'.. edi.e_chips ..' Chips', + edition = true, + e_chips = true}) + end + if edi.ee_chips then + hand_chips = mod_chips(hand_chips:arrow(2, edi.ee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^'.. edi.ee_chips ..' Chips', + edition = true, + ee_chips = true}) + end + if edi.eee_chips then + hand_chips = mod_chips(hand_chips:arrow(3, edi.eee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^^'.. edi.eee_chips ..' Chips', + edition = true, + eee_chips = true}) + end + if edi.hyper_chips and type(edi.hyper_chips) == 'table' then + hand_chips = mod_chips(hand_chips:arrow(edi.hyper_chips[1], edi.hyper_chips[2])) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = (edi.hyper_chips[1] > 5 and ('{' .. edi.hyper_chips[1] .. '}') or string.rep('^', edi.hyper_chips[1])) .. edi.hyper_chips[2] ..' Chips', + edition = true, + eee_chips = true}) + end + if edi.e_mult then + mult = mod_mult(mult ^ edi.e_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^'.. edi.e_mult ..' Mult', + edition = true, + e_mult = true}) + end + if edi.ee_mult then + mult = mod_mult(mult:arrow(2, edi.ee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^'.. edi.ee_mult ..' Mult', + edition = true, + ee_mult = true}) + end + if edi.eee_mult then + mult = mod_mult(mult:arrow(3, edi.eee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^^'.. edi.eee_mult ..' Mult', + edition = true, + eee_mult = true}) + end + if edi.hyper_mult and type(edi.hyper_mult) == 'table' then + mult = mod_mult(mult:arrow(edi.hyper_mult[1], edi.hyper_mult[2])) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = (edi.hyper_mult[1] > 5 and ('{' .. edi.hyper_mult[1] .. '}') or string.rep('^', edi.hyper_mult[1])) .. edi.hyper_mult[2] ..' Mult', + edition = true, + hyper_mult = true}) + end + end + if effects[ii].edition.p_dollars_mod then + if effects[ii].card then juice_card(effects[ii].card) end + ease_dollars(effects[ii].edition.p_dollars_mod) + card_eval_status_text(scoring_hand[i], 'dollars', effects[ii].edition.p_dollars_mod, percent) + end + if not effects[ii].edition then + hand_chips = mod_chips(hand_chips + (effects[ii].edition.chip_mod or 0)) + mult = mult + (effects[ii].edition.mult_mod or 0) + mult = mod_mult(mult*(effects[ii].edition.x_mult_mod or 1)) + update_hand_text({delay = 0}, { + chips = effects[ii].edition.chip_mod and hand_chips or nil, + mult = (effects[ii].edition.mult_mod or effects[ii].edition.x_mult_mod) and mult or nil, + }) + card_eval_status_text(scoring_hand[i], 'extra', nil, percent, nil, { + message = (effects[ii].edition.chip_mod and localize{type='variable',key='a_chips',vars={effects[ii].edition.chip_mod}}) or + (effects[ii].edition.mult_mod and localize{type='variable',key='a_mult',vars={effects[ii].edition.mult_mod}}) or + (effects[ii].edition.x_mult_mod and localize{type='variable',key='a_xmult',vars={effects[ii].edition.x_mult_mod}}), + chip_mod = effects[ii].edition.chip_mod, + mult_mod = effects[ii].edition.mult_mod, + x_mult_mod = effects[ii].edition.x_mult_mod, + colour = G.C.DARK_EDITION, + edition = true})end + if (effects and effects[ii] and effects[ii].edition and effects[ii].edition.x_mult_mod or edition_effects and edition_effects.jokers and edition_effects.jokers.x_mult_mod) and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + end + if effects[ii].from_retrigger then + card_eval_status_text(effects[ii].from_retrigger.card, 'jokers', nil, nil, nil, effects[ii].from_retrigger) + end + +end + end + end + end + + delay(0.3) + local mod_percent = false + if scoring_hand then + local unscoring_hand = {} + for i = 1, #G.play.cards do + local is_scoring = false + for j = 1, #scoring_hand do + if G.play.cards[i] == scoring_hand[j] then + is_scoring = true + end + end + if not is_scoring then + unscoring_hand[#unscoring_hand+1] = G.play.cards[i] + end + end + for i = 1, #unscoring_hand do + unscoring_hand[i]:calculate_seal{unscoring = true} + end + end + for i=1, #G.hand.cards do + if mod_percent then percent = percent + percent_delta end + mod_percent = false + + --Check for hand doubling + local reps = {1} + local j = 1 + while j <= #reps do + if reps[j] ~= 1 and (not scoring_hand or not scoring_hand[i] or not scoring_hand[i].will_shatter) then + card_eval_status_text((reps[j].jokers or reps[j].seals).card, 'jokers', nil, nil, nil, (reps[j].jokers or reps[j].seals)) + percent = percent + percent_delta + end + + --calculate the hand effects + local effects = {eval_card(G.hand.cards[i], {cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands})} + + for k=1, #G.jokers.cards do + --calculate the joker individual card effects + local eval = G.jokers.cards[k]:calculate_joker({cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = G.hand.cards[i], individual = true, callback = function(card, eval, retrigger) + if eval then + mod_percent = true + table.insert(effects, eval) +effects[#effects].from_retrigger = retrigger +end end, no_retrigger_anim = true}) + + end + + if reps[j] == 1 then + --Check for hand doubling + + --From Red seal + local eval = eval_card(G.hand.cards[i], {repetition_only = true,cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, repetition = true, card_effects = effects}) + if next(eval) and (next(effects[1]) or #effects > 1) then + for h = 1, eval.seals.repetitions do + reps[#reps+1] = eval + end + end + + --From Joker + for j=1, #G.jokers.cards do + --calculate the joker effects + local eval = eval_card(G.jokers.cards[j], {cardarea = G.hand, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_card = G.hand.cards[i], repetition = true, card_effects = effects, callback = function(card, ret) eval = {jokers = ret} + if next(eval) then + for h = 1, eval.jokers.repetitions do + reps[#reps+1] = eval + end + end end}) + end + end + + for ii = 1, #effects do + --if this effect came from a joker + if effects[ii].card and not Talisman.config_file.disable_anims then + mod_percent = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() effects[ii].card:juice_up(0.7);return true end) + })) + end + + --If hold mult added, do hold mult add event and add the mult to the total + + --If dollars added, add dollars to total + if effects[ii].dollars then + ease_dollars(effects[ii].dollars) + card_eval_status_text(G.hand.cards[i], 'dollars', effects[ii].dollars, percent) + end + + if effects[ii].h_mult then + mod_percent = true + mult = mod_mult(mult + effects[ii].h_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'h_mult', effects[ii].h_mult, percent) + end + if effects[ii].h_chips then + if effects[ii].card then juice_card(effects[ii].card) end + hand_chips = mod_chips(hand_chips + effects[ii].h_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(effects[ii].card, 'chips', effects[ii].h_chips, percent) + end + + if effects[ii].x_mult then + mod_percent = true + mult = mod_mult(mult*effects[ii].x_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'x_mult', effects[ii].x_mult, percent) + if next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + end + + if effects[ii].x_chips then + mod_percent = true + hand_chips = mod_chips(hand_chips*effects[ii].x_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'x_chips', effects[ii].x_chips, percent) + end + if effects[ii].e_chips then + mod_percent = true + hand_chips = mod_chips(hand_chips^effects[ii].e_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'e_chips', effects[ii].e_chips, percent) + end + if effects[ii].ee_chips then + mod_percent = true + hand_chips = mod_chips(hand_chips:arrow(2, effects[ii].ee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'ee_chips', effects[ii].ee_chips, percent) + end + if effects[ii].eee_chips then + mod_percent = true + hand_chips = mod_chips(hand_chips:arrow(3, effects[ii].eee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'eee_chips', effects[ii].eee_chips, percent) + end + if effects[ii].hyper_chips and type(effects[ii].hyper_chips) == 'table' then + mod_percent = true + hand_chips = mod_chips(hand_chips:arrow(effects[ii].hyper_chips[1], effects[ii].hyper_chips[2])) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(G.hand.cards[i], 'hyper_chips', effects[ii].hyper_chips, percent) + end + if effects[ii].e_mult then + mod_percent = true + mult = mod_mult(mult^effects[ii].e_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'e_mult', effects[ii].e_mult, percent) + end + if effects[ii].ee_mult then + mod_percent = true + mult = mod_mult(mult:arrow(2, effects[ii].ee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'ee_mult', effects[ii].ee_mult, percent) + end + if effects[ii].eee_mult then + mod_percent = true + mult = mod_mult(mult:arrow(3, effects[ii].eee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'eee_mult', effects[ii].eee_mult, percent) + end + if effects[ii].hyper_mult and type(effects[ii].hyper_mult) == 'table' then + mod_percent = true + mult = mod_mult(mult:arrow(effects[ii].hyper_mult[1], effects[ii].hyper_mult[2])) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'hyper_mult', effects[ii].hyper_mult, percent) + end + + if effects[ii].message then + mod_percent = true + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(G.hand.cards[i], 'extra', nil, percent, nil, effects[ii]) + end + if effects[ii].from_retrigger then + card_eval_status_text(effects[ii].from_retrigger.card, 'jokers', nil, nil, nil, effects[ii].from_retrigger) + end + +end + j = j +1 + end + end + --+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + --Joker Effects + --+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + percent = percent + percent_delta + local numcards = #G.jokers.cards + #G.consumeables.cards + if G.GAME.modifiers.cry_beta then numcards = #G.jokers.cards end + for i=1, numcards do + local _card = G.jokers.cards[i] or G.consumeables.cards[i - #G.jokers.cards] + --calculate the joker edition effects + local edition_effects = eval_card(_card, {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, edition = true}) + if not edition_effects then edition_effects = {} end + if edition_effects.jokers then + edition_effects.jokers.edition = true + if edition_effects.jokers.p_dollars_mod then + ease_dollars(edition_effects.jokers.p_dollars_mod) + card_eval_status_text(_card, 'dollars', edition_effects.jokers.p_dollars_mod, percent) + end + if edition_effects.jokers.chip_mod then + hand_chips = mod_chips(hand_chips + edition_effects.jokers.chip_mod) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(_card, 'jokers', nil, percent, nil, { + message = localize{type='variable',key='a_chips',vars={edition_effects.jokers.chip_mod}}, + chip_mod = edition_effects.jokers.chip_mod, + colour = G.C.EDITION, + edition = true}) + if (effects and effects[ii] and effects[ii].edition and effects[ii].edition.x_mult_mod or edition_effects and edition_effects.jokers and edition_effects.jokers.x_mult_mod) and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + end + if edition_effects.jokers.mult_mod then + mult = mod_mult(mult + edition_effects.jokers.mult_mod) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(_card, 'jokers', nil, percent, nil, { + message = localize{type='variable',key='a_mult',vars={edition_effects.jokers.mult_mod}}, + mult_mod = edition_effects.jokers.mult_mod, + colour = G.C.DARK_EDITION, + edition = true}) + if (effects and effects[ii] and effects[ii].edition and effects[ii].edition.x_mult_mod or edition_effects and edition_effects.jokers and edition_effects.jokers.x_mult_mod) and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + end + percent = percent+percent_delta + end + + --calculate the joker effects + local effects = eval_card(_card, {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, joker_main = true, callback = function(_card, ret) effects = {jokers = ret} + + --Any Joker effects + if effects.jokers then + local extras = {mult = false, hand_chips = false} + if effects.jokers.mult_mod then mult = mod_mult(mult + effects.jokers.mult_mod);extras.mult = true end + if effects.jokers.chip_mod then hand_chips = mod_chips(hand_chips + effects.jokers.chip_mod);extras.hand_chips = true end + if effects.jokers.Xmult_mod then mult = mod_mult(mult*effects.jokers.Xmult_mod);extras.mult = true end + if effects.jokers.Emult_mod then mult = mod_mult(mult^effects.jokers.Emult_mod);extras.mult = true end + if effects.jokers.EEmult_mod then mult = mod_mult(mult:arrow(2, effects.jokers.EEmult_mod));extras.mult = true end + if effects.jokers.EEEmult_mod then mult = mod_mult(mult:arrow(3, effects.jokers.EEEmult_mod));extras.mult = true end + if effects.jokers.hypermult_mod and type(effects.jokers.hypermult_mod) == 'table' then mult = mod_mult(mult:arrow(effects.jokers.hypermult_mod[1], effects.jokers.hypermult_mod[2]));extras.mult = true end + if effects.jokers.Xchip_mod then hand_chips = mod_chips(hand_chips*effects.jokers.Xchip_mod);extras.hand_chips = true end + if effects.jokers.Echip_mod then hand_chips = mod_chips(hand_chips^effects.jokers.Echip_mod);extras.hand_chips = true end + if effects.jokers.EEchip_mod then hand_chips = mod_chips(hand_chips:arrow(2, effects.jokers.EEchip_mod));extras.hand_chips = true end + if effects.jokers.EEEchip_mod then hand_chips = mod_chips(hand_chips:arrow(3, effects.jokers.EEEchip_mod));extras.hand_chips = true end + if effects.jokers.hyperchip_mod and type(effects.jokers.hyperchip_mod) == 'table' then hand_chips = mod_chips(hand_chips:arrow(effects.jokers.hyperchip_mod[1], effects.jokers.hyperchip_mod[2]));extras.hand_chips = true end + update_hand_text({delay = 0}, {chips = extras.hand_chips and hand_chips, mult = extras.mult and mult}) + card_eval_status_text(_card, 'jokers', nil, percent, nil, effects.jokers) + if effects.jokers.Xmult_mod and effects.jokers.Xmult_mod ~= 1 and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + percent = percent+percent_delta + end + + end}) + --Joker on Joker effects + for _, v in ipairs(G.jokers.cards) do + local effect = v:calculate_joker({full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, other_joker = _card, callback = function(v, effect) + if effect then + local extras = {mult = false, hand_chips = false} + if effect.mult_mod then mult = mod_mult(mult + effect.mult_mod);extras.mult = true end + if effect.chip_mod then hand_chips = mod_chips(hand_chips + effect.chip_mod);extras.hand_chips = true end + if effect.Xmult_mod then mult = mod_mult(mult*effect.Xmult_mod);extras.mult = true end + if effect.Emult_mod then mult = mod_mult(mult^effect.Emult_mod);extras.mult = true end + if effect.EEmult_mod then mult = mod_mult(mult:arrow(2, effect.EEmult_mod));extras.mult = true end + if effect.EEEmult_mod then mult = mod_mult(mult:arrow(3, effect.EEEmult_mod));extras.mult = true end + if effect.hypermult_mod and type(effect.hypermult_mod) == 'table' then mult = mod_mult(mult:arrow(effect.hypermult_mod[1], effect.hypermult_mod[2]));extras.mult = true end + if effect.Xchip_mod then hand_chips = mod_chips(hand_chips*effect.Xchip_mod);extras.hand_chips = true end + if effect.Echip_mod then hand_chips = mod_chips(hand_chips^effect.Echip_mod);extras.hand_chips = true end + if effect.EEchip_mod then hand_chips = mod_chips(hand_chips:arrow(2, effect.EEchip_mod));extras.hand_chips = true end + if effect.EEEchip_mod then hand_chips = mod_chips(hand_chips:arrow(3, effect.EEEchip_mod));extras.hand_chips = true end + if effect.hyperchip_mod and type(effect.hyperchip_mod) == 'table' then hand_chips = mod_chips(hand_chips:arrow(effect.hyperchip_mod[1], effect.hyperchip_mod[2]));extras.hand_chips = true end + if extras.mult or extras.hand_chips then update_hand_text({delay = 0}, {chips = extras.hand_chips and hand_chips, mult = extras.mult and mult}) end + if extras.mult or extras.hand_chips then card_eval_status_text(v, 'jokers', nil, percent, nil, effect) end + if effects.Xmult_mod and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + if effect.Xmult_mod and effect.Xmult_mod ~= 1 and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + percent = percent+percent_delta + end end}) end if edition_effects.jokers then + if edition_effects.jokers.x_mult_mod then + mult = mod_mult(mult*edition_effects.jokers.x_mult_mod) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(_card, 'jokers', nil, percent, nil, { + message = localize{type='variable',key='a_xmult',vars={edition_effects.jokers.x_mult_mod}}, + x_mult_mod = edition_effects.jokers.x_mult_mod, + colour = G.C.EDITION, + edition = true}) + end + if G.jokers.cards and G.jokers.cards[i] and G.jokers.cards[i].edition then + local trg = G.jokers.cards[i] + local edi = trg.edition + if edi.x_chips then + hand_chips = mod_chips(hand_chips * edi.x_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = 'X'.. edi.x_chips ..' Chips', + edition = true, + x_chips = true}) + end + if edi.e_chips then + hand_chips = mod_chips(hand_chips ^ edi.e_chips) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^'.. edi.e_chips ..' Chips', + edition = true, + e_chips = true}) + end + if edi.ee_chips then + hand_chips = mod_chips(hand_chips:arrow(2, edi.ee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^'.. edi.ee_chips ..' Chips', + edition = true, + ee_chips = true}) + end + if edi.eee_chips then + hand_chips = mod_chips(hand_chips:arrow(3, edi.eee_chips)) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^^'.. edi.eee_chips ..' Chips', + edition = true, + eee_chips = true}) + end + if edi.hyper_chips and type(edi.hyper_chips) == 'table' then + hand_chips = mod_chips(hand_chips:arrow(edi.hyper_chips[1], edi.hyper_chips[2])) + update_hand_text({delay = 0}, {chips = hand_chips}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = (edi.hyper_chips[1] > 5 and ('{' .. edi.hyper_chips[1] .. '}') or string.rep('^', edi.hyper_chips[1])) .. edi.hyper_chips[2] ..' Chips', + edition = true, + eee_chips = true}) + end + if edi.e_mult then + mult = mod_mult(mult ^ edi.e_mult) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^'.. edi.e_mult ..' Mult', + edition = true, + e_mult = true}) + end + if edi.ee_mult then + mult = mod_mult(mult:arrow(2, edi.ee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^'.. edi.ee_mult ..' Mult', + edition = true, + ee_mult = true}) + end + if edi.eee_mult then + mult = mod_mult(mult:arrow(3, edi.eee_mult)) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = '^^^'.. edi.eee_mult ..' Mult', + edition = true, + eee_mult = true}) + end + if edi.hyper_mult and type(edi.hyper_mult) == 'table' then + mult = mod_mult(mult:arrow(edi.hyper_mult[1], edi.hyper_mult[2])) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(trg, 'extra', nil, percent, nil, + {message = (edi.hyper_mult[1] > 5 and ('{' .. edi.hyper_mult[1] .. '}') or string.rep('^', edi.hyper_mult[1])) .. edi.hyper_mult[2] ..' Mult', + edition = true, + hyper_mult = true}) + end + end + edition_effects.jokers.x_mult_mod = nil + if edition_effects.jokers.x_mult_mod then + mult = mod_mult(mult*edition_effects.jokers.x_mult_mod) + update_hand_text({delay = 0}, {mult = mult}) + card_eval_status_text(_card, 'jokers', nil, percent, nil, { + message = localize{type='variable',key='a_xmult',vars={edition_effects.jokers.x_mult_mod}}, + x_mult_mod = edition_effects.jokers.x_mult_mod, + colour = G.C.EDITION, + edition = true}) + if (effects and effects[ii] and effects[ii].edition and effects[ii].edition.x_mult_mod or edition_effects and edition_effects.jokers and edition_effects.jokers.x_mult_mod) and next(find_joker("cry-Exponentia")) then + for _, v in pairs(find_joker("cry-Exponentia")) do + local old = v.ability.extra.Emult + v.ability.extra.Emult = v.ability.extra.Emult + v.ability.extra.Emult_mod + card_eval_status_text(v, 'extra', nil, nil, nil, {message = localize{type='variable',key='a_powmult',vars={number_format(to_big(v.ability.extra.Emult))}}}) + exponentia_scale_mod(v, v.ability.extra.Emult_mod, old, v.ability.extra.Emult) + end + end + end + percent = percent+percent_delta + end + end + + local nu_chip, nu_mult = G.GAME.selected_back:trigger_effect{context = 'final_scoring_step', chips = hand_chips, mult = mult} + mult = mod_mult(nu_mult or mult) + hand_chips = mod_chips(nu_chip or hand_chips) + + local cards_destroyed = {} + for i=1, #scoring_hand do + local destroyed = nil + --un-highlight all cards + highlight_card(scoring_hand[i],(i-0.999)/(#scoring_hand-0.998),'down') + + for j = 1, #G.jokers.cards do + destroyed = G.jokers.cards[j]:calculate_joker({destroying_card = scoring_hand[i], full_hand = G.play.cards, callback = function(card, ret) if ret then destroyed=true end end}) + if destroyed then break end + end + + if ((scoring_hand[i].ability.name == 'Glass Card' and not scoring_hand[i].debuff and pseudorandom('glass') < G.GAME.probabilities.normal/scoring_hand[i].ability.extra) or (G.GAME.modifiers.cry_shatter_rate and pseudorandom('cry_shatter') < 1/G.GAME.modifiers.cry_shatter_rate)) and not scoring_hand[i].ability.eternal then + destroyed = true + end + + if scoring_hand[i].will_shatter then destroyed = true end + if scoring_hand[i]:calculate_seal({destroying_card = scoring_hand[i], full_hand = G.play.cards}) and not scoring_hand[i].ability.eternal then + destroyed = true + end + + if destroyed then + if scoring_hand[i].ability.name == 'Glass Card' then + scoring_hand[i].shattered = true + else + scoring_hand[i].destroyed = true + end + cards_destroyed[#cards_destroyed+1] = scoring_hand[i] + end + end + for j=1, #G.jokers.cards do + eval_card(G.jokers.cards[j], {cardarea = G.jokers, remove_playing_cards = true, removed = cards_destroyed}) + end + + local glass_shattered = {} + for k, v in ipairs(cards_destroyed) do + if v.shattered then glass_shattered[#glass_shattered+1] = v end + end + + check_for_unlock{type = 'shatter', shattered = glass_shattered} + + for i=1, #cards_destroyed do + G.E_MANAGER:add_event(Event({ + func = function() + if cards_destroyed[i].will_shatter then return true end + if cards_destroyed[i].ability.name == 'Glass Card' then + cards_destroyed[i]:shatter() + else + cards_destroyed[i]:start_dissolve() + end + return true + end + })) + end + else + mult = mod_mult(0) + hand_chips = mod_chips(0) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + SMODS.juice_up_blind() + G.E_MANAGER:add_event(Event({trigger = 'after', delay = 0.06*G.SETTINGS.GAMESPEED, blockable = false, blocking = false, func = function() + play_sound('tarot2', 0.76, 0.4);return true end})) + play_sound('tarot2', 1, 0.4) + return true + end) + })) + + play_area_status_text("Not Allowed!")--localize('k_not_allowed_ex'), true) + --+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + --Joker Debuff Effects + --+++++++++++++++++++++++++++++++++++++++++++++++++++++++++-- + for i=1, #G.jokers.cards do + + --calculate the joker effects + local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, debuffed_hand = true, callback = function(card, ret) effects = {jokers = ret} + + --Any Joker effects + if effects.jokers then + card_eval_status_text(card, 'jokers', nil, percent, nil, effects.jokers) + percent = percent+percent_delta + end + end}) end + end + G.E_MANAGER:add_event(Event({ + trigger = 'after',delay = 0.4, + func = (function() update_hand_text({delay = 0, immediate = true}, {mult = 0, chips = 0, chip_total = G.GAME.blind.cry_cap_score and G.GAME.blind:cry_cap_score(math.floor(hand_chips*mult)) or math.floor(hand_chips*mult), level = '', handname = ''});play_sound('button', 0.9, 0.6);return true end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() G.GAME.current_round.current_hand.cry_asc_num = 0;G.GAME.current_round.current_hand.cry_asc_num_text = '';return true end) + })) + check_and_set_high_score('hand', hand_chips*mult) + + check_for_unlock({type = 'chip_score', chips = math.floor(hand_chips*mult)}) + + if to_big(hand_chips)*mult > to_big(0) then + delay(0.8) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() play_sound('chips2');return true end) + })) + end + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blocking = false, + ref_table = G.GAME, + ref_value = 'chips', + ease_to = G.GAME.chips + (G.GAME.blind.cry_cap_score and G.GAME.blind:cry_cap_score(math.floor(hand_chips*mult)) or math.floor(hand_chips*mult)), + delay = 0.5, + func = (function(t) return math.floor(t) end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'ease', + blocking = true, + ref_table = G.GAME.current_round.current_hand, + ref_value = 'chip_total', + ease_to = 0, + delay = 0.5, + func = (function(t) return math.floor(t) end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() G.GAME.current_round.current_hand.handname = '';return true end) + })) + delay(0.3) + + for i=1, #G.jokers.cards do + --calculate the joker after hand played effects + local effects = eval_card(G.jokers.cards[i], {cardarea = G.jokers, full_hand = G.play.cards, scoring_hand = scoring_hand, scoring_name = text, poker_hands = poker_hands, after = true, callback = function(card, ret) effects = {jokers = ret} + if effects.jokers then + card_eval_status_text(card, 'jokers', nil, percent, nil, effects.jokers) + percent = percent + percent_delta + end + end + + }) end + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + if G.GAME.modifiers.debuff_played_cards then + for k, v in ipairs(scoring_hand) do v.ability.perma_debuff = true end + end + return true end) + })) + + end + + G.FUNCS.draw_from_play_to_discard = function(e) + local play_count = #G.play.cards + local it = 1 + for k, v in ipairs(G.play.cards) do + if (not v.shattered) and (not v.destroyed) then + draw_card(G.play,G.discard, it*100/play_count,'down', false, v) + it = it + 1 + end + end + end + + G.FUNCS.draw_from_play_to_hand = function(cards) + local gold_count = #cards + for i=1, gold_count do --draw cards from play + if not cards[i].shattered and not cards[i].destroyed then + draw_card(G.play,G.hand, i*100/gold_count,'up', true, cards[i]) + end + end + end + + G.FUNCS.draw_from_discard_to_deck = function(e) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + local discard_count = #G.discard.cards + for i=1, discard_count do --draw cards from deck + draw_card(G.discard, G.deck, i*100/discard_count,'up', nil ,nil, 0.005, i%2==0, nil, math.max((21-i)/20,0.7)) + end + return true + end + })) + end + + G.FUNCS.draw_from_hand_to_deck = function(e) + local hand_count = #G.hand.cards + for i=1, hand_count do --draw cards from deck + draw_card(G.hand, G.deck, i*100/hand_count,'down', nil, nil, 0.08) + end + end + + G.FUNCS.draw_from_hand_to_discard = function(e) + local hand_count = #G.hand.cards + for i=1, hand_count do + draw_card(G.hand,G.discard, i*100/hand_count,'down', nil, nil, 0.07) + end +end + +G.FUNCS.evaluate_round = function() + total_cashout_rows = 0 + local pitch = 0.95 + local dollars = 0 + + if to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) then + add_round_eval_row({dollars = G.GAME.blind.dollars, name='blind1', pitch = pitch}) + pitch = pitch + 0.06 + dollars = dollars + G.GAME.blind.dollars + else + add_round_eval_row({dollars = 0, name='blind1', pitch = pitch, saved = true}) + pitch = pitch + 0.06 + end + + G.E_MANAGER:add_event(Event({ + trigger = 'before', + delay = 1.3*math.min(G.GAME.blind.dollars+2, 7)/2*0.15 + 0.5, + func = function() + G.GAME.blind:defeat() + return true + end + })) + delay(0.2) + G.E_MANAGER:add_event(Event({ + func = function() + ease_background_colour_blind(G.STATES.ROUND_EVAL, '') + return true + end + })) + G.GAME.selected_back:trigger_effect({context = 'eval'}) + + if G.GAME.current_round.hands_left > 0 and not G.GAME.modifiers.no_extra_hand_money then + add_round_eval_row({dollars = G.GAME.current_round.hands_left*(G.GAME.modifiers.money_per_hand or 1), disp = G.GAME.current_round.hands_left, bonus = true, name='hands', pitch = pitch}) + pitch = pitch + 0.06 + dollars = dollars + G.GAME.current_round.hands_left*(G.GAME.modifiers.money_per_hand or 1) + end + if G.GAME.current_round.discards_left > 0 and G.GAME.modifiers.money_per_discard then + add_round_eval_row({dollars = G.GAME.current_round.discards_left*(G.GAME.modifiers.money_per_discard), disp = G.GAME.current_round.discards_left, bonus = true, name='discards', pitch = pitch}) + pitch = pitch + 0.06 + dollars = dollars + G.GAME.current_round.discards_left*(G.GAME.modifiers.money_per_discard) + end + for i = 1, #G.playing_cards do + local CARD = G.playing_cards[i] + CARD.cry_debuff_immune = false + end + for i = 1, #G.jokers.cards do + local ret = G.jokers.cards[i]:calculate_dollar_bonus() + if ret then + add_round_eval_row({dollars = ret, bonus = true, name='joker'..i, pitch = pitch, card = G.jokers.cards[i]}) + pitch = pitch + 0.06 + dollars = dollars + ret + end + end + for i = 1, #G.GAME.tags do + local ret = G.GAME.tags[i]:apply_to_run({type = 'eval'}) + if ret then + add_round_eval_row({dollars = ret.dollars, bonus = true, name='tag'..i, pitch = pitch, condition = ret.condition, pos = ret.pos, tag = ret.tag}) + pitch = pitch + 0.06 + dollars = dollars + ret.dollars + end + end + if G.GAME.dollars >= 5 and not G.GAME.modifiers.no_interest and G.GAME.cry_payload then + add_round_eval_row({bonus = true, payload = G.GAME.cry_payload, name='interest_payload', pitch = pitch, dollars = G.GAME.interest_amount*G.GAME.cry_payload*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5)}) + pitch = pitch + 0.06 + if not G.GAME.seeded and not G.GAME.challenge then + if G.GAME.interest_amount*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5) == G.GAME.interest_amount*G.GAME.interest_cap/5 then + G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak = G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak + 1 + else + G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak = 0 + end + end + check_for_unlock({type = 'interest_streak'}) + dollars = dollars + G.GAME.interest_amount*G.GAME.cry_payload*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5) + G.GAME.cry_payload = nil + elseif G.GAME.dollars >= 5 and not G.GAME.modifiers.no_interest then + add_round_eval_row({bonus = true, name='interest', pitch = pitch, dollars = G.GAME.interest_amount*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5)}) + pitch = pitch + 0.06 + if not G.GAME.seeded and not G.GAME.challenge then + if G.GAME.interest_amount*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5) == G.GAME.interest_amount*G.GAME.interest_cap/5 then + G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak = G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak + 1 + else + G.PROFILES[G.SETTINGS.profile].career_stats.c_round_interest_cap_streak = 0 + end + end + check_for_unlock({type = 'interest_streak'}) + dollars = dollars + G.GAME.interest_amount*math.min(math.floor(G.GAME.dollars/5), G.GAME.interest_cap/5) + end + + pitch = pitch + 0.06 + + if total_cashout_rows > 7 then + local total_hidden = total_cashout_rows - 7 + + G.E_MANAGER:add_event(Event({ + trigger = 'before',delay = 0.38, + func = function() + local hidden = {n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({ + string = {localize{type = 'variable', key = 'cashout_hidden', vars = {total_hidden}}}, + colours = {G.C.WHITE}, shadow = true, float = false, + scale = 0.45, + font = G.LANGUAGES['en-us'].font, pop_in = 0 + })}} + }} + + G.round_eval:add_child(hidden, G.round_eval:get_UIE_by_ID('bonus_round_eval')) + return true + end + })) + end + add_round_eval_row({name = 'bottom', dollars = dollars}) + Talisman.dollars = dollars +end + +G.FUNCS.tutorial_controller = function() + if G.F_SKIP_TUTORIAL then + G.SETTINGS.tutorial_complete = true + G.SETTINGS.tutorial_progress = nil + return + end + G.SETTINGS.tutorial_progress = G.SETTINGS.tutorial_progress or + { + forced_shop = {'j_joker', 'c_empress'}, + forced_voucher = 'v_grabber', + forced_tags = {'tag_handy', 'tag_garbage'}, + hold_parts = {}, + completed_parts = {}, + } + if not G.SETTINGS.paused and (not G.SETTINGS.tutorial_complete) then + if G.STATE == G.STATES.BLIND_SELECT and G.blind_select and not G.SETTINGS.tutorial_progress.completed_parts['small_blind'] then + G.SETTINGS.tutorial_progress.section = 'small_blind' + G.FUNCS.tutorial_part('small_blind') + G.SETTINGS.tutorial_progress.completed_parts['small_blind'] = true + G:save_progress() + end + if G.STATE == G.STATES.BLIND_SELECT and G.blind_select and not G.SETTINGS.tutorial_progress.completed_parts['big_blind'] and G.GAME.round > 0 then + G.SETTINGS.tutorial_progress.section = 'big_blind' + G.FUNCS.tutorial_part('big_blind') + G.SETTINGS.tutorial_progress.completed_parts['big_blind'] = true + G.SETTINGS.tutorial_progress.forced_tags = nil + G:save_progress() + end + if G.STATE == G.STATES.SELECTING_HAND and not G.SETTINGS.tutorial_progress.completed_parts['second_hand'] and G.SETTINGS.tutorial_progress.hold_parts['big_blind'] then + G.SETTINGS.tutorial_progress.section = 'second_hand' + G.FUNCS.tutorial_part('second_hand') + G.SETTINGS.tutorial_progress.completed_parts['second_hand'] = true + G:save_progress() + end + if G.SETTINGS.tutorial_progress.hold_parts['second_hand'] then + G.SETTINGS.tutorial_complete = true + end + if not G.SETTINGS.tutorial_progress.completed_parts['first_hand_section'] then + if G.STATE == G.STATES.SELECTING_HAND and not G.SETTINGS.tutorial_progress.completed_parts['first_hand'] then + G.SETTINGS.tutorial_progress.section = 'first_hand' + G.FUNCS.tutorial_part('first_hand') + G.SETTINGS.tutorial_progress.completed_parts['first_hand'] = true + G:save_progress() + end + if G.STATE == G.STATES.SELECTING_HAND and not G.SETTINGS.tutorial_progress.completed_parts['first_hand_2'] and G.SETTINGS.tutorial_progress.hold_parts['first_hand'] then + G.FUNCS.tutorial_part('first_hand_2') + G.SETTINGS.tutorial_progress.completed_parts['first_hand_2'] = true + G:save_progress() + end + if G.STATE == G.STATES.SELECTING_HAND and not G.SETTINGS.tutorial_progress.completed_parts['first_hand_3'] and G.SETTINGS.tutorial_progress.hold_parts['first_hand_2'] then + G.FUNCS.tutorial_part('first_hand_3') + G.SETTINGS.tutorial_progress.completed_parts['first_hand_3'] = true + G:save_progress() + end + if G.STATE == G.STATES.SELECTING_HAND and not G.SETTINGS.tutorial_progress.completed_parts['first_hand_4'] and G.SETTINGS.tutorial_progress.hold_parts['first_hand_3'] then + G.FUNCS.tutorial_part('first_hand_4') + G.SETTINGS.tutorial_progress.completed_parts['first_hand_4'] = true + G.SETTINGS.tutorial_progress.completed_parts['first_hand_section'] = true + G:save_progress() + end + end + if G.STATE == G.STATES.SHOP and G.shop and G.shop.VT.y < 5 and not G.SETTINGS.tutorial_progress.completed_parts['shop_1'] then + G.SETTINGS.tutorial_progress.section = 'shop' + G.FUNCS.tutorial_part('shop_1') + G.SETTINGS.tutorial_progress.completed_parts['shop_1'] = true + G.SETTINGS.tutorial_progress.forced_voucher = nil + G:save_progress() + end + end +end + +G.FUNCS.tutorial_part = function(_part) + local step = 1 + G.SETTINGS.paused = true + if _part == 'small_blind' then + step = tutorial_info({ + text_key = 'sb_1', + attach = {major = G.ROOM_ATTACH, type = 'cm', offset = {x = 0, y = 0}}, + step = step, + }) + step = tutorial_info({ + text_key = 'sb_2', + attach = {major = G.ROOM_ATTACH, type = 'cm', offset = {x = 0, y = 0}}, + step = step, + }) + step = tutorial_info({ + text_key = 'sb_3', + highlight = { + G.blind_select.UIRoot.children[1].children[1].config.object:get_UIE_by_ID('blind_name'), + G.blind_select.UIRoot.children[1].children[1].config.object:get_UIE_by_ID('blind_desc'), + }, + attach = {major = G.blind_select.UIRoot.children[1].children[1], type = 'tr', offset = {x = 2, y = 4}}, + step = step, + }) + step = tutorial_info({ + text_key = 'sb_4', + highlight = { + G.blind_select.UIRoot.children[1].children[1] + }, + snap_to = function() + if G.blind_select and G.blind_select.UIRoot and G.blind_select.UIRoot.children[1] and G.blind_select.UIRoot.children[1].children[1] and G.blind_select.UIRoot.children[1].children[1].config.object then + return G.blind_select.UIRoot.children[1].children[1].config.object:get_UIE_by_ID('select_blind_button') end + end, + attach = {major = G.blind_select.UIRoot.children[1].children[1], type = 'tr', offset = {x = 2, y = 4}}, + step = step, + no_button = true, + button_listen = 'select_blind' + }) + elseif _part == 'big_blind' then + step = tutorial_info({ + text_key = 'bb_1', + highlight = { + G.blind_select.UIRoot.children[1].children[2].config.object:get_UIE_by_ID('blind_name'), + G.blind_select.UIRoot.children[1].children[2].config.object:get_UIE_by_ID('blind_desc'), + }, + hard_set = true, + attach = {major = G.HUD, type = 'cm', offset = {x = 0, y = -2}}, + step = step, + }) + step = tutorial_info({ + text_key = 'bb_2', + highlight = { + G.blind_select.UIRoot.children[1].children[2].config.object:get_UIE_by_ID('blind_name'), + G.blind_select.UIRoot.children[1].children[2].config.object:get_UIE_by_ID('tag_desc'), + }, + attach = {major = G.HUD, type = 'cm', offset = {x = 0, y = -2}}, + step = step, + }) + step = tutorial_info({ + text_key = 'bb_3', + highlight = { + G.blind_select.UIRoot.children[1].children[3].config.object:get_UIE_by_ID('blind_name'), + G.blind_select.UIRoot.children[1].children[3].config.object:get_UIE_by_ID('blind_desc'), + }, + attach = {major = G.HUD, type = 'cm', offset = {x = 0, y = -2}}, + step = step, + }) + step = tutorial_info({ + text_key = 'bb_4', + highlight = { + G.blind_select.UIRoot.children[1].children[3].config.object:get_UIE_by_ID('blind_name'), + G.blind_select.UIRoot.children[1].children[3].config.object:get_UIE_by_ID('blind_desc'), + G.blind_select.UIRoot.children[1].children[3].config.object:get_UIE_by_ID('blind_extras'), + G.HUD:get_UIE_by_ID('hud_ante') + }, + attach = {major = G.HUD, type = 'cm', offset = {x = 0, y = -2}}, + step = step, + }) + step = tutorial_info({ + text_key = 'bb_5', + loc_vars = {G.GAME.win_ante}, + highlight = { + G.blind_select, + G.HUD:get_UIE_by_ID('hud_ante') + }, + attach = {major = G.HUD, type = 'cm', offset = {x = 0, y = -2}}, + step = step, + no_button = true, + snap_to = function() + if G.blind_select and G.blind_select.UIRoot and G.blind_select.UIRoot.children[1] and G.blind_select.UIRoot.children[1].children[2] and + G.blind_select.UIRoot.children[1].children[2].config.object then + return G.blind_select.UIRoot.children[1].children[2].config.object:get_UIE_by_ID('select_blind_button') end + end, + button_listen = 'select_blind' + }) + elseif _part == 'first_hand' then + step = tutorial_info({ + text_key = 'fh_1', + attach = {major = G.ROOM_ATTACH, type = 'cm', offset = {x = 0, y = 0}}, + step = step, + }) + step = tutorial_info({ + text_key = 'fh_2', + highlight = { + G.HUD:get_UIE_by_ID('hand_text_area') + }, + attach = {major = G.ROOM_ATTACH, type = 'cm', offset = {x = 0, y = 0}}, + step = step, + }) + step = tutorial_info({ + text_key = 'fh_3', + attach = {major = G.ROOM_ATTACH, type = 'cm', offset = {x = 0, y = 0}}, + highlight = { + G.HUD:get_UIE_by_ID('run_info_button') + }, + no_button = true, + button_listen = 'run_info', + snap_to = function() return G.HUD:get_UIE_by_ID('run_info_button') end, + step = step, + }) + elseif _part == 'first_hand_2' then + step = tutorial_info({ + hard_set = true, + text_key = 'fh_4', + highlight = { + G.hand, + G.HUD:get_UIE_by_ID('run_info_button') + }, + attach = {major = G.hand, type = 'cl', offset = {x = -1.5, y = 0}}, + snap_to = function() return G.hand.cards[1] end, + step = step, + }) + step = tutorial_info({ + text_key = 'fh_5', + highlight = { + G.hand, + G.buttons:get_UIE_by_ID('play_button'), + G.HUD:get_UIE_by_ID('run_info_button') + }, + attach = {major = G.hand, type = 'cl', offset = {x = -1.5, y = 0}}, + no_button = true, + button_listen = 'play_cards_from_highlighted', + step = step, + }) + elseif _part == 'first_hand_3' then + step = tutorial_info({ + hard_set = true, + text_key = 'fh_6', + highlight = { + G.hand, + G.buttons:get_UIE_by_ID('discard_button'), + G.HUD:get_UIE_by_ID('run_info_button') + }, + attach = {major = G.hand, type = 'cl', offset = {x = -1.5, y = 0}}, + no_button = true, + button_listen = 'discard_cards_from_highlighted', + step = step, + }) + elseif _part == 'first_hand_4' then + step = tutorial_info({ + hard_set = true, + text_key = 'fh_7', + highlight = { + G.HUD:get_UIE_by_ID('hud_hands').parent, + }, + attach = {major = G.ROOM_ATTACH, type = 'cm', offset = {x = 0, y = 0}}, + step = step, + }) + step = tutorial_info({ + text_key = 'fh_8', + highlight = { + G.HUD:get_UIE_by_ID('hud_hands').parent, + G.HUD:get_UIE_by_ID('row_dollars_chips'), + G.HUD_blind + }, + attach = {major = G.ROOM_ATTACH, type = 'cm', offset = {x = 0, y = 0}}, + step = step, + }) + elseif _part == 'second_hand' then + step = tutorial_info({ + text_key = 'sh_1', + hard_set = true, + highlight = { + G.jokers + }, + attach = {major = G.HUD, type = 'cm', offset = {x = 0, y = -2}}, + step = step, + }) + local empress = find_joker('The Empress')[1] + if empress then + step = tutorial_info({ + text_key = 'sh_2', + highlight = { + empress + }, + attach = {major = G.HUD, type = 'cm', offset = {x = 0, y = -2}}, + step = step, + }) + step = tutorial_info({ + text_key = 'sh_3', + attach = {major = G.HUD, type = 'cm', offset = {x = 0, y = -2}}, + highlight = { + empress, + G.hand + }, + no_button = true, + button_listen = 'use_card', + snap_to = function() return G.hand.cards[1] end, + step = step, + }) + end + elseif _part == 'shop_1' then + step = tutorial_info({ + hard_set = true, + text_key = 's_1', + highlight = { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent + }, + attach = {major = G.shop, type = 'tm', offset = {x = 0, y = 4}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_2', + highlight = { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.shop_jokers.cards[2], + }, + snap_to = function() return G.shop_jokers.cards[2] end, + attach = {major = G.shop, type = 'tr', offset = {x = -0.5, y = 6}}, + no_button = true, + button_listen = 'buy_from_shop', + step = step, + }) + step = tutorial_info({ + text_key = 's_3', + loc_vars = {#G.P_CENTER_POOLS.Joker}, + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.jokers.cards[1], + } end, + attach = {major = G.shop, type = 'tm', offset = {x = 0, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_4', + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.jokers.cards[1], + } end, + attach = {major = G.shop, type = 'tm', offset = {x = 0, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_5', + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.jokers, + } end, + attach = {major = G.shop, type = 'tm', offset = {x = 0, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_6', + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.shop_jokers.cards[1], + } end, + snap_to = function() return G.shop_jokers.cards[1] end, + no_button = true, + button_listen = 'buy_from_shop', + attach = {major = G.shop, type = 'tr', offset = {x = -0.5, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_7', + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.consumeables.cards[#G.consumeables.cards], + } end, + attach = {major = G.shop, type = 'tm', offset = {x = 0, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_8', + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.consumeables + } end, + attach = {major = G.shop, type = 'tm', offset = {x = 0, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_9', + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.shop_vouchers, + } end, + snap_to = function() return G.shop_vouchers.cards[1] end, + attach = {major = G.shop, type = 'tr', offset = {x = -4, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_10', + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.shop_vouchers, + } end, + attach = {major = G.shop, type = 'tr', offset = {x = -4, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_11', + highlight = function() return { + G.SHOP_SIGN, + G.HUD:get_UIE_by_ID('dollar_text_UI').parent.parent.parent, + G.shop_booster, + } end, + snap_to = function() return G.shop_booster.cards[1] end, + attach = {major = G.shop, type = 'tl', offset = {x = 3, y = 6}}, + step = step, + }) + step = tutorial_info({ + text_key = 's_12', + highlight = function() return { + G.shop:get_UIE_by_ID('next_round_button'), + } end, + snap_to = function() if G.shop then return G.shop:get_UIE_by_ID('next_round_button') end end, + no_button = true, + button_listen = 'toggle_shop', + attach = {major = G.shop, type = 'tm', offset = {x = 0, y = 6}}, + step = step, + }) + end + + + G.E_MANAGER:add_event(Event({ + blockable = false, + timer = 'REAL', + func = function() + if (G.OVERLAY_TUTORIAL.step == step and + not G.OVERLAY_TUTORIAL.step_complete) or G.OVERLAY_TUTORIAL.skip_steps then + if G.OVERLAY_TUTORIAL.Jimbo then G.OVERLAY_TUTORIAL.Jimbo:remove() end + if G.OVERLAY_TUTORIAL.content then G.OVERLAY_TUTORIAL.content:remove() end + G.OVERLAY_TUTORIAL:remove() + G.OVERLAY_TUTORIAL = nil + G.SETTINGS.tutorial_progress.hold_parts[_part]=true + return true + end + return G.OVERLAY_TUTORIAL.step > step or G.OVERLAY_TUTORIAL.skip_steps + end + }), 'tutorial') + G.SETTINGS.paused = false +end diff --git a/lovely/dump/game.lua b/lovely/dump/game.lua new file mode 100644 index 0000000..3d37b7b --- /dev/null +++ b/lovely/dump/game.lua @@ -0,0 +1,3983 @@ +LOVELY_INTEGRITY = '1f28fb4cd208cf3d7ee40ad59534c6cc25dbf6d4335f376d30f1e4b080e388b7' + +--Class +Game = Object:extend() + +--Class Methods +function Game:init() + G = self + + self:set_globals() +end + +function Game:start_up() + --Load the settings file + local settings = get_compressed('settings.jkr') + local settings_ver = nil + if settings then + local settings_file = STR_UNPACK(settings) + if G.VERSION >= '1.0.0' and (love.system.getOS() == 'NOPE') and ((not settings_file.version) or (settings_file.version < '1.0.0')) then + for i = 1, 3 do + love.filesystem.remove(i..'/'..'profile.jkr') + love.filesystem.remove(i..'/'..'save.jkr') + love.filesystem.remove(i..'/'..'meta.jkr') + love.filesystem.remove(i..'/'..'unlock_notify.jkr') + love.filesystem.remove(i..'') + end + for k, v in pairs(settings_file) do + self.SETTINGS[k] = v + end + self.SETTINGS.profile = 1 + self.SETTINGS.tutorial_progress = nil + else + if G.VERSION < '1.0.0' then + settings_ver = settings_file.version + end + for k, v in pairs(settings_file) do + self.SETTINGS[k] = v + end + end + end + self.SETTINGS.version = settings_ver or G.VERSION + self.SETTINGS.paused = nil + + local new_colour_proto = self.C["SO_"..(self.SETTINGS.colourblind_option and 2 or 1)] + self.C.SUITS.Hearts = new_colour_proto.Hearts + self.C.SUITS.Diamonds = new_colour_proto.Diamonds + self.C.SUITS.Spades = new_colour_proto.Spades + self.C.SUITS.Clubs = new_colour_proto.Clubs + + boot_timer('start', 'settings', 0.1) + + if self.SETTINGS.GRAPHICS.texture_scaling then + self.SETTINGS.GRAPHICS.texture_scaling = self.SETTINGS.GRAPHICS.texture_scaling > 1 and 2 or 1 + end + + if self.SETTINGS.DEMO and not self.F_CTA then + self.SETTINGS.DEMO = { + total_uptime = 0, + timed_CTA_shown = true, + win_CTA_shown = true, + quit_CTA_shown = true + } + end + + --create all sounds from resources and play one each to load into mem + SOURCES = {} + local sound_files = love.filesystem.getDirectoryItems("resources/sounds") + + for _, filename in ipairs(sound_files) do + local extension = string.sub(filename, -4) + if extension == '.ogg' then + local sound_code = string.sub(filename, 1, -5) + SOURCES[sound_code] = {} + end + end + + self.SETTINGS.language = self.SETTINGS.language or 'en-us' + boot_timer('settings', 'window init', 0.2) + self:init_window() + + if G.F_SOUND_THREAD then + boot_timer('window init', 'soundmanager2') + --call the sound manager to prepare the thread to play sounds + self.SOUND_MANAGER = { + thread = love.thread.newThread('engine/sound_manager.lua'), + channel = love.thread.getChannel('sound_request'), + load_channel = love.thread.getChannel('load_channel') + } + self.SOUND_MANAGER.thread:start(1) + + local sound_loaded, prev_file = false, 'none' + while not sound_loaded and false do + --Monitor the channel for any new requests + local request = self.SOUND_MANAGER.load_channel:pop() -- Value from channel + if request then + --If the request is for an update to the music track, handle it here + if request == 'finished' then sound_loaded = true + else + boot_timer(request, prev_file) + prev_file = request + end + end + love.timer.sleep(0.001) + end + + boot_timer('soundmanager2', 'savemanager',0.22) + end + + boot_timer('window init', 'savemanager') + --call the save manager to wait for any save requests + G.SAVE_MANAGER = { + thread = love.thread.newThread([[require "love.system" + + if (love.system.getOS() == 'OS X' ) and (jit.arch == 'arm64' or jit.arch == 'arm') then jit.off() end + + require "love.timer" + require "love.thread" + require 'love.filesystem' + require "engine/object" + require "engine/string_packer" + + --vars needed for sound manager thread + CHANNEL = love.thread.getChannel("save_request") + + talisman = "]] .. Talisman.config_file.break_infinity .. [[" + + --untested + function tal_compress_and_save(_file, _data) + local save_string = type(_data) == 'table' and STR_PACK(_data) or _data + local fallback_save = STR_PACK({GAME = {won = true}}) --just bare minimum to not crash, maybe eventually display some info? + if talisman == 'bignumber' then + fallback_save = "if not BigMeta then " .. fallback_save + elseif talisman == 'omeganum' then + fallback_save = "if not OmegaMeta then " .. fallback_save + else + fallback_save = "if BigMeta or OmegaMeta then " .. fallback_save + end + fallback_save = fallback_save .. " end" + save_string = fallback_save .. " " .. save_string + save_string = love.data.compress('string', 'deflate', save_string, 1) + love.filesystem.write(_file,save_string) + end + + while true do + --Monitor the channel for any new requests + local request = CHANNEL:demand() -- Value from channel + if request then + --Saves progress for settings, unlocks, alerts and discoveries + if request.type == 'save_progress' then + local prefix_profile = (request.save_progress.SETTINGS.profile or 1)..'' + if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end + prefix_profile = prefix_profile..'/' + + if not love.filesystem.getInfo(prefix_profile..'meta.jkr') then + love.filesystem.append( prefix_profile..'meta.jkr', 'return {}' ) + end + + local meta = STR_UNPACK(get_compressed(prefix_profile..'meta.jkr') or 'return {}') + meta.unlocked = meta.unlocked or {} + meta.discovered = meta.discovered or {} + meta.alerted = meta.alerted or {} + + local _append = false + + for k, v in pairs(request.save_progress.UDA) do + if string.find(v, 'u') and not meta.unlocked[k] then + meta.unlocked[k] = true + _append = true + end + if string.find(v, 'd') and not meta.discovered[k] then + meta.discovered[k] = true + _append = true + end + if string.find(v, 'a') and not meta.alerted[k] then + meta.alerted[k] = true + _append = true + end + end + if _append then compress_and_save( prefix_profile..'meta.jkr', STR_PACK(meta)) end + + compress_and_save('settings.jkr', request.save_progress.SETTINGS) + compress_and_save(prefix_profile..'profile.jkr', request.save_progress.PROFILE) + + CHANNEL:push('done') + --Saves the settings file + elseif request.type == 'save_settings' then + compress_and_save('settings.jkr', request.save_settings) + compress_and_save(request.profile_num..'/profile.jkr', request.save_profile) + --Saves the metrics file + elseif request.type == 'save_metrics' then + compress_and_save('metrics.jkr', request.save_metrics) + --Saves any notifications + elseif request.type == 'save_notify' then + local prefix_profile = (request.profile_num or 1)..'' + if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end + prefix_profile = prefix_profile..'/' + + if not love.filesystem.getInfo(prefix_profile..'unlock_notify.jkr') then love.filesystem.append( prefix_profile..'unlock_notify.jkr', '') end + local unlock_notify = get_compressed(prefix_profile..'unlock_notify.jkr') or '' + + if request.save_notify and not string.find(unlock_notify, request.save_notify) then + compress_and_save( prefix_profile..'unlock_notify.jkr', unlock_notify..request.save_notify..'\n') + end + + --Saves the run + elseif request.type == 'save_run' then + local prefix_profile = (request.profile_num or 1)..'' + if not love.filesystem.getInfo(prefix_profile) then love.filesystem.createDirectory( prefix_profile ) end + prefix_profile = prefix_profile..'/' + + tal_compress_and_save(prefix_profile..'save.jkr', request.save_table) + end + end + end]]), + channel = love.thread.getChannel('save_request') + } + G.SAVE_MANAGER.thread:start(2) + boot_timer('savemanager', 'shaders',0.4) + + --call the http manager + G.HTTP_MANAGER = { + thread = love.thread.newThread('engine/http_manager.lua'), + out_channel = love.thread.getChannel('http_request'), + in_channel = love.thread.getChannel('http_response') + } + if G.F_HTTP_SCORES then + G.HTTP_MANAGER.thread:start() + end + + --Load all shaders from resources + self.SHADERS = {} + local shader_files = love.filesystem.getDirectoryItems("resources/shaders") + for k, filename in ipairs(shader_files) do + local extension = string.sub(filename, -3) + if extension == '.fs' then + local shader_name = string.sub(filename, 1, -4) + self.SHADERS[shader_name] = love.graphics.newShader("resources/shaders/"..filename) + end + end + + boot_timer('shaders', 'controllers',0.7) + + --Input handler/controller for game objects + self.CONTROLLER = Controller() + love.joystick.loadGamepadMappings("resources/gamecontrollerdb.txt") + if self.F_RUMBLE then + local joysticks = love.joystick.getJoysticks() + if joysticks then + if joysticks[1] then + self.CONTROLLER:set_gamepad(joysticks[2] or joysticks[1]) + end + end + end + boot_timer('controllers', 'localization',0.8) + + if self.SETTINGS.GRAPHICS.texture_scaling then + self.SETTINGS.GRAPHICS.texture_scaling = self.SETTINGS.GRAPHICS.texture_scaling > 1 and 2 or 1 + end + + self:load_profile(G.SETTINGS.profile or 1) + + self.SETTINGS.QUEUED_CHANGE = {} + self.SETTINGS.music_control = {desired_track = '', current_track = '', lerp = 1} + + self:set_render_settings() + + self:set_language() + + self:init_item_prototypes() + boot_timer('protos', 'shared sprites',0.9) + + --For globally shared sprites + self.shared_debuff = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["centers"], {x=4, y = 0}) + + self.shared_soul = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["centers"], self.P_CENTERS.soul.pos) + self.shared_undiscovered_joker = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["centers"], self.P_CENTERS.undiscovered_joker.pos) + self.shared_undiscovered_tarot = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["centers"], self.P_CENTERS.undiscovered_tarot.pos) + + self.shared_sticker_eternal = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 0,y = 0}) + self.shared_sticker_perishable = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 0,y = 2}) + self.shared_sticker_rental = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 1,y = 2}) + + self.shared_stickers = { + White = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 1,y = 0}), + Red = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 2,y = 0}), + Green = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 3,y = 0}), + Black = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 0,y = 1}), + Blue = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 4,y = 0}), + Purple = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 1,y = 1}), + Orange = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 2,y = 1}), + Gold = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["stickers"], {x = 3,y = 1}) + } + self.shared_seals = { + Gold = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["centers"], {x = 2,y = 0}), + Purple = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["centers"], {x = 4,y = 4}), + Red = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["centers"], {x = 5,y = 4}), + Blue = Sprite(0, 0, self.CARD_W, self.CARD_H, self.ASSET_ATLAS["centers"], {x = 6,y = 4}), + } + self.sticker_map = { + 'White','Red','Green','Black','Blue','Purple','Orange','Gold' + } + boot_timer('shared sprites', 'prep stage',0.95) + + --For the visible cursor + G.STAGE_OBJECT_INTERRUPT =true + self.CURSOR = Sprite(0,0,0.3, 0.3, self.ASSET_ATLAS['gamepad_ui'], {x = 18, y = 0}) + self.CURSOR.states.collide.can = false + G.STAGE_OBJECT_INTERRUPT = false + + --Create the event manager for the game + self.E_MANAGER = EventManager() + self.SPEEDFACTOR = 1 + initSteamodded() + + for i = 1, #G.CHALLENGES do + if (G.CHALLENGES[i].id == 'c_cry_rush_hour' or G.CHALLENGES[i].id == 'c_cry_rush_hour_ii' or G.CHALLENGES[i].id == 'c_cry_rush_hour_iii') and #G.CHALLENGES[i].restrictions.banned_other == 0 then + for k, v in pairs(G.P_BLINDS) do + if k ~= "bl_cry_clock" and k ~= "bl_cry_lavender_loop" and v.boss then + G.CHALLENGES[i].restrictions.banned_other[#G.CHALLENGES[i].restrictions.banned_other+1] = {id = k, type = 'blind'} + end + end + end + end + set_profile_progress() + Cartomancer.load_mod_file('internal/localization.lua', 'localization') + boot_timer('prep stage', 'splash prep',1) + self:splash_screen() + boot_timer('splash prep', 'end',1) +end + +function Game:init_item_prototypes() + --Initialize all prototypes for units/items + self.P_SEALS = { + Red = {order = 1, discovered = false, set = "Seal"}, + Blue = {order = 2, discovered = false, set = "Seal"}, + Gold = {order = 3, discovered = false, set = "Seal"}, + Purple = {order = 4, discovered = false, set = "Seal"}, + } + + self.P_TAGS = { + tag_uncommon = {name = 'Uncommon Tag', set = 'Tag', discovered = false, min_ante = nil, order = 1, config = {type = 'store_joker_create'}, pos = {x = 0,y = 0}}, + tag_rare = {name = 'Rare Tag', set = 'Tag', discovered = false, min_ante = nil, order = 2, config = {type = 'store_joker_create', odds = 3}, requires = 'j_blueprint', pos = {x = 1,y = 0}}, + tag_negative = {name = 'Negative Tag', set = 'Tag', discovered = false, min_ante = 2, order = 3, config = {type = 'store_joker_modify', edition = 'negative', odds = 5}, requires = 'e_negative', pos = {x = 2, y = 0}}, + tag_foil = {name = 'Foil Tag', set = 'Tag', discovered = false, min_ante = nil, order = 4, config = {type = 'store_joker_modify', edition = 'foil', odds = 2}, requires = 'e_foil', pos = {x = 3,y = 0}}, + tag_holo = {name = 'Holographic Tag', set = 'Tag', discovered = false, min_ante = nil, order = 5, config = {type = 'store_joker_modify', edition = 'holo', odds = 3}, requires = 'e_holo', pos = {x = 0,y = 1}}, + tag_polychrome = {name = 'Polychrome Tag', set = 'Tag', discovered = false, min_ante = nil, order = 6, config = {type = 'store_joker_modify', edition = 'polychrome', odds = 4}, requires = 'e_polychrome', pos = {x = 1,y = 1}}, + tag_investment = {name = 'Investment Tag', set = 'Tag', discovered = false, min_ante = nil, order = 7, config = {type = 'eval', dollars = 25}, pos = {x = 2,y = 1}}, + tag_voucher = {name = 'Voucher Tag', set = 'Tag', discovered = false, min_ante = nil, order = 8, config = {type = 'voucher_add'}, pos = {x = 3,y = 1}}, + tag_boss = {name = 'Boss Tag', set = 'Tag', discovered = false, min_ante = nil, order = 9, config = {type = 'new_blind_choice', }, pos = {x = 0,y = 2}}, + tag_standard = {name = 'Standard Tag', set = 'Tag', discovered = false, min_ante = 2, order = 10, config = {type = 'new_blind_choice', }, pos = {x = 1,y = 2}}, + tag_charm = {name = 'Charm Tag', set = 'Tag', discovered = false, min_ante = nil, order = 11, config = {type = 'new_blind_choice', }, pos = {x = 2,y = 2}}, + tag_meteor = {name = 'Meteor Tag', set = 'Tag', discovered = false, min_ante = 2, order = 12, config = {type = 'new_blind_choice', }, pos = {x = 3,y = 2}}, + tag_buffoon = {name = 'Buffoon Tag', set = 'Tag', discovered = false, min_ante = 2, order = 13, config = {type = 'new_blind_choice', }, pos = {x = 4,y = 2}}, + tag_handy = {name = 'Handy Tag', set = 'Tag', discovered = false, min_ante = 2, order = 14, config = {type = 'immediate', dollars_per_hand = 1}, pos = {x = 1,y = 3}}, + tag_garbage = {name = 'Garbage Tag', set = 'Tag', discovered = false, min_ante = 2, order = 15, config = {type = 'immediate', dollars_per_discard = 1}, pos = {x = 2,y = 3}}, + tag_ethereal = {name = 'Ethereal Tag', set = 'Tag', discovered = false, min_ante = 2, order = 16, config = {type = 'new_blind_choice'}, pos = {x = 3,y = 3}}, + tag_coupon = {name = 'Coupon Tag', set = 'Tag', discovered = false, min_ante = nil, order = 17, config = {type = 'shop_final_pass', }, pos = {x = 4,y = 0}}, + tag_double = {name = 'Double Tag', set = 'Tag', discovered = false, min_ante = nil, order = 18, config = {type = 'tag_add', }, pos = {x = 5,y = 0}}, + tag_juggle = {name = 'Juggle Tag', set = 'Tag', discovered = false, min_ante = nil, order = 19, config = {type = 'round_start_bonus', h_size = 3}, pos = {x = 5,y = 1}}, + tag_d_six = {name = 'D6 Tag', set = 'Tag', discovered = false, min_ante = nil, order = 20, config = {type = 'shop_start', }, pos = {x = 5,y = 3}}, + tag_top_up = {name = 'Top-up Tag', set = 'Tag', discovered = false, min_ante = 2, order = 21, config = {type = 'immediate', spawn_jokers = 2}, pos = {x = 4,y = 1}}, + tag_skip = {name = 'Skip Tag', set = 'Tag', discovered = false, min_ante = nil, order = 22, config = {type = 'immediate', skip_bonus = 5}, pos = {x = 0,y = 3}}, + tag_orbital = {name = 'Orbital Tag', set = 'Tag', discovered = false, min_ante = 2, order = 23, config = {type = 'immediate', levels = 3}, pos = {x = 5,y = 2}}, + tag_economy = {name = 'Economy Tag', set = 'Tag', discovered = false, min_ante = nil, order = 24, config = {type = 'immediate', max = 40}, pos = {x = 4,y = 3}}, + } + self.tag_undiscovered = {name = 'Not Discovered', order = 1, config = {type = ''}, pos = {x=3,y=4}} + + self.P_STAKES = { + stake_white = {name = 'White Chip', unlocked = true, order = 1, pos = {x = 0,y = 0}, stake_level = 1, set = 'Stake'}, + stake_red = {name = 'Red Chip', unlocked = false, order = 2, pos = {x = 1,y = 0}, stake_level = 2, set = 'Stake'}, + stake_green = {name = 'Green Chip', unlocked = false, order = 3, pos = {x = 2,y = 0}, stake_level = 3, set = 'Stake'}, + stake_black = {name = 'Black Chip', unlocked = false, order = 4, pos = {x = 4,y = 0}, stake_level = 4, set = 'Stake'}, + stake_blue = {name = 'Blue Chip', unlocked = false, order = 5, pos = {x = 3,y = 0}, stake_level = 5, set = 'Stake'}, + stake_purple = {name = 'Purple Chip', unlocked = false, order = 6, pos = {x = 0,y = 1}, stake_level = 6, set = 'Stake'}, + stake_orange = {name = 'Orange Chip', unlocked = false, order = 7, pos = {x = 1,y = 1}, stake_level = 7, set = 'Stake'}, + stake_gold = {name = 'Gold Chip', unlocked = false, order = 8, pos = {x = 2,y = 1}, stake_level = 8, set = 'Stake'}, + } + + self.P_BLINDS = { + bl_small = {name = 'Small Blind', defeated = false, order = 1, dollars = 3, mult = 1, vars = {}, debuff_text = '', debuff = {}, pos = {x=0, y=0}}, + bl_big = {name = 'Big Blind', defeated = false, order = 2, dollars = 4, mult = 1.5,vars = {}, debuff_text = '', debuff = {}, pos = {x=0, y=1}}, + bl_ox = {name = 'The Ox', defeated = false, order = 4, dollars = 5, mult = 2, vars = {localize('ph_most_played')}, debuff = {}, pos = {x=0, y=2}, boss = {min = 6, max = 10}, boss_colour = HEX('b95b08')}, + bl_hook = {name = 'The Hook', defeated = false, order = 3, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=7}, boss = {min = 1, max = 10}, boss_colour = HEX('a84024')}, + bl_mouth = {name = 'The Mouth', defeated = false, order = 17, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=18}, boss = {min = 2, max = 10}, boss_colour = HEX('ae718e')}, + bl_fish = {name = 'The Fish', defeated = false, order = 10, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=5}, boss = {min = 2, max = 10}, boss_colour = HEX('3e85bd')}, + bl_club = {name = 'The Club', defeated = false, order = 9, dollars = 5, mult = 2, vars = {}, debuff = {suit = 'Clubs'}, pos = {x=0, y=4}, boss = {min = 1, max = 10}, boss_colour = HEX('b9cb92')}, + bl_manacle = {name = 'The Manacle', defeated = false, order = 15, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=8}, boss = {min = 1, max = 10}, boss_colour = HEX('575757')}, + bl_tooth = {name = 'The Tooth', defeated = false, order = 23, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=22}, boss = {min = 3, max = 10}, boss_colour = HEX('b52d2d')}, + bl_wall = {name = 'The Wall', defeated = false, order = 6, dollars = 5, mult = 4, vars = {}, debuff = {}, pos = {x=0, y=9}, boss = {min = 2, max = 10}, boss_colour = HEX('8a59a5')}, + bl_house = {name = 'The House', defeated = false, order = 5, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=3}, boss ={min = 2, max = 10}, boss_colour = HEX('5186a8')}, + bl_mark = {name = 'The Mark', defeated = false, order = 25, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=23}, boss = {min = 2, max = 10}, boss_colour = HEX('6a3847')}, + + bl_final_bell = {name = 'Cerulean Bell',defeated = false, order = 30, dollars = 8, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=26}, boss = {showdown = true, min = 10, max = 10}, boss_colour = HEX('009cfd')}, + bl_wheel = {name = 'The Wheel', defeated = false, order = 7, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=10}, boss = {min = 2, max = 10}, boss_colour = HEX('50bf7c')}, + bl_arm = {name = 'The Arm', defeated = false, order = 8, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=11}, boss = {min = 2, max = 10}, boss_colour = HEX('6865f3')}, + bl_psychic = {name = 'The Psychic', defeated = false, order = 11, dollars = 5, mult = 2, vars = {}, debuff = {h_size_ge = 5}, pos = {x=0, y=12}, boss = {min = 1, max = 10}, boss_colour = HEX('efc03c')}, + bl_goad = {name = 'The Goad', defeated = false, order = 12, dollars = 5, mult = 2, vars = {}, debuff = {suit = 'Spades'}, pos = {x=0, y=13}, boss = {min = 1, max = 10}, boss_colour = HEX('b95c96')}, + bl_water = {name = 'The Water', defeated = false, order = 13, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=14}, boss = {min = 2, max = 10}, boss_colour = HEX('c6e0eb')}, + bl_eye = {name = 'The Eye', defeated = false, order = 16, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=17}, boss = {min = 3, max = 10}, boss_colour = HEX('4b71e4')}, + bl_plant = {name = 'The Plant', defeated = false, order = 18, dollars = 5, mult = 2, vars = {}, debuff = {is_face = 'face'}, pos = {x=0, y=19}, boss = {min = 4, max = 10}, boss_colour = HEX('709284')}, + bl_needle = {name = 'The Needle', defeated = false, order = 21, dollars = 5, mult = 1, vars = {}, debuff = {}, pos = {x=0, y=20}, boss = {min = 2, max = 10}, boss_colour = HEX('5c6e31')}, + bl_head = {name = 'The Head', defeated = false, order = 22, dollars = 5, mult = 2, vars = {}, debuff = {suit = 'Hearts'}, pos = {x=0, y=21}, boss = {min = 1, max = 10}, boss_colour = HEX('ac9db4')}, + bl_final_leaf = {name = 'Verdant Leaf', defeated = false, order = 27, dollars = 8, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=28}, boss = {showdown = true, min = 10, max = 10}, boss_colour = HEX('56a786')}, + bl_final_vessel = {name = 'Violet Vessel',defeated = false, order = 28, dollars = 8, mult = 6, vars = {}, debuff = {}, pos = {x=0, y=29}, boss = {showdown = true, min = 10, max = 10}, boss_colour = HEX('8a71e1')}, + bl_window = {name = 'The Window', defeated = false, order = 14, dollars = 5, mult = 2, vars = {}, debuff = {suit = 'Diamonds'}, pos = {x=0, y=6}, boss = {min = 1, max = 10}, boss_colour = HEX('a9a295')}, + bl_serpent = {name = 'The Serpent', defeated = false, order = 19, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=15}, boss = {min = 5, max = 10}, boss_colour = HEX('439a4f')}, + bl_pillar = {name = 'The Pillar', defeated = false, order = 20, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=16}, boss = {min = 1, max = 10}, boss_colour = HEX('7e6752')}, + bl_flint = {name = 'The Flint', defeated = false, order = 24, dollars = 5, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=24}, boss = {min = 2, max = 10}, boss_colour = HEX('e56a2f')}, + bl_final_acorn = {name = 'Amber Acorn', defeated = false, order = 26, dollars = 8, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=27}, boss = {showdown = true, min = 10, max = 10}, boss_colour = HEX('fda200')}, + bl_final_heart = {name = 'Crimson Heart',defeated = false, order = 29, dollars = 8, mult = 2, vars = {}, debuff = {}, pos = {x=0, y=25}, boss = {showdown = true, min = 10, max = 10}, boss_colour = HEX('ac3232')}, + + } + self.b_undiscovered = {name = 'Undiscovered', debuff_text = 'Defeat this blind to discover', pos = {x=0,y=30}} + + self.P_CARDS = { + H_2={name = "2 of Hearts",value = '2', suit = 'Hearts', pos = {x=0,y=0}}, + H_3={name = "3 of Hearts",value = '3', suit = 'Hearts', pos = {x=1,y=0}}, + H_4={name = "4 of Hearts",value = '4', suit = 'Hearts', pos = {x=2,y=0}}, + H_5={name = "5 of Hearts",value = '5', suit = 'Hearts', pos = {x=3,y=0}}, + H_6={name = "6 of Hearts",value = '6', suit = 'Hearts', pos = {x=4,y=0}}, + H_7={name = "7 of Hearts",value = '7', suit = 'Hearts', pos = {x=5,y=0}}, + H_8={name = "8 of Hearts",value = '8', suit = 'Hearts', pos = {x=6,y=0}}, + H_9={name = "9 of Hearts",value = '9', suit = 'Hearts', pos = {x=7,y=0}}, + H_T={name = "10 of Hearts",value = '10', suit = 'Hearts', pos = {x=8,y=0}}, + H_J={name = "Jack of Hearts",value = 'Jack', suit = 'Hearts', pos = {x=9,y=0}}, + H_Q={name = "Queen of Hearts",value = 'Queen', suit = 'Hearts', pos = {x=10,y=0}}, + H_K={name = "King of Hearts",value = 'King', suit = 'Hearts', pos = {x=11,y=0}}, + H_A={name = "Ace of Hearts",value = 'Ace', suit = 'Hearts', pos = {x=12,y=0}}, + C_2={name = "2 of Clubs",value = '2', suit = 'Clubs', pos = {x=0,y=1}}, + C_3={name = "3 of Clubs",value = '3', suit = 'Clubs', pos = {x=1,y=1}}, + C_4={name = "4 of Clubs",value = '4', suit = 'Clubs', pos = {x=2,y=1}}, + C_5={name = "5 of Clubs",value = '5', suit = 'Clubs', pos = {x=3,y=1}}, + C_6={name = "6 of Clubs",value = '6', suit = 'Clubs', pos = {x=4,y=1}}, + C_7={name = "7 of Clubs",value = '7', suit = 'Clubs', pos = {x=5,y=1}}, + C_8={name = "8 of Clubs",value = '8', suit = 'Clubs', pos = {x=6,y=1}}, + C_9={name = "9 of Clubs",value = '9', suit = 'Clubs', pos = {x=7,y=1}}, + C_T={name = "10 of Clubs",value = '10', suit = 'Clubs', pos = {x=8,y=1}}, + C_J={name = "Jack of Clubs",value = 'Jack', suit = 'Clubs', pos = {x=9,y=1}}, + C_Q={name = "Queen of Clubs",value = 'Queen', suit = 'Clubs', pos = {x=10,y=1}}, + C_K={name = "King of Clubs",value = 'King', suit = 'Clubs', pos = {x=11,y=1}}, + C_A={name = "Ace of Clubs",value = 'Ace', suit = 'Clubs', pos = {x=12,y=1}}, + D_2={name = "2 of Diamonds",value = '2', suit = 'Diamonds', pos = {x=0,y=2}}, + D_3={name = "3 of Diamonds",value = '3', suit = 'Diamonds', pos = {x=1,y=2}}, + D_4={name = "4 of Diamonds",value = '4', suit = 'Diamonds', pos = {x=2,y=2}}, + D_5={name = "5 of Diamonds",value = '5', suit = 'Diamonds', pos = {x=3,y=2}}, + D_6={name = "6 of Diamonds",value = '6', suit = 'Diamonds', pos = {x=4,y=2}}, + D_7={name = "7 of Diamonds",value = '7', suit = 'Diamonds', pos = {x=5,y=2}}, + D_8={name = "8 of Diamonds",value = '8', suit = 'Diamonds', pos = {x=6,y=2}}, + D_9={name = "9 of Diamonds",value = '9', suit = 'Diamonds', pos = {x=7,y=2}}, + D_T={name = "10 of Diamonds",value = '10', suit = 'Diamonds', pos = {x=8,y=2}}, + D_J={name = "Jack of Diamonds",value = 'Jack', suit = 'Diamonds', pos = {x=9,y=2}}, + D_Q={name = "Queen of Diamonds",value = 'Queen', suit = 'Diamonds', pos = {x=10,y=2}}, + D_K={name = "King of Diamonds",value = 'King', suit = 'Diamonds', pos = {x=11,y=2}}, + D_A={name = "Ace of Diamonds",value = 'Ace', suit = 'Diamonds', pos = {x=12,y=2}}, + S_2={name = "2 of Spades",value = '2', suit = 'Spades', pos = {x=0,y=3}}, + S_3={name = "3 of Spades",value = '3', suit = 'Spades', pos = {x=1,y=3}}, + S_4={name = "4 of Spades",value = '4', suit = 'Spades', pos = {x=2,y=3}}, + S_5={name = "5 of Spades",value = '5', suit = 'Spades', pos = {x=3,y=3}}, + S_6={name = "6 of Spades",value = '6', suit = 'Spades', pos = {x=4,y=3}}, + S_7={name = "7 of Spades",value = '7', suit = 'Spades', pos = {x=5,y=3}}, + S_8={name = "8 of Spades",value = '8', suit = 'Spades', pos = {x=6,y=3}}, + S_9={name = "9 of Spades",value = '9', suit = 'Spades', pos = {x=7,y=3}}, + S_T={name = "10 of Spades",value = '10', suit = 'Spades', pos = {x=8,y=3}}, + S_J={name = "Jack of Spades",value = 'Jack', suit = 'Spades', pos = {x=9,y=3}}, + S_Q={name = "Queen of Spades",value = 'Queen', suit = 'Spades', pos = {x=10,y=3}}, + S_K={name = "King of Spades",value = 'King', suit = 'Spades', pos = {x=11,y=3}}, + S_A={name = "Ace of Spades",value = 'Ace', suit = 'Spades', pos = {x=12,y=3}}, + } + + self.j_locked = {unlocked = false, max = 1, name = "Locked", pos = {x=8,y=9}, set = "Joker", cost_mult = 1.0,config = {}} + self.v_locked = {unlocked = false, max = 1, name = "Locked", pos = {x=8,y=3}, set = "Voucher", cost_mult = 1.0,config = {}} + self.c_locked = {unlocked = false, max = 1, name = "Locked", pos = {x=4,y=2}, set = "Tarot", cost_mult = 1.0,config = {}} + self.j_undiscovered = {unlocked = false, max = 1, name = "Locked", pos = {x=9,y=9}, set = "Joker", cost_mult = 1.0,config = {}} + self.t_undiscovered = {unlocked = false, max = 1, name = "Locked", pos = {x=6,y=2}, set = "Tarot", cost_mult = 1.0,config = {}} + self.p_undiscovered = {unlocked = false, max = 1, name = "Locked", pos = {x=7,y=2}, set = "Planet", cost_mult = 1.0,config = {}} + self.s_undiscovered = {unlocked = false, max = 1, name = "Locked", pos = {x=5,y=2}, set = "Spectral", cost_mult = 1.0,config = {}} + self.v_undiscovered = {unlocked = false, max = 1, name = "Locked", pos = {x=8,y=2}, set = "Voucher", cost_mult = 1.0,config = {}} + self.booster_undiscovered = {unlocked = false, max = 1, name = "Locked", pos = {x=0,y=5}, set = "Booster", cost_mult = 1.0,config = {}} + + self.P_CENTERS = { + c_base={max = 500, freq = 1, line = 'base', name = "Default Base", pos = {x=1,y=0}, set = "Default", label = 'Base Card', effect = "Base", cost_mult = 1.0, config = {}}, + + --Jokers + j_joker= {order = 1, unlocked = true, start_alerted = true, discovered = true, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 2, name = "Joker", pos = {x=0,y=0}, set = "Joker", effect = "Mult", cost_mult = 1.0, config = {mult = 4}}, + j_greedy_joker= {order = 2, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Greedy Joker", pos = {x=6,y=1}, set = "Joker", effect = "Suit Mult", cost_mult = 1.0, config = {extra = {s_mult = 3, suit = 'Diamonds'}}}, + j_lusty_joker= {order = 3, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Lusty Joker", pos = {x=7,y=1}, set = "Joker", effect = "Suit Mult", cost_mult = 1.0, config = {extra = {s_mult = 3, suit = 'Hearts'}}}, + j_wrathful_joker= {order = 4, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Wrathful Joker", pos = {x=8,y=1}, set = "Joker", effect = "Suit Mult", cost_mult = 1.0, config = {extra = {s_mult = 3, suit = 'Spades'}}}, + j_gluttenous_joker= {order = 5, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Gluttonous Joker", pos = {x=9,y=1}, set = "Joker", effect = "Suit Mult", cost_mult = 1.0, config = {extra = {s_mult = 3, suit = 'Clubs'}}}, + j_jolly= {order = 6, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 3, name = "Jolly Joker", pos = {x=2,y=0}, set = "Joker", effect = "Type Mult", cost_mult = 1.0, config = {t_mult = 8, type = 'Pair'}}, + j_zany= {order = 7, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Zany Joker", pos = {x=3,y=0}, set = "Joker", effect = "Type Mult", cost_mult = 1.0, config = {t_mult = 12, type = 'Three of a Kind'}}, + j_mad= {order = 8, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Mad Joker", pos = {x=4,y=0}, set = "Joker", effect = "Type Mult", cost_mult = 1.0, config = {t_mult = 10, type = 'Two Pair'}}, + j_crazy= {order = 9, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Crazy Joker", pos = {x=5,y=0}, set = "Joker", effect = "Type Mult", cost_mult = 1.0, config = {t_mult = 12, type = 'Straight'}}, + j_droll= {order = 10, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Droll Joker", pos = {x=6,y=0}, set = "Joker", effect = "Type Mult", cost_mult = 1.0, config = {t_mult = 10, type = 'Flush'}}, + j_sly= {order = 11, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 3, name = "Sly Joker",set = "Joker", config = {t_chips = 50, type = 'Pair'}, pos = {x=0,y=14}}, + j_wily= {order = 12, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Wily Joker",set = "Joker", config = {t_chips = 100, type = 'Three of a Kind'}, pos = {x=1,y=14}}, + j_clever= {order = 13, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Clever Joker",set = "Joker", config = {t_chips = 80, type = 'Two Pair'}, pos = {x=2,y=14}}, + j_devious= {order = 14, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Devious Joker",set = "Joker", config = {t_chips = 100, type = 'Straight'}, pos = {x=3,y=14}}, + j_crafty= {order = 15, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Crafty Joker",set = "Joker", config = {t_chips = 80, type = 'Flush'}, pos = {x=4,y=14}}, + + j_half= {order = 16, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Half Joker", pos = {x=7,y=0}, set = "Joker", effect = "Hand Size Mult", cost_mult = 1.0, config = {extra = {mult = 20, size = 3}}}, + j_stencil= {order = 17, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 8, name = "Joker Stencil", pos = {x=2,y=5}, set = "Joker", effect = "Hand Size Mult", cost_mult = 1.0, config = {}}, + j_four_fingers= {order = 18, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Four Fingers", pos = {x=6,y=6}, set = "Joker", effect = "", config = {}}, + j_mime= {order = 19, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 5, name = "Mime", pos = {x=4,y=1}, set = "Joker", effect = "Hand card double", cost_mult = 1.0, config = {extra = 1}}, + j_credit_card= {order = 20, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 1, name = "Credit Card", pos = {x=5,y=1}, set = "Joker", effect = "Credit", cost_mult = 1.0, config = {extra = 20}}, + j_ceremonial= {order = 21, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 6, name = "Ceremonial Dagger", pos = {x=5,y=5}, set = "Joker", effect = "", config = {mult = 0}}, + j_banner= {order = 22, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Banner", pos = {x=1,y=2}, set = "Joker", effect = "Discard Chips", cost_mult = 1.0, config = {extra = 30}}, + j_mystic_summit= {order = 23, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Mystic Summit", pos = {x=2,y=2}, set = "Joker", effect = "No Discard Mult", cost_mult = 1.0, config = {extra = {mult = 15, d_remaining = 0}}}, + j_marble= {order = 24, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Marble Joker", pos = {x=3,y=2}, set = "Joker", effect = "Stone card hands", cost_mult = 1.0, config = {extra = 1}}, + j_loyalty_card= {order = 25, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 5, name = "Loyalty Card", pos = {x=4,y=2}, set = "Joker", effect = "1 in 10 mult", cost_mult = 1.0, config = {extra = {Xmult = 4, every = 5, remaining = "5 remaining"}}}, + j_8_ball= {order = 26, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "8 Ball", pos = {x=0,y=5}, set = "Joker", effect = "Spawn Tarot", cost_mult = 1.0, config = {extra=4}}, + j_misprint= {order = 27, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Misprint", pos = {x=6,y=2}, set = "Joker", effect = "Random Mult", cost_mult = 1.0, config = {extra = {max = 23, min = 0}}}, + j_dusk= {order = 28, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 5, name = "Dusk", pos = {x=4,y=7}, set = "Joker", effect = "", config = {extra = 1}, unlock_condition = {type = '', extra = '', hidden = true}}, + j_raised_fist= {order = 29, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Raised Fist", pos = {x=8,y=2}, set = "Joker", effect = "Socialized Mult", cost_mult = 1.0, config = {}}, + j_chaos= {order = 30, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Chaos the Clown", pos = {x=1,y=0}, set = "Joker", effect = "Bonus Rerolls", cost_mult = 1.0, config = {extra = 1}}, + + j_fibonacci= {order = 31, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 8, name = "Fibonacci", pos = {x=1,y=5}, set = "Joker", effect = "Card Mult", cost_mult = 1.0, config = {extra = 8}}, + j_steel_joker= {order = 32, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Steel Joker", pos = {x=7,y=2}, set = "Joker", effect = "Steel Card Buff", cost_mult = 1.0, config = {extra = 0.2}, enhancement_gate = 'm_steel'}, + j_scary_face= {order = 33, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Scary Face", pos = {x=2,y=3}, set = "Joker", effect = "Scary Face Cards", cost_mult = 1.0, config = {extra = 30}}, + j_abstract= {order = 34, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Abstract Joker", pos = {x=3,y=3}, set = "Joker", effect = "Joker Mult", cost_mult = 1.0, config = {extra = 3}}, + j_delayed_grat= {order = 35, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Delayed Gratification", pos = {x=4,y=3}, set = "Joker", effect = "Discard dollars", cost_mult = 1.0, config = {extra = 2}}, + j_hack= {order = 36, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Hack", pos = {x=5,y=2}, set = "Joker", effect = "Low Card double", cost_mult = 1.0, config = {extra = 1}}, + j_pareidolia= {order = 37, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 5, name = "Pareidolia", pos = {x=6,y=3}, set = "Joker", effect = "All face cards", cost_mult = 1.0, config = {}}, + j_gros_michel= {order = 38, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = false, rarity = 1, cost = 5, name = "Gros Michel", pos = {x=7,y=6}, set = "Joker", effect = "", config = {extra = {odds = 6, mult = 15}}, no_pool_flag = 'gros_michel_extinct'}, + j_even_steven= {order = 39, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Even Steven", pos = {x=8,y=3}, set = "Joker", effect = "Even Card Buff", cost_mult = 1.0, config = {extra = 4}}, + j_odd_todd= {order = 40, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Odd Todd", pos = {x=9,y=3}, set = "Joker", effect = "Odd Card Buff", cost_mult = 1.0, config = {extra = 31}}, + j_scholar= {order = 41, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Scholar", pos = {x=0,y=4}, set = "Joker", effect = "Ace Buff", cost_mult = 1.0, config = {extra = {mult = 4, chips = 20}}}, + j_business= {order = 42, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Business Card", pos = {x=1,y=4}, set = "Joker", effect = "Face Card dollar Chance", cost_mult = 1.0, config = {extra = 2}}, + j_supernova= {order = 43, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Supernova", pos = {x=2,y=4}, set = "Joker", effect = "Hand played mult", cost_mult = 1.0, config = {extra = 1}}, + j_ride_the_bus= {order = 44, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 1, cost = 6, name = "Ride the Bus", pos = {x=1,y=6}, set = "Joker", effect = "", config = {extra = 1}, unlock_condition = {type = 'discard_custom'}}, + j_space= {order = 45, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 5, name = "Space Joker", pos = {x=3,y=5}, set = "Joker", effect = "Upgrade Hand chance", cost_mult = 1.0, config = {extra = 4}}, + + j_egg= {order = 46, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = 'Egg', pos = {x = 0, y = 10}, set = 'Joker', config = {extra = 3}}, + j_burglar= {order = 47, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = 'Burglar', pos = {x = 1, y = 10}, set = 'Joker', config = {extra = 3}}, + j_blackboard= {order = 48, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = 'Blackboard', pos = {x = 2, y = 10}, set = 'Joker', config = {extra = 3}}, + j_runner= {order = 49, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 1, cost = 5, name = 'Runner', pos = {x = 3, y = 10}, set = 'Joker', config = {extra = {chips = 0, chip_mod = 15}}}, + j_ice_cream= {order = 50, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = false, rarity = 1, cost = 5, name = 'Ice Cream', pos = {x = 4, y = 10}, set = 'Joker', config = {extra = {chips = 100, chip_mod = 5}}}, + j_dna= {order = 51, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = 'DNA', pos = {x = 5, y = 10}, set = 'Joker', config = {}}, + j_splash= {order = 52, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 3, name = 'Splash', pos = {x = 6, y = 10}, set = 'Joker', config = {}}, + j_blue_joker= {order = 53, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = 'Blue Joker', pos = {x = 7, y = 10}, set = 'Joker', config = {extra = 2}}, + j_sixth_sense= {order = 54, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = 'Sixth Sense', pos = {x = 8, y = 10}, set = 'Joker', config = {}}, + j_constellation= {order = 55, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 6, name = 'Constellation', pos = {x = 9, y = 10}, set = 'Joker', config = {extra = 0.1, Xmult = 1}}, + j_hiker= {order = 56, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 5, name = 'Hiker', pos = {x = 0, y = 11}, set = 'Joker', config = {extra = 5}}, + j_faceless= {order = 57, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = 'Faceless Joker', pos = {x = 1, y = 11}, set = 'Joker', config = {extra = {dollars = 5, faces = 3}}}, + j_green_joker= {order = 58, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 1, cost = 4, name = 'Green Joker', pos = {x = 2, y = 11}, set = 'Joker', config = {extra = {hand_add = 1, discard_sub = 1}}}, + j_superposition= {order = 59, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = 'Superposition', pos = {x = 3, y = 11}, set = 'Joker', config = {}}, + j_todo_list= {order = 60, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = 'To Do List', pos = {x = 4, y = 11}, set = 'Joker', config = {extra = {dollars = 4, poker_hand = 'High Card'}}}, + + j_cavendish= {order = 61, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = false, rarity = 1, cost = 4, name = "Cavendish", pos = {x=5,y=11}, set = "Joker", cost_mult = 1.0, config = {extra = {odds = 1000, Xmult = 3}}, yes_pool_flag = 'gros_michel_extinct'}, + j_card_sharp= {order = 62, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Card Sharp", pos = {x=6,y=11}, set = "Joker", cost_mult = 1.0, config = {extra = {Xmult = 3}}}, + j_red_card= {order = 63, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 1, cost = 5, name = "Red Card", pos = {x=7,y=11}, set = "Joker", cost_mult = 1.0, config = {extra = 3}}, + j_madness= {order = 64, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 7, name = "Madness", pos = {x=8,y=11}, set = "Joker", cost_mult = 1.0, config = {extra = 0.5}}, + j_square= {order = 65, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 1, cost = 4, name = "Square Joker", pos = {x=9,y=11}, set = "Joker", cost_mult = 1.0, config = {extra = {chips = 0, chip_mod = 4}}}, + j_seance= {order = 66, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Seance", pos = {x=0,y=12}, set = "Joker", cost_mult = 1.0, config = {extra = {poker_hand = 'Straight Flush'}}}, + j_riff_raff= {order = 67, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 6, name = "Riff-raff", pos = {x=1,y=12}, set = "Joker", cost_mult = 1.0, config = {extra = 2}}, + j_vampire= {order = 68, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 7, name = "Vampire",set = "Joker", config = {extra = 0.1, Xmult = 1}, pos = {x=2,y=12}}, + j_shortcut= {order = 69, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Shortcut",set = "Joker", config = {}, pos = {x=3,y=12}}, + j_hologram= {order = 70, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 7, name = "Hologram",set = "Joker", config = {extra = 0.25, Xmult = 1}, pos = {x=4,y=12}, soul_pos = {x=2, y=9},}, + j_vagabond= {order = 71, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "Vagabond",set = "Joker", config = {extra = 4}, pos = {x=5,y=12}}, + j_baron= {order = 72, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "Baron",set = "Joker", config = {extra = 1.5}, pos = {x=6,y=12}}, + j_cloud_9= {order = 73, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Cloud 9",set = "Joker", config = {extra = 1}, pos = {x=7,y=12}}, + j_rocket= {order = 74, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 6, name = "Rocket",set = "Joker", config = {extra = {dollars = 1, increase = 2}}, pos = {x=8,y=12}}, + j_obelisk= {order = 75, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 3, cost = 8, name = "Obelisk",set = "Joker", config = {extra = 0.2, Xmult = 1}, pos = {x=9,y=12}}, + + j_midas_mask= {order = 76, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Midas Mask",set = "Joker", config = {}, pos = {x=0,y=13}}, + j_luchador= {order = 77, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = false, rarity = 2, cost = 5, name = "Luchador",set = "Joker", config = {}, pos = {x=1,y=13}}, + j_photograph= {order = 78, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Photograph",set = "Joker", config = {extra = 2}, pos = {x=2,y=13}}, + j_gift= {order = 79, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Gift Card",set = "Joker", config = {extra = 1}, pos = {x=3,y=13}}, + j_turtle_bean= {order = 80, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = false, rarity = 2, cost = 6, name = "Turtle Bean",set = "Joker", config = {extra = {h_size = 5, h_mod = 1}}, pos = {x=4,y=13}}, + j_erosion= {order = 81, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Erosion",set = "Joker", config = {extra = 4}, pos = {x=5,y=13}}, + j_reserved_parking= {order = 82, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 6, name = "Reserved Parking",set = "Joker", config = {extra = {odds = 2, dollars = 1}}, pos = {x=6,y=13}}, + j_mail= {order = 83, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Mail-In Rebate",set = "Joker", config = {extra = 5}, pos = {x=7,y=13}}, + j_to_the_moon= {order = 84, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 5, name = "To the Moon",set = "Joker", config = {extra = 1}, pos = {x=8,y=13}}, + j_hallucination= {order = 85, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Hallucination",set = "Joker", config = {extra = 2}, pos = {x=9,y=13}}, + j_fortune_teller= {order = 86, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 6, name = "Fortune Teller", pos = {x=7,y=5}, set = "Joker", effect = "", config = {extra = 1}}, + j_juggler= {order = 87, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Juggler", pos = {x=0,y=1}, set = "Joker", effect = "Hand Size", cost_mult = 1.0, config = {h_size = 1}}, + j_drunkard= {order = 88, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Drunkard", pos = {x=1,y=1}, set = "Joker", effect = "Discard Size", cost_mult = 1.0, config = {d_size = 1}}, + j_stone= {order = 89, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Stone Joker", pos = {x=9,y=0}, set = "Joker", effect = "Stone Card Buff", cost_mult = 1.0, config = {extra = 25}, enhancement_gate = 'm_stone'}, + j_golden= {order = 90, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 6, name = "Golden Joker", pos = {x=9,y=2}, set = "Joker", effect = "Bonus dollars", cost_mult = 1.0, config = {extra = 4}}, + + j_lucky_cat= {order = 91, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 6, name = "Lucky Cat",set = "Joker", config = {Xmult = 1, extra = 0.25}, pos = {x=5,y=14}, enhancement_gate = 'm_lucky'}, + j_baseball= {order = 92, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "Baseball Card",set = "Joker", config = {extra = 1.5}, pos = {x=6,y=14}}, + j_bull= {order = 93, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Bull",set = "Joker", config = {extra = 2}, pos = {x=7,y=14}}, + j_diet_cola= {order = 94, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = false, rarity = 2, cost = 6, name = "Diet Cola",set = "Joker", config = {}, pos = {x=8,y=14}}, + j_trading= {order = 95, unlocked = true, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Trading Card",set = "Joker", config = {extra = 3}, pos = {x=9,y=14}}, + j_flash= {order = 96, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 5, name = "Flash Card",set = "Joker", config = {extra = 2, mult = 0}, pos = {x=0,y=15}}, + j_popcorn= {order = 97, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = false, rarity = 1, cost = 5, name = "Popcorn",set = "Joker", config = {mult = 20, extra = 4}, pos = {x=1,y=15}}, + j_trousers= {order = 98, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 6, name = "Spare Trousers",set = "Joker", config = {extra = 2}, pos = {x=4,y=15}}, + j_ancient= {order = 99, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "Ancient Joker",set = "Joker", config = {extra = 1.5}, pos = {x=7,y=15}}, + j_ramen= {order = 100, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = false, rarity = 2, cost = 6, name = "Ramen",set = "Joker", config = {Xmult = 2, extra = 0.01}, pos = {x=2,y=15}}, + j_walkie_talkie= {order = 101, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Walkie Talkie",set = "Joker", config = {extra = {chips = 10, mult = 4}}, pos = {x=8,y=15}}, + j_selzer= {order = 102, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = false, rarity = 2, cost = 6, name = "Seltzer",set = "Joker", config = {extra = 10}, pos = {x=3,y=15}}, + j_castle= {order = 103, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 6, name = "Castle",set = "Joker", config = {extra = {chips = 0, chip_mod = 3}}, pos = {x=9,y=15}}, + j_smiley= {order = 104, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Smiley Face",set = "Joker", config = {extra = 5}, pos = {x=6,y=15}}, + j_campfire= {order = 105, unlocked = true, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 9, name = "Campfire",set = "Joker", config = {extra = 0.25}, pos = {x=5,y=15}}, + + j_ticket= {order = 106, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Golden Ticket", pos = {x=5,y=3}, set = "Joker", effect = "dollars for Gold cards", cost_mult = 1.0, config = {extra = 4},unlock_condition = {type = 'hand_contents', extra = 'Gold'}, enhancement_gate = 'm_gold'}, + j_mr_bones= {order = 107, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = false, rarity = 2, cost = 5, name = "Mr. Bones", pos = {x=3,y=4}, set = "Joker", effect = "Prevent Death", cost_mult = 1.0, config = {},unlock_condition = {type = 'c_losses', extra = 5}}, + j_acrobat= {order = 108, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Acrobat", pos = {x=2,y=1}, set = "Joker", effect = "Shop size", cost_mult = 1.0, config = {extra = 3},unlock_condition = {type = 'c_hands_played', extra = 200}}, + j_sock_and_buskin= {order = 109, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Sock and Buskin", pos = {x=3,y=1}, set = "Joker", effect = "Face card double", cost_mult = 1.0, config = {extra = 1},unlock_condition = {type = 'c_face_cards_played', extra = 300}}, + j_swashbuckler= {order = 110, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Swashbuckler", pos = {x=9,y=5}, set = "Joker", effect = "Set Mult", cost_mult = 1.0, config = {mult = 1},unlock_condition = {type = 'c_jokers_sold', extra = 20}}, + j_troubadour= {order = 111, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Troubadour", pos = {x=0,y=2}, set = "Joker", effect = "Hand Size, Plays", cost_mult = 1.0, config = {extra = {h_size = 2, h_plays = -1}}, unlock_condition = {type = 'round_win', extra = 5}}, + j_certificate= {order = 112, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Certificate", pos = {x=8,y=8}, set = "Joker", effect = "", config = {}, unlock_condition = {type = 'double_gold'}}, + j_smeared= {order = 113, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Smeared Joker", pos = {x=4,y=6}, set = "Joker", effect = "", config = {}, unlock_condition = {type = 'modify_deck', extra = {count = 3, enhancement = 'Wild Card', e_key = 'm_wild'}}}, + j_throwback= {order = 114, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Throwback", pos = {x=5,y=7}, set = "Joker", effect = "", config = {extra = 0.25}, unlock_condition = {type = 'continue_game'}}, + j_hanging_chad= {order = 115, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 4, name = "Hanging Chad", pos = {x=9,y=6}, set = "Joker", effect = "", config = {extra = 2}, unlock_condition = {type = 'round_win', extra = 'High Card'}}, + j_rough_gem= {order = 116, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Rough Gem", pos = {x=9,y=7}, set = "Joker", effect = "", config = {extra = 1}, unlock_condition = {type = 'modify_deck', extra = {count = 30, suit = 'Diamonds'}}}, + j_bloodstone= {order = 117, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Bloodstone", pos = {x=0,y=8}, set = "Joker", effect = "", config = {extra = {odds = 2, Xmult = 1.5}}, unlock_condition = {type = 'modify_deck', extra = {count = 30, suit = 'Hearts'}}}, + j_arrowhead= {order = 118, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Arrowhead", pos = {x=1,y=8}, set = "Joker", effect = "", config = {extra = 50}, unlock_condition = {type = 'modify_deck', extra = {count = 30, suit = 'Spades'}}}, + j_onyx_agate= {order = 119, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Onyx Agate", pos = {x=2,y=8}, set = "Joker", effect = "", config = {extra = 7}, unlock_condition = {type = 'modify_deck', extra = {count = 30, suit = 'Clubs'}}}, + j_glass= {order = 120, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 2, cost = 6, name = "Glass Joker", pos = {x=1,y=3}, set = "Joker", effect = "Glass Card", cost_mult = 1.0, config = {extra = 0.75, Xmult = 1}, unlock_condition = {type = 'modify_deck', extra = {count = 5, enhancement = 'Glass Card', e_key = 'm_glass'}}, enhancement_gate = 'm_glass'}, + + j_ring_master= {order = 121, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 5, name = "Showman", pos = {x=6,y=5}, set = "Joker", effect = "", config = {}, unlock_condition = {type = 'ante_up', ante = 4}}, + j_flower_pot= {order = 122, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Flower Pot", pos = {x=0,y=6}, set = "Joker", effect = "", config = {extra = 3}, unlock_condition = {type = 'ante_up', ante = 8}}, + j_blueprint= {order = 123, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 10,name = "Blueprint", pos = {x=0,y=3}, set = "Joker", effect = "Copycat", cost_mult = 1.0, config = {},unlock_condition = {type = 'win_custom'}}, + j_wee= {order = 124, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = false, eternal_compat = true, rarity = 3, cost = 8, name = "Wee Joker", pos = {x=0,y=0}, set = "Joker", effect = "", config = {extra = {chips = 0, chip_mod = 8}}, unlock_condition = {type = 'win', n_rounds = 18}}, + j_merry_andy= {order = 125, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Merry Andy", pos = {x=8,y=0}, set = "Joker", effect = "", cost_mult = 1.0, config = {d_size = 3, h_size = -1}, unlock_condition = {type = 'win', n_rounds = 12}}, + j_oops= {order = 126, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 4, name = "Oops! All 6s", pos = {x=5,y=6}, set = "Joker", effect = "", config = {}, unlock_condition = {type = 'chip_score', chips = 10000}}, + j_idol= {order = 127, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "The Idol", pos = {x=6,y=7}, set = "Joker", effect = "", config = {extra = 2}, unlock_condition = {type = 'chip_score', chips = 1000000}}, + j_seeing_double= {order = 128, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Seeing Double", pos = {x=4,y=4}, set = "Joker", effect = "X1.5 Mult club 7", cost_mult = 1.0, config = {extra = 2},unlock_condition = {type = 'hand_contents', extra = 'four 7 of Clubs'}}, + j_matador= {order = 129, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Matador", pos = {x=4,y=5}, set = "Joker", effect = "", config = {extra = 8}, unlock_condition = {type = 'round_win'}}, + j_hit_the_road= {order = 130, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "Hit the Road", pos = {x=8,y=5}, set = "Joker", effect = "Jack Discard Effect", cost_mult = 1.0, config = {extra = 0.5}, unlock_condition = {type = 'discard_custom'}}, + j_duo= {order = 131, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "The Duo", pos = {x=5,y=4}, set = "Joker", effect = "X1.5 Mult", cost_mult = 1.0, config = {Xmult = 2, type = 'Pair'}, unlock_condition = {type = 'win_no_hand', extra = 'Pair'}}, + j_trio= {order = 132, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "The Trio", pos = {x=6,y=4}, set = "Joker", effect = "X2 Mult", cost_mult = 1.0, config = {Xmult = 3, type = 'Three of a Kind'}, unlock_condition = {type = 'win_no_hand', extra = 'Three of a Kind'}}, + j_family= {order = 133, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "The Family", pos = {x=7,y=4}, set = "Joker", effect = "X3 Mult", cost_mult = 1.0, config = {Xmult = 4, type = 'Four of a Kind'}, unlock_condition = {type = 'win_no_hand', extra = 'Four of a Kind'}}, + j_order= {order = 134, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "The Order", pos = {x=8,y=4}, set = "Joker", effect = "X3 Mult", cost_mult = 1.0, config = {Xmult = 3, type = 'Straight'}, unlock_condition = {type = 'win_no_hand', extra = 'Straight'}}, + j_tribe= {order = 135, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "The Tribe", pos = {x=9,y=4}, set = "Joker", effect = "X3 Mult", cost_mult = 1.0, config = {Xmult = 2, type = 'Flush'}, unlock_condition = {type = 'win_no_hand', extra = 'Flush'}}, + + j_stuntman= {order = 136, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 7, name = "Stuntman", pos = {x=8,y=6}, set = "Joker", effect = "", config = {extra = {h_size = 2, chip_mod = 250}}, unlock_condition = {type = 'chip_score', chips = 100000000}}, + j_invisible= {order = 137, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = false, rarity = 3, cost = 8, name = "Invisible Joker", pos = {x=1,y=7}, set = "Joker", effect = "", config = {extra = 2}, unlock_condition = {type = 'win_custom'}}, + j_brainstorm= {order = 138, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 10, name = "Brainstorm", pos = {x=7,y=7}, set = "Joker", effect = "Copycat", config = {}, unlock_condition = {type = 'discard_custom'}}, + j_satellite= {order = 139, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Satellite", pos = {x=8,y=7}, set = "Joker", effect = "", config = {extra = 1}, unlock_condition = {type = 'money', extra = 400}}, + j_shoot_the_moon= {order = 140, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 1, cost = 5, name = "Shoot the Moon", pos = {x=2,y=6}, set = "Joker", effect = "", config = {extra = 13}, unlock_condition = {type = 'play_all_hearts'}}, + j_drivers_license= {order = 141, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 7, name = "Driver's License", pos = {x=0,y=7}, set = "Joker", effect = "", config = {extra = 3}, unlock_condition = {type = 'modify_deck', extra = {count = 16, tally = 'total'}}}, + j_cartomancer= {order = 142, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 6, name = "Cartomancer", pos = {x=7,y=3}, set = "Joker", effect = "Tarot Buff", cost_mult = 1.0, config = {}, unlock_condition = {type = 'discover_amount', tarot_count = 22}}, + j_astronomer= {order = 143, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 8, name = "Astronomer", pos = {x=2,y=7}, set = "Joker", effect = "", config = {}, unlock_condition = {type = 'discover_amount', planet_count = 12}}, + j_burnt= {order = 144, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 3, cost = 8, name = "Burnt Joker", pos = {x=3,y=7}, set = "Joker", effect = "", config = {h_size = 0, extra = 4}, unlock_condition = {type = 'c_cards_sold', extra = 50}}, + j_bootstraps= {order = 145, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 2, cost = 7, name = "Bootstraps", pos = {x=9,y=8}, set = "Joker", effect = "", config = {extra = {mult = 2, dollars = 5}}, unlock_condition = {type = 'modify_jokers', extra = {polychrome = true, count = 2}}}, + j_caino= {order = 146, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 4, cost = 20, name = "Caino", pos = {x=3,y=8}, soul_pos = {x=3, y=9}, set = "Joker", effect = "", config = {extra = 1}, unlock_condition = {type = '', extra = '', hidden = true}}, + j_triboulet= {order = 147, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 4, cost = 20, name = "Triboulet", pos = {x=4,y=8}, soul_pos = {x=4, y=9}, set = "Joker", effect = "", config = {extra = 2}, unlock_condition = {type = '', extra = '', hidden = true}}, + j_yorick= {order = 148, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 4, cost = 20, name = "Yorick", pos = {x=5,y=8}, soul_pos = {x=5, y=9}, set = "Joker", effect = "", config = {extra = {xmult = 1, discards = 23}}, unlock_condition = {type = '', extra = '', hidden = true}}, + j_chicot= {order = 149, unlocked = false, discovered = false, blueprint_compat = false, perishable_compat = true, eternal_compat = true, rarity = 4, cost = 20, name = "Chicot", pos = {x=6,y=8}, soul_pos = {x=6, y=9}, set = "Joker", effect = "", config = {}, unlock_condition = {type = '', extra = '', hidden = true}}, + j_perkeo= {order = 150, unlocked = false, discovered = false, blueprint_compat = true, perishable_compat = true, eternal_compat = true, rarity = 4, cost = 20, name = "Perkeo", pos = {x=7,y=8}, soul_pos = {x=7, y=9}, set = "Joker", effect = "", config = {}, unlock_condition = {type = '', extra = '', hidden = true}}, + + + + --All Consumeables + + --Tarots + c_fool= {order = 1, discovered = false, cost = 3, consumeable = true, name = "The Fool", pos = {x=0,y=0}, set = "Tarot", effect = "Disable Blind Effect", cost_mult = 1.0, config = {}}, + c_magician= {order = 2, discovered = false, cost = 3, consumeable = true, name = "The Magician", pos = {x=1,y=0}, set = "Tarot", effect = "Enhance", cost_mult = 1.0, config = {mod_conv = 'm_lucky', max_highlighted = 2}}, + c_high_priestess= {order = 3, discovered = false, cost = 3, consumeable = true, name = "The High Priestess", pos = {x=2,y=0}, set = "Tarot", effect = "Round Bonus", cost_mult = 1.0, config = {planets = 2}}, + c_empress= {order = 4, discovered = false, cost = 3, consumeable = true, name = "The Empress", pos = {x=3,y=0}, set = "Tarot", effect = "Enhance", cost_mult = 1.0, config = {mod_conv = 'm_mult', max_highlighted = 2}}, + c_emperor= {order = 5, discovered = false, cost = 3, consumeable = true, name = "The Emperor", pos = {x=4,y=0}, set = "Tarot", effect = "Round Bonus", cost_mult = 1.0, config = {tarots = 2}}, + c_heirophant= {order = 6, discovered = false, cost = 3, consumeable = true, name = "The Hierophant", pos = {x=5,y=0}, set = "Tarot", effect = "Enhance", cost_mult = 1.0, config = {mod_conv = 'm_bonus', max_highlighted = 2}}, + c_lovers= {order = 7, discovered = false, cost = 3, consumeable = true, name = "The Lovers", pos = {x=6,y=0}, set = "Tarot", effect = "Enhance", cost_mult = 1.0, config = {mod_conv = 'm_wild', max_highlighted = 1}}, + c_chariot= {order = 8, discovered = false, cost = 3, consumeable = true, name = "The Chariot", pos = {x=7,y=0}, set = "Tarot", effect = "Enhance", cost_mult = 1.0, config = {mod_conv = 'm_steel', max_highlighted = 1}}, + c_justice= {order = 9, discovered = false, cost = 3, consumeable = true, name = "Justice", pos = {x=8,y=0}, set = "Tarot", effect = "Enhance", cost_mult = 1.0, config = {mod_conv = 'm_glass', max_highlighted = 1}}, + c_hermit= {order = 10, discovered = false, cost = 3, consumeable = true, name = "The Hermit", pos = {x=9,y=0}, set = "Tarot", effect = "Dollar Doubler", cost_mult = 1.0, config = {extra = 20}}, + c_wheel_of_fortune= {order = 11, discovered = false, cost = 3, consumeable = true, name = "The Wheel of Fortune", pos = {x=0,y=1}, set = "Tarot", effect = "Round Bonus", cost_mult = 1.0, config = {extra = 4}}, + c_strength= {order = 12, discovered = false, cost = 3, consumeable = true, name = "Strength", pos = {x=1,y=1}, set = "Tarot", effect = "Round Bonus", cost_mult = 1.0, config = {mod_conv = 'up_rank', max_highlighted = 2}}, + c_hanged_man= {order = 13, discovered = false, cost = 3, consumeable = true, name = "The Hanged Man", pos = {x=2,y=1}, set = "Tarot", effect = "Card Removal", cost_mult = 1.0, config = {remove_card = true, max_highlighted = 2}}, + c_death= {order = 14, discovered = false, cost = 3, consumeable = true, name = "Death", pos = {x=3,y=1}, set = "Tarot", effect = "Card Conversion", cost_mult = 1.0, config = {mod_conv = 'card', max_highlighted = 2, min_highlighted = 2}}, + c_temperance= {order = 15, discovered = false, cost = 3, consumeable = true, name = "Temperance", pos = {x=4,y=1}, set = "Tarot", effect = "Joker Payout", cost_mult = 1.0, config = {extra = 50}}, + c_devil= {order = 16, discovered = false, cost = 3, consumeable = true, name = "The Devil", pos = {x=5,y=1}, set = "Tarot", effect = "Enhance", cost_mult = 1.0, config = {mod_conv = 'm_gold', max_highlighted = 1}}, + c_tower= {order = 17, discovered = false, cost = 3, consumeable = true, name = "The Tower", pos = {x=6,y=1}, set = "Tarot", effect = "Enhance", cost_mult = 1.0, config = {mod_conv = 'm_stone', max_highlighted = 1}}, + c_star= {order = 18, discovered = false, cost = 3, consumeable = true, name = "The Star", pos = {x=7,y=1}, set = "Tarot", effect = "Suit Conversion", cost_mult = 1.0, config = {suit_conv = 'Diamonds', max_highlighted = 3}}, + c_moon= {order = 19, discovered = false, cost = 3, consumeable = true, name = "The Moon", pos = {x=8,y=1}, set = "Tarot", effect = "Suit Conversion", cost_mult = 1.0, config = {suit_conv = 'Clubs', max_highlighted = 3}}, + c_sun= {order = 20, discovered = false, cost = 3, consumeable = true, name = "The Sun", pos = {x=9,y=1}, set = "Tarot", effect = "Suit Conversion", cost_mult = 1.0, config = {suit_conv = 'Hearts', max_highlighted = 3}}, + c_judgement= {order = 21, discovered = false, cost = 3, consumeable = true, name = "Judgement", pos = {x=0,y=2}, set = "Tarot", effect = "Random Joker", cost_mult = 1.0, config = {}}, + c_world= {order = 22, discovered = false, cost = 3, consumeable = true, name = "The World", pos = {x=1,y=2}, set = "Tarot", effect = "Suit Conversion", cost_mult = 1.0, config = {suit_conv = 'Spades', max_highlighted = 3}}, + + --Planets + c_mercury= {order = 1, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Mercury", pos = {x=0,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Pair'}}, + c_venus= {order = 2, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Venus", pos = {x=1,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Three of a Kind'}}, + c_earth= {order = 3, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Earth", pos = {x=2,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Full House'}}, + c_mars= {order = 4, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Mars", pos = {x=3,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Four of a Kind'}}, + c_jupiter= {order = 5, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Jupiter", pos = {x=4,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Flush'}}, + c_saturn= {order = 6, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Saturn", pos = {x=5,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Straight'}}, + c_uranus= {order = 7, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Uranus", pos = {x=6,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Two Pair'}}, + c_neptune= {order = 8, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Neptune", pos = {x=7,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Straight Flush'}}, + c_pluto= {order = 9, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Pluto", pos = {x=8,y=3}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'High Card'}}, + c_planet_x= {order = 10, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Planet X", pos = {x=9,y=2}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Five of a Kind', softlock = true}}, + c_ceres= {order = 11, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Ceres", pos = {x=8,y=2}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Flush House', softlock = true}}, + c_eris= {order = 12, discovered = false, cost = 3, consumeable = true, freq = 1, name = "Eris", pos = {x=3,y=2}, set = "Planet", effect = "Hand Upgrade", cost_mult = 1.0, config = {hand_type = 'Flush Five', softlock = true}}, + + --Spectral + c_familiar= {order = 1, discovered = false, cost = 4, consumeable = true, name = "Familiar", pos = {x=0,y=4}, set = "Spectral", config = {remove_card = true, extra = 3}}, + c_grim= {order = 2, discovered = false, cost = 4, consumeable = true, name = "Grim", pos = {x=1,y=4}, set = "Spectral", config = {remove_card = true, extra = 2}}, + c_incantation= {order = 3, discovered = false, cost = 4, consumeable = true, name = "Incantation", pos = {x=2,y=4}, set = "Spectral", config = {remove_card = true, extra = 4}}, + c_talisman= {order = 4, discovered = false, cost = 4, consumeable = true, name = "Talisman", pos = {x=3,y=4}, set = "Spectral", config = {extra = 'Gold', max_highlighted = 1}}, + c_aura= {order = 5, discovered = false, cost = 4, consumeable = true, name = "Aura", pos = {x=4,y=4}, set = "Spectral", config = {}}, + c_wraith= {order = 6, discovered = false, cost = 4, consumeable = true, name = "Wraith", pos = {x=5,y=4}, set = "Spectral", config = {}}, + c_sigil= {order = 7, discovered = false, cost = 4, consumeable = true, name = "Sigil", pos = {x=6,y=4}, set = "Spectral", config = {}}, + c_ouija= {order = 8, discovered = false, cost = 4, consumeable = true, name = "Ouija", pos = {x=7,y=4}, set = "Spectral", config = {}}, + c_ectoplasm= {order = 9, discovered = false, cost = 4, consumeable = true, name = "Ectoplasm", pos = {x=8,y=4}, set = "Spectral", config = {}}, + c_immolate= {order = 10, discovered = false, cost = 4, consumeable = true, name = "Immolate", pos = {x=9,y=4}, set = "Spectral", config = {remove_card = true, extra = {destroy = 5, dollars = 20}}}, + c_ankh= {order = 11, discovered = false, cost = 4, consumeable = true, name = "Ankh", pos = {x=0,y=5}, set = "Spectral", config = {extra = 2}}, + c_deja_vu= {order = 12, discovered = false, cost = 4, consumeable = true, name = "Deja Vu", pos = {x=1,y=5}, set = "Spectral", config = {extra = 'Red', max_highlighted = 1}}, + c_hex= {order = 13, discovered = false, cost = 4, consumeable = true, name = "Hex", pos = {x=2,y=5}, set = "Spectral", config = {extra = 2}}, + c_trance= {order = 14, discovered = false, cost = 4, consumeable = true, name = "Trance", pos = {x=3,y=5}, set = "Spectral", config = {extra = 'Blue', max_highlighted = 1}}, + c_medium= {order = 15, discovered = false, cost = 4, consumeable = true, name = "Medium", pos = {x=4,y=5}, set = "Spectral", config = {extra = 'Purple', max_highlighted = 1}}, + c_cryptid= {order = 16, discovered = false, cost = 4, consumeable = true, name = "Cryptid", pos = {x=5,y=5}, set = "Spectral", config = {extra = 2, max_highlighted = 1}}, + c_soul= {order = 17, discovered = false, cost = 4, consumeable = true, name = "The Soul", pos = {x=2,y=2}, set = "Spectral", effect = "Unlocker", config = {}, hidden = true}, + c_black_hole= {order = 18, discovered = false, cost = 4, consumeable = true, name = "Black Hole", pos = {x=9,y=3}, set = "Spectral", config = {}, hidden = true}, + + --Vouchers + + v_overstock_norm = {order = 1, discovered = false, unlocked = true , available = true, cost = 10, name = "Overstock", pos = {x=0,y=0}, set = "Voucher", config = {extra = 1}}, + v_clearance_sale= {order = 3, discovered = false, unlocked = true , available = true, cost = 10, name = "Clearance Sale", pos = {x=3,y=0}, set = "Voucher", config = {extra = 25}}, + v_hone= {order = 5, discovered = false, unlocked = true , available = true, cost = 10, name = "Hone", pos = {x=4,y=0}, set = "Voucher", config = {extra = 2}}, + v_reroll_surplus= {order = 7, discovered = false, unlocked = true , available = true, cost = 10, name = "Reroll Surplus", pos = {x=0,y=2}, set = "Voucher", config = {extra = 2}}, + v_crystal_ball= {order = 9, discovered = false, unlocked = true , available = true, cost = 10, name = "Crystal Ball", pos = {x=2,y=2}, set = "Voucher", config = {extra = 1}}, + v_telescope= {order = 11, discovered = false, unlocked = true , available = true, cost = 10, name = "Telescope", pos = {x=3,y=2}, set = "Voucher", config = {extra = 3}}, + v_grabber= {order = 13, discovered = false, unlocked = true , available = true, cost = 10, name = "Grabber", pos = {x=5,y=0}, set = "Voucher", config = {extra = 1}}, + v_wasteful= {order = 15, discovered = false, unlocked = true , available = true, cost = 10, name = "Wasteful", pos = {x=6,y=0}, set = "Voucher", config = {extra = 1}}, + v_tarot_merchant= {order = 17, discovered = false, unlocked = true , available = true, cost = 10, name = "Tarot Merchant", pos = {x=1,y=0}, set = "Voucher", config = {extra = 9.6/4, extra_disp = 2}}, + v_planet_merchant= {order = 19, discovered = false, unlocked = true , available = true, cost = 10, name = "Planet Merchant", pos = {x=2,y=0}, set = "Voucher", config = {extra = 9.6/4, extra_disp = 2}}, + v_seed_money= {order = 21, discovered = false, unlocked = true , available = true, cost = 10, name = "Seed Money", pos = {x=1,y=2}, set = "Voucher", config = {extra = 50}}, + v_blank= {order = 23, discovered = false, unlocked = true , available = true, cost = 10, name = "Blank", pos = {x=7,y=0}, set = "Voucher", config = {extra = 5}}, + v_magic_trick= {order = 25, discovered = false, unlocked = true , available = true, cost = 10, name = "Magic Trick", pos = {x=4,y=2}, set = "Voucher", config = {extra = 4}}, + v_hieroglyph= {order = 27, discovered = false, unlocked = true , available = true, cost = 10, name = "Hieroglyph", pos = {x=5,y=2}, set = "Voucher", config = {extra = 1}}, + v_directors_cut= {order = 29, discovered = false, unlocked = true , available = true, cost = 10, name = "Director's Cut", pos = {x=6,y=2}, set = "Voucher", config = {extra = 10}}, + v_paint_brush= {order = 31, discovered = false, unlocked = true , available = true, cost = 10, name = "Paint Brush", pos = {x=7,y=2}, set = "Voucher", config = {extra = 1}}, + + v_overstock_plus= {order = 2, discovered = false, unlocked = false, available = true, cost = 10, name = "Overstock Plus", pos = {x=0,y=1}, set = "Voucher", config = {extra = 1}, requires = {'v_overstock_norm'},unlock_condition = {type = 'c_shop_dollars_spent', extra = 2500}}, + v_liquidation= {order = 4, discovered = false, unlocked = false, available = true, cost = 10, name = "Liquidation", pos = {x=3,y=1}, set = "Voucher", config = {extra = 50}, requires = {'v_clearance_sale'},unlock_condition = {type = 'run_redeem', extra = 10}}, + v_glow_up= {order = 6, discovered = false, unlocked = false, available = true, cost = 10, name = "Glow Up", pos = {x=4,y=1}, set = "Voucher", config = {extra = 4}, requires = {'v_hone'},unlock_condition = {type = 'have_edition', extra = 5}}, + v_reroll_glut= {order = 8, discovered = false, unlocked = false, available = true, cost = 10, name = "Reroll Glut", pos = {x=0,y=3}, set = "Voucher", config = {extra = 2}, requires = {'v_reroll_surplus'},unlock_condition = {type = 'c_shop_rerolls', extra = 100}}, + v_omen_globe= {order = 10, discovered = false, unlocked = false, available = true, cost = 10, name = "Omen Globe", pos = {x=2,y=3}, set = "Voucher", config = {extra = 4}, requires = {'v_crystal_ball'},unlock_condition = {type = 'c_tarot_reading_used', extra = 25}}, + v_observatory= {order = 12, discovered = false, unlocked = false, available = true, cost = 10, name = "Observatory", pos = {x=3,y=3}, set = "Voucher", config = {extra = 1.5}, requires = {'v_telescope'},unlock_condition = {type = 'c_planetarium_used', extra = 25}}, + v_nacho_tong= {order = 14, discovered = false, unlocked = false, available = true, cost = 10, name = "Nacho Tong", pos = {x=5,y=1}, set = "Voucher", config = {extra = 1}, requires = {'v_grabber'},unlock_condition = {type = 'c_cards_played', extra = 2500}}, + v_recyclomancy= {order = 16, discovered = false, unlocked = false, available = true, cost = 10, name = "Recyclomancy", pos = {x=6,y=1}, set = "Voucher", config = {extra = 1}, requires = {'v_wasteful'},unlock_condition = {type = 'c_cards_discarded', extra = 2500}}, + v_tarot_tycoon= {order = 18, discovered = false, unlocked = false, available = true,cost = 10, name = "Tarot Tycoon", pos = {x=1,y=1}, set = "Voucher", config = {extra = 32/4, extra_disp = 4}, requires = {'v_tarot_merchant'},unlock_condition = {type = 'c_tarots_bought', extra = 50}}, + v_planet_tycoon= {order = 20, discovered = false, unlocked = false, available = true,cost = 10, name = "Planet Tycoon", pos = {x=2,y=1}, set = "Voucher", config = {extra = 32/4, extra_disp = 4}, requires = {'v_planet_merchant'},unlock_condition = {type = 'c_planets_bought', extra = 50}}, + v_money_tree= {order = 22, discovered = false, unlocked = false, available = true, cost = 10, name = "Money Tree", pos = {x=1,y=3}, set = "Voucher", config = {extra = 100}, requires = {'v_seed_money'},unlock_condition = {type = 'interest_streak', extra = 10}}, + v_antimatter= {order = 24, discovered = false, unlocked = false, available = true, cost = 10, name = "Antimatter", pos = {x=7,y=1}, set = "Voucher", config = {extra = 1}, requires = {'v_blank'},unlock_condition = {type = 'blank_redeems', extra = 10}}, + v_illusion= {order = 26, discovered = false, unlocked = false, available = true, cost = 10, name = "Illusion", pos = {x=4,y=3}, set = "Voucher", config = {extra = 4}, requires = {'v_magic_trick'},unlock_condition = {type = 'c_playing_cards_bought', extra = 20}}, + v_petroglyph= {order = 28, discovered = false, unlocked = false, available = true, cost = 10, name = "Petroglyph", pos = {x=5,y=3}, set = "Voucher", config = {extra = 1}, requires = {'v_hieroglyph'},unlock_condition = {type = 'ante_up', ante = 12, extra = 12}}, + v_retcon= {order = 30, discovered = false, unlocked = false, available = true, cost = 10, name = "Retcon", pos = {x=6,y=3}, set = "Voucher", config = {extra = 10}, requires = {'v_directors_cut'},unlock_condition = {type = 'blind_discoveries', extra = 25}}, + v_palette= {order = 32, discovered = false, unlocked = false, available = true, cost = 10, name = "Palette", pos = {x=7,y=3}, set = "Voucher", config = {extra = 1}, requires = {'v_paint_brush'},unlock_condition = {type = 'min_hand_size', extra = 5}}, + + --Backs + + b_red= {name = "Red Deck", stake = 1, unlocked = true,order = 1, pos = {x=0,y=0}, set = "Back", config = {discards = 1}, discovered = true}, + b_blue= {name = "Blue Deck", stake = 1, unlocked = false,order = 2, pos = {x=0,y=2}, set = "Back", config = {hands = 1}, unlock_condition = {type = 'discover_amount', amount = 20}}, + b_yellow= {name = "Yellow Deck", stake = 1, unlocked = false,order = 3, pos = {x=1,y=2}, set = "Back", config = {dollars = 10}, unlock_condition = {type = 'discover_amount', amount = 50}}, + b_green= {name = "Green Deck", stake = 1, unlocked = false,order = 4, pos = {x=2,y=2}, set = "Back", config = {extra_hand_bonus = 2, extra_discard_bonus = 1, no_interest = true}, unlock_condition = {type = 'discover_amount', amount = 75}}, + b_black= {name = "Black Deck", stake = 1, unlocked = false,order = 5, pos = {x=3,y=2}, set = "Back", config = {hands = -1, joker_slot = 1}, unlock_condition = {type = 'discover_amount', amount = 100}}, + b_magic= {name = "Magic Deck", stake = 1, unlocked = false,order = 6, pos = {x=0,y=3}, set = "Back", config = {voucher = 'v_crystal_ball', consumables = {'c_fool', 'c_fool'}}, unlock_condition = {type = 'win_deck', deck = 'b_red'}}, + b_nebula= {name = "Nebula Deck", stake = 1, unlocked = false,order = 7, pos = {x=3,y=0}, set = "Back", config = {voucher = 'v_telescope', consumable_slot = -1}, unlock_condition = {type = 'win_deck', deck = 'b_blue'}}, + b_ghost= {name = "Ghost Deck", stake = 1, unlocked = false,order = 8, pos = {x=6,y=2}, set = "Back", config = {spectral_rate = 2, consumables = {'c_hex'}}, unlock_condition = {type = 'win_deck', deck = 'b_yellow'}}, + b_abandoned= {name = "Abandoned Deck", stake = 1, unlocked = false,order = 9, pos = {x=3,y=3}, set = "Back", config = {remove_faces = true}, unlock_condition = {type = 'win_deck', deck = 'b_green'}}, + b_checkered= {name = "Checkered Deck", stake = 1, unlocked = false,order = 10,pos = {x=1,y=3}, set = "Back", config = {}, unlock_condition = {type = 'win_deck', deck = 'b_black'}}, + b_zodiac= {name = "Zodiac Deck", stake = 1, unlocked = false,order = 11, pos = {x=3,y=4}, set = "Back", config = {vouchers = {'v_tarot_merchant','v_planet_merchant', 'v_overstock_norm'}}, unlock_condition = {type = 'win_stake', stake = 2}}, + b_painted= {name = "Painted Deck", stake = 1, unlocked = false,order = 12, pos = {x=4,y=3}, set = "Back", config = {hand_size = 2, joker_slot = -1}, unlock_condition = {type = 'win_stake', stake=3}}, + b_anaglyph= {name = "Anaglyph Deck", stake = 1, unlocked = false,order = 13, pos = {x=2,y=4}, set = "Back", config = {}, unlock_condition = {type = 'win_stake', stake = 4}}, + b_plasma= {name = "Plasma Deck", stake = 1, unlocked = false,order = 14, pos = {x=4,y=2}, set = "Back", config = {ante_scaling = 2}, unlock_condition = {type = 'win_stake', stake=5}}, + b_erratic= {name = "Erratic Deck", stake = 1, unlocked = false,order = 15, pos = {x=2,y=3}, set = "Back", config = {randomize_rank_suit = true}, unlock_condition = {type = 'win_stake', stake=7}}, + + b_challenge= {name = "Challenge Deck", stake = 1, unlocked = true,order = 16, pos = {x=0,y=4}, set = "Back", config = {}, omit = true}, + + + --All enhanced card types here + m_bonus = {max = 500, order = 2, name = "Bonus", set = "Enhanced", pos = {x=1,y=1}, effect = "Bonus Card", label = "Bonus Card", config = {bonus=30}}, + m_mult = {max = 500, order = 3, name = "Mult", set = "Enhanced", pos = {x=2,y=1}, effect = "Mult Card", label = "Mult Card", config = {mult = 4}}, + m_wild = {max = 500, order = 4, name = "Wild Card", set = "Enhanced", pos = {x=3,y=1}, effect = "Wild Card", label = "Wild Card", config = {}}, + m_glass = {max = 500, order = 5, name = "Glass Card", set = "Enhanced", pos = {x=5,y=1}, effect = "Glass Card", label = "Glass Card", config = {Xmult = 2, extra = 4}}, + m_steel = {max = 500, order = 6, name = "Steel Card", set = "Enhanced", pos = {x=6,y=1}, effect = "Steel Card", label = "Steel Card", config = {h_x_mult = 1.5}}, + m_stone = {max = 500, order = 7, name = "Stone Card", set = "Enhanced", pos = {x=5,y=0}, effect = "Stone Card", label = "Stone Card", config = {bonus = 50}}, + m_gold = {max = 500, order = 8, name = "Gold Card", set = "Enhanced", pos = {x=6,y=0}, effect = "Gold Card", label = "Gold Card", config = {h_dollars = 3}}, + m_lucky = {max = 500, order = 9, name = "Lucky Card", set = "Enhanced", pos = {x=4,y=1}, effect = "Lucky Card", label = "Lucky Card", config = {mult=20, p_dollars = 20}}, + + --editions + e_base = {order = 1, unlocked = true, discovered = false, name = "Base", pos = {x=0,y=0}, atlas = 'Joker', set = "Edition", config = {}}, + e_foil = {order = 2, unlocked = true, discovered = false, name = "Foil", pos = {x=0,y=0}, atlas = 'Joker', set = "Edition", config = {extra = 50}}, + e_holo = {order = 3, unlocked = true, discovered = false, name = "Holographic", pos = {x=0,y=0}, atlas = 'Joker', set = "Edition", config = {extra = 10}}, + e_polychrome = {order = 4, unlocked = true, discovered = false, name = "Polychrome", pos = {x=0,y=0}, atlas = 'Joker', set = "Edition", config = {extra = 1.5}}, + e_negative = {order = 5, unlocked = true, discovered = false, name = "Negative", pos = {x=0,y=0}, atlas = 'Joker', set = "Edition", config = {extra = 1}}, + + --booster packs + p_arcana_normal_1 = {order = 1, discovered = false, name = "Arcana Pack", weight = 1, kind = 'Arcana', cost = 4, pos = {x=0,y=0}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_arcana_normal_2 = {order = 2, discovered = false, name = "Arcana Pack", weight = 1, kind = 'Arcana', cost = 4, pos = {x=1,y=0}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_arcana_normal_3 = {order = 3, discovered = false, name = "Arcana Pack", weight = 1, kind = 'Arcana', cost = 4, pos = {x=2,y=0}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_arcana_normal_4 = {order = 4, discovered = false, name = "Arcana Pack", weight = 1, kind = 'Arcana', cost = 4, pos = {x=3,y=0}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_arcana_jumbo_1 = {order = 5, discovered = false, name = "Jumbo Arcana Pack", weight = 1, kind = 'Arcana', cost = 6, pos = {x=0,y=2}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 1}}, + p_arcana_jumbo_2 = {order = 6, discovered = false, name = "Jumbo Arcana Pack", weight = 1, kind = 'Arcana', cost = 6, pos = {x=1,y=2}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 1}}, + p_arcana_mega_1 = {order = 7, discovered = false, name = "Mega Arcana Pack", weight = 0.25, kind = 'Arcana', cost = 8, pos = {x=2,y=2}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 2}}, + p_arcana_mega_2 = {order = 8, discovered = false, name = "Mega Arcana Pack", weight = 0.25, kind = 'Arcana', cost = 8, pos = {x=3,y=2}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 2}}, + p_celestial_normal_1 = {order = 9, discovered = false, name = "Celestial Pack", weight = 1, kind = 'Celestial', cost = 4, pos = {x=0,y=1}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_celestial_normal_2 = {order = 10, discovered = false, name = "Celestial Pack", weight = 1, kind = 'Celestial', cost = 4, pos = {x=1,y=1}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_celestial_normal_3 = {order = 11, discovered = false, name = "Celestial Pack", weight = 1, kind = 'Celestial', cost = 4, pos = {x=2,y=1}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_celestial_normal_4 = {order = 12, discovered = false, name = "Celestial Pack", weight = 1, kind = 'Celestial', cost = 4, pos = {x=3,y=1}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_celestial_jumbo_1 = {order = 13, discovered = false, name = "Jumbo Celestial Pack", weight = 1, kind = 'Celestial', cost = 6, pos = {x=0,y=3}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 1}}, + p_celestial_jumbo_2 = {order = 14, discovered = false, name = "Jumbo Celestial Pack", weight = 1, kind = 'Celestial', cost = 6, pos = {x=1,y=3}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 1}}, + p_celestial_mega_1 = {order = 15, discovered = false, name = "Mega Celestial Pack", weight = 0.25, kind = 'Celestial', cost = 8, pos = {x=2,y=3}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 2}}, + p_celestial_mega_2 = {order = 16, discovered = false, name = "Mega Celestial Pack", weight = 0.25, kind = 'Celestial', cost = 8, pos = {x=3,y=3}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 2}}, + p_spectral_normal_1 = {order = 29, discovered = false, name = "Spectral Pack", weight = 0.3, kind = 'Spectral', cost = 4, pos = {x=0,y=4}, atlas = 'Booster', set = 'Booster', config = {extra = 2, choose = 1}}, + p_spectral_normal_2 = {order = 30, discovered = false, name = "Spectral Pack", weight = 0.3, kind = 'Spectral', cost = 4, pos = {x=1,y=4}, atlas = 'Booster', set = 'Booster', config = {extra = 2, choose = 1}}, + p_spectral_jumbo_1 = {order = 31, discovered = false, name = "Jumbo Spectral Pack", weight = 0.3, kind = 'Spectral', cost = 6, pos = {x=2,y=4}, atlas = 'Booster', set = 'Booster', config = {extra = 4, choose = 1}}, + p_spectral_mega_1 = {order = 32, discovered = false, name = "Mega Spectral Pack", weight = 0.07, kind = 'Spectral', cost = 8, pos = {x=3,y=4}, atlas = 'Booster', set = 'Booster', config = {extra = 4, choose = 2}}, + p_standard_normal_1 = {order = 17, discovered = false, name = "Standard Pack", weight = 1, kind = 'Standard', cost = 4, pos = {x=0,y=6}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_standard_normal_2 = {order = 18, discovered = false, name = "Standard Pack", weight = 1, kind = 'Standard', cost = 4, pos = {x=1,y=6}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_standard_normal_3 = {order = 19, discovered = false, name = "Standard Pack", weight = 1, kind = 'Standard', cost = 4, pos = {x=2,y=6}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_standard_normal_4 = {order = 20, discovered = false, name = "Standard Pack", weight = 1, kind = 'Standard', cost = 4, pos = {x=3,y=6}, atlas = 'Booster', set = 'Booster', config = {extra = 3, choose = 1}}, + p_standard_jumbo_1 = {order = 21, discovered = false, name = "Jumbo Standard Pack", weight = 1, kind = 'Standard', cost = 6, pos = {x=0,y=7}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 1}}, + p_standard_jumbo_2 = {order = 22, discovered = false, name = "Jumbo Standard Pack", weight = 1, kind = 'Standard', cost = 6, pos = {x=1,y=7}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 1}}, + p_standard_mega_1 = {order = 23, discovered = false, name = "Mega Standard Pack", weight = 0.25, kind = 'Standard', cost = 8, pos = {x=2,y=7}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 2}}, + p_standard_mega_2 = {order = 24, discovered = false, name = "Mega Standard Pack", weight = 0.25, kind = 'Standard', cost = 8, pos = {x=3,y=7}, atlas = 'Booster', set = 'Booster', config = {extra = 5, choose = 2}}, + p_buffoon_normal_1 = {order = 25, discovered = false, name = "Buffoon Pack", weight = 0.6, kind = 'Buffoon', cost = 4, pos = {x=0,y=8}, atlas = 'Booster', set = 'Booster', config = {extra = 2, choose = 1}}, + p_buffoon_normal_2 = {order = 26, discovered = false, name = "Buffoon Pack", weight = 0.6, kind = 'Buffoon', cost = 4, pos = {x=1,y=8}, atlas = 'Booster', set = 'Booster', config = {extra = 2, choose = 1}}, + p_buffoon_jumbo_1 = {order = 27, discovered = false, name = "Jumbo Buffoon Pack", weight = 0.6, kind = 'Buffoon', cost = 6, pos = {x=2,y=8}, atlas = 'Booster', set = 'Booster', config = {extra = 4, choose = 1}}, + p_buffoon_mega_1 = {order = 28, discovered = false, name = "Mega Buffoon Pack", weight = 0.15, kind = 'Buffoon', cost = 8, pos = {x=3,y=8}, atlas = 'Booster', set = 'Booster', config = {extra = 4, choose = 2}}, + + --Extras + soul={pos = {x=0,y=1}}, + undiscovered_joker={pos = {x=5,y=3}}, + undiscovered_tarot={pos = {x=6,y=3}}, + } + + self.P_CENTER_POOLS = { + Booster = {}, + Default = {}, + Enhanced = {}, + Edition = {}, + Joker = {}, + Tarot = {}, + Planet = {}, + Tarot_Planet = {}, + Spectral = {}, + Consumeables = {}, + Voucher = {}, + Back = {}, + Tag = {}, + Seal = {}, + Stake = {}, + Demo = {} + } + + self.P_JOKER_RARITY_POOLS = { + {},{},{},{} + } + + self.P_LOCKED = {} + + self:save_progress() + + + ------------------------------------- + local TESTHELPER_unlocks = false and not _RELEASE_MODE + ------------------------------------- + if not love.filesystem.getInfo(G.SETTINGS.profile..'') then love.filesystem.createDirectory( G.SETTINGS.profile..'' ) end + if not love.filesystem.getInfo(G.SETTINGS.profile..'/'..'meta.jkr') then love.filesystem.append( G.SETTINGS.profile..'/'..'meta.jkr', 'return {}') end + + convert_save_to_meta() + + local meta = STR_UNPACK(get_compressed(G.SETTINGS.profile..'/'..'meta.jkr') or 'return {}') + meta.unlocked = meta.unlocked or {} + meta.discovered = meta.discovered or {} + meta.alerted = meta.alerted or {} + for _, t in ipairs{ + G.P_CENTERS, + G.P_BLINDS, + G.P_TAGS, + G.P_SEALS, + } do + for k, v in pairs(t) do + SMODS._save_d_u(v) + v._discovered_unlocked_overwritten = true + end + end + + for k, v in pairs(self.P_CENTERS) do + if not v.wip and not v.demo then + if TESTHELPER_unlocks then v.unlocked = true; v.discovered = true;v.alerted = true end --REMOVE THIS + if not v.unlocked and (string.find(k, '^j_') or string.find(k, '^b_') or string.find(k, '^v_')) and meta.unlocked[k] then + v.unlocked = true + end + if not v.unlocked and (string.find(k, '^j_') or string.find(k, '^b_') or string.find(k, '^v_')) then self.P_LOCKED[#self.P_LOCKED+1] = v end + if not v.discovered and (string.find(k, '^j_') or string.find(k, '^b_') or string.find(k, '^e_') or string.find(k, '^c_') or string.find(k, '^p_') or string.find(k, '^v_')) and meta.discovered[k] then + v.discovered = true + end + if v.discovered and meta.alerted[k] or v.set == 'Back' or v.start_alerted then + v.alerted = true + elseif v.discovered then + v.alerted = false + end + end + end + + table.sort(self.P_LOCKED, function (a, b) return not a.order or not b.order or a.order < b.order end) + + for k, v in pairs(self.P_BLINDS) do + v.key = k + if not v.wip and not v.demo then + if TESTHELPER_unlocks then v.discovered = true; v.alerted = true end --REMOVE THIS + if not v.discovered and meta.discovered[k] then + v.discovered = true + end + if v.discovered and meta.alerted[k] then + v.alerted = true + elseif v.discovered then + v.alerted = false + end + end + end + for k, v in pairs(self.P_TAGS) do + v.key = k + if not v.wip and not v.demo then + if TESTHELPER_unlocks then v.discovered = true; v.alerted = true end --REMOVE THIS + if not v.discovered and meta.discovered[k] then + v.discovered = true + end + if v.discovered and meta.alerted[k] then + v.alerted = true + elseif v.discovered then + v.alerted = false + end + table.insert(self.P_CENTER_POOLS['Tag'], v) + end + end + for k, v in pairs(self.P_SEALS) do + v.key = k + if not v.wip and not v.demo then + if TESTHELPER_unlocks then v.discovered = true; v.alerted = true end --REMOVE THIS + if not v.discovered and meta.discovered[k] then + v.discovered = true + end + if v.discovered and meta.alerted[k] then + v.alerted = true + elseif v.discovered then + v.alerted = false + end + table.insert(self.P_CENTER_POOLS['Seal'], v) + end + end + for k, v in pairs(self.P_STAKES) do + v.key = k + table.insert(self.P_CENTER_POOLS['Stake'], v) + end + + for k, v in pairs(self.P_CENTERS) do + v.key = k + if v.set == 'Joker' then table.insert(self.P_CENTER_POOLS['Joker'], v) end + if v.set and v.demo and v.pos then table.insert(self.P_CENTER_POOLS['Demo'], v) end + if not v.wip then + if v.set and v.set ~= 'Joker' and not v.skip_pool and not v.omit then table.insert(self.P_CENTER_POOLS[v.set], v) end + if v.set == 'Tarot' or v.set == 'Planet' then table.insert(self.P_CENTER_POOLS['Tarot_Planet'], v) end + if v.consumeable then table.insert(self.P_CENTER_POOLS['Consumeables'], v) end + if v.rarity and v.set == 'Joker' and not v.demo then table.insert(self.P_JOKER_RARITY_POOLS[v.rarity], v) end + end + end + + table.sort(self.P_CENTER_POOLS["Joker"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Tarot"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Planet"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Tarot_Planet"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Spectral"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Voucher"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Booster"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Consumeables"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Back"], function (a, b) return (a.order - (a.unlocked and 100 or 0)) < (b.order - (b.unlocked and 100 or 0)) end) + table.sort(self.P_CENTER_POOLS["Enhanced"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Edition"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Stake"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Tag"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Seal"], function (a, b) return a.order < b.order end) + table.sort(self.P_CENTER_POOLS["Demo"], function (a, b) return a.order + (a.set == 'Joker' and 1000 or 0) < b.order + (b.set == 'Joker' and 1000 or 0) end) + for i = 1, 4 do + table.sort(self.P_JOKER_RARITY_POOLS[i], function (a, b) return a.order < b.order end) + end +end + +function Game:load_profile(_profile) + if not G.PROFILES[_profile] then _profile = 1 end + G.SETTINGS.profile = _profile + + --Load the settings file + local info = get_compressed(_profile..'/profile.jkr') + if info ~= nil then + for k, v in pairs(STR_UNPACK(info)) do + G.PROFILES[G.SETTINGS.profile][k] = v + end + end + + local temp_profile = { + MEMORY = { + deck = 'Red Deck', + stake = 1, + }, + stake = 1, + + high_scores = { + hand = {label = 'Best Hand', amt = 0}, + furthest_round = {label = 'Highest Round', amt = 0}, + furthest_ante = {label = 'Highest Ante', amt = 0}, + most_money = {label = 'Most Money', amt = 0}, + boss_streak = {label = 'Most Bosses in a Row', amt = 0}, + collection = {label = 'Collection', amt = 0, tot = 1}, + win_streak = {label = 'Best Win Streak', amt = 0}, + current_streak = {label = '', amt = 0}, + poker_hand = {label = 'Most Played Hand', amt = 0} + }, + + career_stats = { + c_round_interest_cap_streak = 0, + c_dollars_earned = 0, + c_shop_dollars_spent = 0, + c_tarots_bought = 0, + c_planets_bought = 0, + c_playing_cards_bought = 0, + c_vouchers_bought = 0, + c_tarot_reading_used = 0, + c_planetarium_used = 0, + c_shop_rerolls = 0, + c_cards_played = 0, + c_cards_discarded = 0, + c_losses = 0, + c_wins = 0, + c_rounds = 0, + c_hands_played = 0, + c_face_cards_played = 0, + c_jokers_sold = 0, + c_cards_sold = 0, + c_single_hand_round_streak = 0, + }, + progress = { + + }, + joker_usage = {}, + consumeable_usage = {}, + voucher_usage = {}, + hand_usage = {}, + deck_usage = {}, + deck_stakes = {}, + challenges_unlocked = nil, + challenge_progress = { + completed = {}, + unlocked = {} + } + } + local recursive_init + recursive_init = function(t1, t2) + for k, v in pairs(t1) do + if not t2[k] then + t2[k] = v + elseif type(t2[k]) == 'table' and type(v) == 'table' then + recursive_init(v, t2[k]) + end + end + end + + recursive_init(temp_profile, G.PROFILES[G.SETTINGS.profile]) +end + +function Game:set_language() + if not self.LANGUAGES then + if false then + ------------------------------------------ + --SET LANGUAGE FOR FIRST TIME STARTUP HERE + + ------------------------------------------ + + G.SETTINGS.language = 'en-us' + end + ------------------------------------------------------- + --IF LANGUAGE NEEDS TO BE SET ON EVERY REBOOT, SET HERE + + ------------------------------------------------------- + + self.LANGUAGES = { + ['en-us'] = {font = 1, label = "English", key = 'en-us', button = "Language Feedback", warning = {'This language is still in Beta. To help us','improve it, please click on the feedback button.', 'Click again to confirm'}}, + ['de'] = {font = 1, label = "Deutsch", key = 'de', beta = nil, button = "Feedback zur Übersetzung", warning = {'Diese Übersetzung ist noch im Beta-Stadium. Willst du uns helfen,','sie zu verbessern? Dann klicke bitte auf die Feedback-Taste.', "Zum Bestätigen erneut klicken"}}, + ['es_419'] = {font = 1, label = "Español (México)", key = 'es_419', beta = nil, button = "Sugerencias de idioma", warning = {'Este idioma todavía está en Beta. Pulsa el botón','de sugerencias para ayudarnos a mejorarlo.', "Haz clic de nuevo para confirmar"}}, + ['es_ES'] = {font = 1, label = "Español (España)", key = 'es_ES', beta = nil, button = "Sugerencias de idioma", warning = {'Este idioma todavía está en Beta. Pulsa el botón','de sugerencias para ayudarnos a mejorarlo.', "Haz clic de nuevo para confirmar"}}, + ['fr'] = {font = 1, label = "Français", key = 'fr', beta = nil, button = "Partager votre avis", warning = {'La traduction française est encore en version bêta. ','Veuillez cliquer sur le bouton pour nous donner votre avis.', "Cliquez à nouveau pour confirmer"}}, + ['id'] = {font = 1, label = "Bahasa Indonesia", key = 'id', beta = true, button = "Umpan Balik Bahasa", warning = {'Bahasa ini masih dalam tahap Beta. Untuk membantu','kami meningkatkannya, silakan klik tombol umpan balik.', "Klik lagi untuk mengonfirmasi"}}, + ['it'] = {font = 1, label = "Italiano", key = 'it', beta = nil, button = "Feedback traduzione", warning = {'Questa traduzione è ancora in Beta. Per','aiutarci a migliorarla, clicca il tasto feedback', "Fai clic di nuovo per confermare"}}, + ['ja'] = {font = 5, label = "日本語", key = 'ja', beta = nil, button = "提案する", warning = {'この翻訳は現在ベータ版です。提案があった場合、','ボタンをクリックしてください。', "もう一度クリックして確認"}}, + ['ko'] = {font = 4, label = "한국어", key = 'ko', beta = nil, button = "번역 피드백", warning = {'이 언어는 아직 베타 단계에 있습니다. ','번역을 도와주시려면 피드백 버튼을 눌러주세요.', "다시 클릭해서 확인하세요"}}, + ['nl'] = {font = 1, label = "Nederlands", key = 'nl', beta = nil, button = "Taal suggesties", warning = {'Deze taal is nog in de Beta fase. Help ons het te ','verbeteren door op de suggestie knop te klikken.', "Klik opnieuw om te bevestigen"}}, + ['pl'] = {font = 1, label = "Polski", key = 'pl', beta = nil, button = "Wyślij uwagi do tłumaczenia", warning = {'Polska wersja językowa jest w fazie Beta. By pomóc nam poprawić',' jakość tłumaczenia, kliknij przycisk i podziel się swoją opinią i uwagami.', "Kliknij ponownie, aby potwierdzić"}}, + ['pt_BR'] = {font = 1, label = "Português", key = 'pt_BR', beta = nil, button = "Feedback de Tradução", warning = {'Esta tradução ainda está em Beta. Se quiser nos ajudar','a melhorá-la, clique no botão de feedback por favor', "Clique novamente para confirmar"}}, + ['ru'] = {font = 6, label = "Русский", key = 'ru', beta = true, button = "Отзыв о языке", warning = {'Этот язык все еще находится в Бета-версии. Чтобы помочь','нам его улучшить, пожалуйста, нажмите на кнопку обратной связи.', "Щелкните снова, чтобы подтвердить"}}, + ['zh_CN'] = {font = 2, label = "简体中文", key = 'zh_CN', beta = nil, button = "意见反馈", warning = {'这个语言目前尚为Beta版本。 请帮助我们改善翻译品质,','点击”意见反馈” 来提供你的意见。', "再次点击确认"}}, + ['zh_TW'] = {font = 3, label = "繁體中文", key = 'zh_TW', beta = nil, button = "意見回饋", warning = {'這個語言目前尚為Beta版本。請幫助我們改善翻譯品質,','點擊”意見回饋” 來提供你的意見。', "再按一下即可確認"}}, + ['all1'] = {font = 8, label = "English", key = 'all', omit = true}, + ['all2'] = {font = 9, label = "English", key = 'all', omit = true}, + } + --if G.F_ENGLISH_ONLY then + -- self.LANGUAGES = { + -- ['en-us'] = self.LANGUAGES['en-us'] + -- } + --end + + --load the font and set filter + self.FONTS = { + {file = "resources/fonts/m6x11plus.ttf", render_scale = self.TILESIZE*10, TEXT_HEIGHT_SCALE = 0.83, TEXT_OFFSET = {x=10,y=-20}, FONTSCALE = 0.1, squish = 1, DESCSCALE = 1}, + {file = "resources/fonts/NotoSansSC-Bold.ttf", render_scale = self.TILESIZE*7, TEXT_HEIGHT_SCALE = 0.7, TEXT_OFFSET = {x=0,y=-35}, FONTSCALE = 0.12, squish = 1, DESCSCALE = 1.1}, + {file = "resources/fonts/NotoSansTC-Bold.ttf", render_scale = self.TILESIZE*7, TEXT_HEIGHT_SCALE = 0.7, TEXT_OFFSET = {x=0,y=-35}, FONTSCALE = 0.12, squish = 1, DESCSCALE = 1.1}, + {file = "resources/fonts/NotoSansKR-Bold.ttf", render_scale = self.TILESIZE*7, TEXT_HEIGHT_SCALE = 0.8, TEXT_OFFSET = {x=0,y=-20}, FONTSCALE = 0.12, squish = 1, DESCSCALE = 1}, + {file = "resources/fonts/NotoSansJP-Bold.ttf", render_scale = self.TILESIZE*7, TEXT_HEIGHT_SCALE = 0.8, TEXT_OFFSET = {x=0,y=-20}, FONTSCALE = 0.12, squish = 1, DESCSCALE = 1}, + {file = "resources/fonts/NotoSans-Bold.ttf", render_scale = self.TILESIZE*7, TEXT_HEIGHT_SCALE = 0.65, TEXT_OFFSET = {x=0,y=-40}, FONTSCALE = 0.12, squish = 1, DESCSCALE = 1}, + {file = "resources/fonts/m6x11plus.ttf", render_scale = self.TILESIZE*10, TEXT_HEIGHT_SCALE = 0.9, TEXT_OFFSET = {x=10,y=15}, FONTSCALE = 0.1, squish = 1, DESCSCALE = 1}, + {file = "resources/fonts/GoNotoCurrent-Bold.ttf", render_scale = self.TILESIZE*10, TEXT_HEIGHT_SCALE = 0.8, TEXT_OFFSET = {x=10,y=-20}, FONTSCALE = 0.1, squish = 1, DESCSCALE = 1}, + {file = "resources/fonts/GoNotoCJKCore.ttf", render_scale = self.TILESIZE*10, TEXT_HEIGHT_SCALE = 0.8, TEXT_OFFSET = {x=10,y=-20}, FONTSCALE = 0.1, squish = 1, DESCSCALE = 1}, + } + for _, v in ipairs(self.FONTS) do + if love.filesystem.getInfo(v.file) then + v.FONT = love.graphics.newFont( v.file, v.render_scale) + end + end + for _, v in pairs(self.LANGUAGES) do + v.font = self.FONTS[v.font] + end + end + + self.LANG = self.LANGUAGES[self.SETTINGS.language] or self.LANGUAGES['en-us'] + + local localization = love.filesystem.getInfo('localization/'..G.SETTINGS.language..'.lua') or love.filesystem.getInfo('localization/en-us.lua') + if localization ~= nil then + self.localization = assert(loadstring(love.filesystem.read('localization/'..G.SETTINGS.language..'.lua') or love.filesystem.read('localization/en-us.lua')))() + init_localization() + end +end + +function Game:set_render_settings() + self.SETTINGS.GRAPHICS.texture_scaling = self.SETTINGS.GRAPHICS.texture_scaling or 2 + + --Set fiter to linear interpolation and nearest, best for pixel art + love.graphics.setDefaultFilter( + self.SETTINGS.GRAPHICS.texture_scaling == 1 and 'nearest' or 'linear', + self.SETTINGS.GRAPHICS.texture_scaling == 1 and 'nearest' or 'linear', 1) + + love.graphics.setLineStyle("rough") + + --spritesheets + self.animation_atli = { + {name = "blind_chips", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/BlindChips.png",px=34,py=34, frames = 21}, + {name = "shop_sign", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/ShopSignAnimation.png",px=113,py=57, frames = 4} + } + self.asset_atli = { + {name = "cards_1", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/8BitDeck.png",px=71,py=95}, + {name = "cards_2", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/8BitDeck_opt2.png",px=71,py=95}, + {name = "centers", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/Enhancers.png",px=71,py=95}, + {name = "Joker", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/Jokers.png",px=71,py=95}, + {name = "Tarot", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/Tarots.png",px=71,py=95}, + {name = "Voucher", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/Vouchers.png",px=71,py=95}, + {name = "Booster", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/boosters.png",px=71,py=95}, + {name = "ui_1", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/ui_assets.png",px=18,py=18}, + {name = "ui_2", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/ui_assets_opt2.png",px=18,py=18}, + {name = "balatro", path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/balatro.png",px=333,py=216}, + {name = 'gamepad_ui', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/gamepad_ui.png",px=32,py=32}, + {name = 'icons', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/icons.png",px=66,py=66}, + {name = 'tags', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/tags.png",px=34,py=34}, + {name = 'stickers', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/stickers.png",px=71,py=95}, + {name = 'chips', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/chips.png",px=29,py=29}, + + {name = 'collab_AU_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_AU_1.png",px=71,py=95}, + {name = 'collab_AU_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_AU_2.png",px=71,py=95}, + {name = 'collab_TW_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_TW_1.png",px=71,py=95}, + {name = 'collab_TW_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_TW_2.png",px=71,py=95}, + {name = 'collab_VS_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_VS_1.png",px=71,py=95}, + {name = 'collab_VS_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_VS_2.png",px=71,py=95}, + {name = 'collab_DTD_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_DTD_1.png",px=71,py=95}, + {name = 'collab_DTD_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_DTD_2.png",px=71,py=95}, + {name = 'collab_CYP_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_CYP_1.png",px=71,py=95}, + {name = 'collab_CYP_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_CYP_2.png",px=71,py=95}, + {name = 'collab_STS_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_STS_1.png",px=71,py=95}, + {name = 'collab_STS_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_STS_2.png",px=71,py=95}, + {name = 'collab_TBoI_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_TBoI_1.png",px=71,py=95}, + {name = 'collab_TBoI_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_TBoI_2.png",px=71,py=95}, + {name = 'collab_SV_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_SV_1.png",px=71,py=95}, + {name = 'collab_SV_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_SV_2.png",px=71,py=95}, + + {name = 'collab_SK_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_SK_1.png",px=71,py=95}, + {name = 'collab_SK_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_SK_2.png",px=71,py=95}, + {name = 'collab_DS_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_DS_1.png",px=71,py=95}, + {name = 'collab_DS_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_DS_2.png",px=71,py=95}, + {name = 'collab_CL_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_CL_1.png",px=71,py=95}, + {name = 'collab_CL_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_CL_2.png",px=71,py=95}, + {name = 'collab_D2_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_D2_1.png",px=71,py=95}, + {name = 'collab_D2_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_D2_2.png",px=71,py=95}, + {name = 'collab_PC_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_PC_1.png",px=71,py=95}, + {name = 'collab_PC_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_PC_2.png",px=71,py=95}, + {name = 'collab_WF_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_WF_1.png",px=71,py=95}, + {name = 'collab_WF_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_WF_2.png",px=71,py=95}, + {name = 'collab_EG_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_EG_1.png",px=71,py=95}, + {name = 'collab_EG_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_EG_2.png",px=71,py=95}, + {name = 'collab_XR_1', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_XR_1.png",px=71,py=95}, + {name = 'collab_XR_2', path = "resources/textures/"..self.SETTINGS.GRAPHICS.texture_scaling.."x/collabs/collab_XR_2.png",px=71,py=95}, + } + self.asset_images = { + {name = "playstack_logo", path = "resources/textures/1x/playstack-logo.png", px=1417,py=1417}, + {name = "localthunk_logo", path = "resources/textures/1x/localthunk-logo.png", px=1390,py=560} + } + + --Load in all atli defined above + for i=1, #self.animation_atli do + self.ANIMATION_ATLAS[self.animation_atli[i].name] = {} + self.ANIMATION_ATLAS[self.animation_atli[i].name].name = self.animation_atli[i].name + self.ANIMATION_ATLAS[self.animation_atli[i].name].image = love.graphics.newImage(self.animation_atli[i].path, {mipmaps = true, dpiscale = self.SETTINGS.GRAPHICS.texture_scaling}) + self.ANIMATION_ATLAS[self.animation_atli[i].name].px = self.animation_atli[i].px + self.ANIMATION_ATLAS[self.animation_atli[i].name].py = self.animation_atli[i].py + self.ANIMATION_ATLAS[self.animation_atli[i].name].frames = self.animation_atli[i].frames + end + + for i=1, #self.asset_atli do + self.ASSET_ATLAS[self.asset_atli[i].name] = {} + self.ASSET_ATLAS[self.asset_atli[i].name].name = self.asset_atli[i].name + self.ASSET_ATLAS[self.asset_atli[i].name].image = love.graphics.newImage(self.asset_atli[i].path, {mipmaps = true, dpiscale = self.SETTINGS.GRAPHICS.texture_scaling}) + local mipmap_level = SMODS.config.graphics_mipmap_level_options[SMODS.config.graphics_mipmap_level] + if mipmap_level and mipmap_level > 0 then + self.ASSET_ATLAS[self.asset_atli[i].name].image:setMipmapFilter('linear', mipmap_level) + end + self.ASSET_ATLAS[self.asset_atli[i].name].type = self.asset_atli[i].type + self.ASSET_ATLAS[self.asset_atli[i].name].px = self.asset_atli[i].px + self.ASSET_ATLAS[self.asset_atli[i].name].py = self.asset_atli[i].py + end + for i=1, #self.asset_images do + self.ASSET_ATLAS[self.asset_images[i].name] = {} + self.ASSET_ATLAS[self.asset_images[i].name].name = self.asset_images[i].name + self.ASSET_ATLAS[self.asset_images[i].name].image = love.graphics.newImage(self.asset_images[i].path, {mipmaps = true, dpiscale = 1}) + self.ASSET_ATLAS[self.asset_images[i].name].type = self.asset_images[i].type + self.ASSET_ATLAS[self.asset_images[i].name].px = self.asset_images[i].px + self.ASSET_ATLAS[self.asset_images[i].name].py = self.asset_images[i].py + end + + for _, v in pairs(G.I.SPRITE) do + v:reset() + end + + self.ASSET_ATLAS.Planet = self.ASSET_ATLAS.Tarot + self.ASSET_ATLAS.Spectral = self.ASSET_ATLAS.Tarot +end + +function Game:init_window(reset) + --Initialize the window + self.ROOM_PADDING_H= 0.7 + self.ROOM_PADDING_W = 1 + self.WINDOWTRANS = { + x = 0, y = 0, + w = self.TILE_W+2*self.ROOM_PADDING_W, + h = self.TILE_H+2*self.ROOM_PADDING_H + } + self.window_prev = { + orig_scale = self.TILESCALE, + w=self.WINDOWTRANS.w*self.TILESIZE*self.TILESCALE, + h=self.WINDOWTRANS.h*self.TILESIZE*self.TILESCALE, + orig_ratio = self.WINDOWTRANS.w*self.TILESIZE*self.TILESCALE/(self.WINDOWTRANS.h*self.TILESIZE*self.TILESCALE)} + G.SETTINGS.QUEUED_CHANGE = G.SETTINGS.QUEUED_CHANGE or {} + G.SETTINGS.QUEUED_CHANGE.screenmode = G.SETTINGS.WINDOW.screenmode + + G.FUNCS.apply_window_changes(true) +end + +function Game:delete_run() + if self.ROOM then + remove_all(G.STAGE_OBJECTS[G.STAGE]) + self.load_shop_booster = nil + self.load_shop_jokers = nil + self.load_shop_vouchers = nil + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.deck_preview then self.deck_preview:remove(); self.deck_preview = nil end + if self.shop and not G.GAME.USING_CODE then self.shop:remove(); self.shop = nil end + if self.blind_select then self.blind_select:remove(); self.blind_select = nil end + if self.booster_pack then self.booster_pack:remove(); self.booster_pack = nil end + if self.MAIN_MENU_UI then self.MAIN_MENU_UI:remove(); self.MAIN_MENU_UI = nil end + if self.SPLASH_FRONT then self.SPLASH_FRONT:remove(); self.SPLASH_FRONT = nil end + if self.SPLASH_BACK then self.SPLASH_BACK:remove(); self.SPLASH_BACK = nil end + if self.SPLASH_LOGO then self.SPLASH_LOGO:remove(); self.SPLASH_LOGO = nil end + if self.GAME_OVER_UI then self.GAME_OVER_UI:remove(); self.GAME_OVER_UI = nil end + if self.collection_alert then self.collection_alert:remove(); self.collection_alert = nil end + if self.HUD then self.HUD:remove(); self.HUD = nil end + if self.HUD_blind then self.HUD_blind:remove(); self.HUD_blind = nil end + if self.HUD_tags then + for k, v in pairs(self.HUD_tags) do + v:remove() + end + self.HUD_tags = nil + end + if self.OVERLAY_MENU then self.OVERLAY_MENU:remove(); self.OVERLAY_MENU = nil end + if self.OVERLAY_TUTORIAL then + G.OVERLAY_TUTORIAL.Jimbo:remove() + if G.OVERLAY_TUTORIAL.content then G.OVERLAY_TUTORIAL.content:remove() end + G.OVERLAY_TUTORIAL:remove() + G.OVERLAY_TUTORIAL = nil + end + for k, v in pairs(G) do + if (type(v) == "table") and v.is and v:is(CardArea) then + G[k] = nil + end + end + G.I.CARD = {} + end + G.VIEWING_DECK = nil + G.E_MANAGER:clear_queue() + G.CONTROLLER:mod_cursor_context_layer(-1000) + G.CONTROLLER.focus_cursor_stack = {} + G.CONTROLLER.focus_cursor_stack_level = 1 + if G.GAME then G.GAME.won = false end + + G.STATE = -1 +end + + + +function Game:save_progress() + G.ARGS.save_progress = G.ARGS.save_progress or {} + G.ARGS.save_progress.UDA = EMPTY(G.ARGS.save_progress.UDA) + G.ARGS.save_progress.SETTINGS = G.SETTINGS + G.ARGS.save_progress.PROFILE = G.PROFILES[G.SETTINGS.profile] + + for k, v in pairs(self.P_CENTERS) do + G.ARGS.save_progress.UDA[k] = (v.unlocked and 'u' or '')..(v.discovered and 'd' or '')..(v.alerted and 'a' or '') + end + for k, v in pairs(self.P_BLINDS) do + G.ARGS.save_progress.UDA[k] = (v.unlocked and 'u' or '')..(v.discovered and 'd' or '')..(v.alerted and 'a' or '') + end + for k, v in pairs(self.P_TAGS) do + G.ARGS.save_progress.UDA[k] = (v.unlocked and 'u' or '')..(v.discovered and 'd' or '')..(v.alerted and 'a' or '') + end + for k, v in pairs(self.P_SEALS) do + G.ARGS.save_progress.UDA[k] = (v.unlocked and 'u' or '')..(v.discovered and 'd' or '')..(v.alerted and 'a' or '') + end + + G.FILE_HANDLER = G.FILE_HANDLER or {} + G.FILE_HANDLER.progress = true + G.FILE_HANDLER.update_queued = true +end + +function Game:save_notify(card) + G.SAVE_MANAGER.channel:push({ + type = 'save_notify', + save_notify = card.key, + profile_num = G.SETTINGS.profile + }) +end + +function Game:save_settings() + G.ARGS.save_settings = G.SETTINGS + G.FILE_HANDLER = G.FILE_HANDLER or {} + G.FILE_HANDLER.settings = true + G.FILE_HANDLER.update_queued = true +end + +function Game:save_metrics() + G.ARGS.save_metrics = G.METRICS + G.FILE_HANDLER = G.FILE_HANDLER or {} + G.FILE_HANDLER.settings = true + G.FILE_HANDLER.update_queued = true +end + +function Game:prep_stage(new_stage, new_state, new_game_obj) + for k, v in pairs(self.CONTROLLER.locks) do + self.CONTROLLER.locks[k] = nil + end + if new_game_obj then self.GAME = self:init_game_object() end + if new_game_obj and Talisman and Talisman.igo then self.GAME = Talisman.igo(self.GAME) end + self.STAGE = new_stage or self.STAGES.MAIN_MENU + self.STATE = new_state or self.STATES.MENU + self.STATE_COMPLETE = false + self.SETTINGS.paused = false + + self.ROOM = Node{T={ + x = self.ROOM_PADDING_W, + y = self.ROOM_PADDING_H, + w = self.TILE_W, + h = self.TILE_H} + } + self.ROOM.jiggle = 0 + self.ROOM.states.drag.can = false + self.ROOM:set_container(self.ROOM) + + self.ROOM_ATTACH = Moveable{T={ + x = 0, + y = 0, + w = self.TILE_W, + h = self.TILE_H} + } + self.ROOM_ATTACH.states.drag.can = false + self.ROOM_ATTACH:set_container(self.ROOM) + love.resize(love.graphics.getWidth( ),love.graphics.getHeight( )) +end + +function Game:sandbox() + G.TIMERS.REAL = 0 + G.TIMERS.TOTAL = 0 + + self:prep_stage(G.STAGES.SANDBOX, G.STATES.SANDBOX, true) + self.GAME.selected_back = Back(G.P_CENTERS.b_red) + + ease_background_colour{new_colour = G.C.BLACK, contrast = 1} + + G.SANDBOX = { + vort_time = 7, + vort_speed = 0, + col_op = {'RED','BLUE','GREEN','BLACK','L_BLACK','WHITE','EDITION','DARK_EDITION','ORANGE','PURPLE'}, + col1 = G.C.RED,col2 = G.C.BLUE, + mid_flash = 0, + joker_text = '', + edition = 'base', + tilt = 1, + card_size = 1, + base_size = {w = G.CARD_W, h = G.CARD_H}, + gamespeed = 0 + } + + if G.SPLASH_FRONT then G.SPLASH_FRONT:remove(); G.SPLASH_FRONT = nil end + if G.SPLASH_BACK then G.SPLASH_BACK:remove(); G.SPLASH_BACK = nil end + + G.SPLASH_BACK = Sprite(-30, -13, G.ROOM.T.w+60, G.ROOM.T.h+22, G.ASSET_ATLAS["ui_"..(G.SETTINGS.colourblind_option and 2 or 1)], {x = 2, y = 0}) + G.SPLASH_BACK:set_alignment({ + major = G.ROOM_ATTACH, + type = 'cm', + offset = {x=0,y=0} + }) + + G.SPLASH_BACK:define_draw_steps({{ + shader = 'splash', + send = { + {name = 'time', ref_table = G.SANDBOX, ref_value = 'vort_time'}, + {name = 'vort_speed', val = 0.4}, + {name = 'colour_1', ref_table = G.SANDBOX, ref_value = 'col1'}, + {name = 'colour_2', ref_table = G.SANDBOX, ref_value = 'col2'}, + {name = 'mid_flash', ref_table = G.SANDBOX, ref_value = 'mid_flash'}, + {name = 'vort_offset', val = 0}, + }}}) + + function create_UIBox_sandbox_controls() + G.FUNCS.col1change = function(args) + G.SANDBOX.col1 = G.C[args.to_val] + end + G.FUNCS.col2change = function(args) + G.SANDBOX.col2 = G.C[args.to_val] + end + G.FUNCS.edition_change = function(args) + G.SANDBOX.edition = args.to_val + if G.SANDBOX.joker then G.SANDBOX.joker:set_edition({[args.to_val] = true}, true, true) end + end + G.FUNCS.pulseme = function(e) + if math.random() > 0.998 then e.config.object:pulse(1) end + end + G.FUNCS.spawn_joker = function(e) G.FUNCS.rem_joker(); G.SANDBOX.joker = add_joker(G.SANDBOX.joker_text, G.SANDBOX.edition) end + G.FUNCS.rem_joker = function(e) if G.SANDBOX.joker then G.SANDBOX.joker:remove(); G.SANDBOX.joker = nil end end + G.FUNCS.do_time = function(args) if args.to_val == 'PLAY' then G.SANDBOX.gamespeed = 1 else G.SANDBOX.gamespeed = 0 end end + G.FUNCS.cb = function(rt) G.CARD_W = rt.ref_table[rt.ref_value]*G.SANDBOX.base_size.w; G.CARD_H = rt.ref_table[rt.ref_value]*G.SANDBOX.base_size.h end + G.E_MANAGER:add_event(Event({ + func = (function() + G.SANDBOX.file_reload_timer = (G.SANDBOX.file_reload_timer or 0) + if G.SANDBOX.file_reload_timer < G.TIMERS.REAL then + G.SANDBOX.file_reload_timer = G.SANDBOX.file_reload_timer+0.25 + end + if G.SANDBOX.joker then G.SANDBOX.joker.ambient_tilt = G.SANDBOX.tilt end + G.SANDBOX.vort_time = G.SANDBOX.vort_time + G.real_dt*G.SANDBOX.gamespeed + G.CONTROLLER.lock_input = false + end) + })) + + local t = { + n=G.UIT.ROOT, config = {align = "cm",colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.05, r = 0.1, emboss = 0.1, colour = G.C.L_BLACK}, nodes={ + create_slider({label = 'Time', w = 2, h = 0.3, text_scale = 0.2, label_scale = 0.3, ref_table = G.SANDBOX, ref_value = 'vort_time', min = 0, max = 30}), + create_option_cycle({options = {'PLAY','PAUSE'}, opt_callback = 'do_time', current_option = 1, colour = G.C.RED, w = 2, scale = 0.7}), + create_slider({label = 'tilt', w = 2, h = 0.3, text_scale = 0.2, label_scale = 0.3, ref_table = G.SANDBOX, ref_value = 'tilt', min = 0, max = 3, decimal_places = 2}), + create_slider({label = 'Card size', w = 2, h = 0.3, text_scale = 0.2, label_scale = 0.3, ref_table = G.SANDBOX, ref_value = 'card_size', min = 0.1, max = 3, callback = 'cb', decimal_places = 2}), + create_option_cycle({options = G.SANDBOX.col_op, opt_callback = 'col1change', current_option = 1, colour = G.C.RED, w = 2, scale = 0.7}), + create_option_cycle({options = G.SANDBOX.col_op, opt_callback = 'col2change', current_option = 2, colour = G.C.RED, w = 2, scale = 0.7}), + {n=G.UIT.R, config={align = "cm", padding = 0.05}, nodes = { + UIBox_button{ label = {"+"}, button = "spawn_joker", minw = 0.7, col = true}, + create_text_input({prompt_text = 'Joker key', extended_corpus = true, ref_table = G.SANDBOX, ref_value = 'joker_text', text_scale = 0.3, w = 1.5, h = 0.6}), + UIBox_button{ label = {"-"}, button = "rem_joker", minw = 0.7, col = true}, + }}, + create_option_cycle({options = {'base', 'foil', 'holo', 'polychrome','negative'}, opt_callback = 'edition_change', current_option = 1, colour = G.C.RED, w = 2, scale = 0.7}), + }} + }} + return t + end + + + G.SANDBOX.UI = UIBox{ + definition = create_UIBox_sandbox_controls(), + config = {align="cli", offset = {x=0,y=0}, major = G.ROOM_ATTACH, bond = 'Weak'} + } + + G.SANDBOX.UI:recalculate(true) +end + +function Game:splash_screen() + --If the skip splash screen option is set, immediately go to the main menu here + if G.SETTINGS.skip_splash == 'Yes' then + G:main_menu() + return + end + + self:prep_stage(G.STAGES.MAIN_MENU, G.STATES.SPLASH, true) + G.E_MANAGER:add_event(Event({ + func = (function() + discover_card() + return true + end) + })) + + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + G.TIMERS.TOTAL = 0 + G.TIMERS.REAL = 0 + --Prep the splash screen shaders for both the background(colour swirl) and the foreground(white flash), starting at black + G.SPLASH_BACK = Sprite(-30, -13, G.ROOM.T.w+60, G.ROOM.T.h+22, G.ASSET_ATLAS["ui_1"], {x = 2, y = 0}) + G.SPLASH_BACK:define_draw_steps({{ + shader = 'splash', + send = { + {name = 'time', ref_table = G.TIMERS, ref_value = 'REAL'}, + {name = 'vort_speed', val = 1}, + {name = 'colour_1', ref_table = G.C, ref_value = 'BLUE'}, + {name = 'colour_2', ref_table = G.C, ref_value = 'WHITE'}, + {name = 'mid_flash', val = 0}, + {name = 'vort_offset', val = (2*90.15315131*os.time())%100000}, + }}}) + G.SPLASH_BACK:set_alignment({ + major = G.ROOM_ATTACH, + type = 'cm', + offset = {x=0,y=0} + }) + G.SPLASH_FRONT = Sprite(0,-20, G.ROOM.T.w*2, G.ROOM.T.h*4, G.ASSET_ATLAS["ui_1"], {x = 2, y = 0}) + G.SPLASH_FRONT:define_draw_steps({{ + shader = 'flash', + send = { + {name = 'time', ref_table = G.TIMERS, ref_value = 'REAL'}, + {name = 'mid_flash', val = 1} + }}}) + G.SPLASH_FRONT:set_alignment({ + major = G.ROOM_ATTACH, + type = 'cm', + offset = {x=0,y=0} + }) + + --spawn in splash card + local SC = nil + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 0.2,func = (function() + local SC_scale = 1.2 + SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_joker']) + if Cryptid.enabled["Menu"] then + if Cryptid.enabled["M Jokers"] then + local mcard = {} + for k, _ in pairs(Cryptid.M_jokers) do + if G.P_CENTERS[k] then + mcard[#mcard + 1] = k + end + end + local option = math.random(#mcard) + local chosenoption = mcard[option] + if chosenoption == "j_cry_biggestm" or chosenoption == "j_cry_reverse" then --These don't render properly; replace these with loopy instead + SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_cry_loopy']) + else + SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[chosenoption]) + end + else + SC = Card(G.ROOM.T.w/2 - SC_scale*G.CARD_W/2, 10. + G.ROOM.T.h/2 - SC_scale*G.CARD_H/2, SC_scale*G.CARD_W, SC_scale*G.CARD_H, G.P_CARDS.empty, G.P_CENTERS['j_jolly']) + end + end + SC.T.y = G.ROOM.T.h/2 - SC_scale*G.CARD_H/2 + SC.ambient_tilt = 1 + SC.states.drag.can = false + SC.states.hover.can = false + SC.no_ui = true + G.VIBRATION = G.VIBRATION + 2 + play_sound('whoosh1', 0.7, 0.2) + play_sound('introPad1', 0.704, 0.6) + return true;end)})) + + --dissolve fool card and start to fade in the vortex + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 1.8,func = (function() --||||||||||| + SC:start_dissolve({G.C.WHITE, G.C.WHITE},true, 12, true) + play_sound('magic_crumple', 1, 0.5) + play_sound('splash_buildup', 1, 0.7) + return true;end)})) + + --create all the cards and suck them in + function make_splash_card(args) + args = args or {} + local angle = math.random()*2*3.14 + local card_size = (args.scale or 1.5)*(math.random() + 1) + local card_pos = args.card_pos or { + x = (18 + card_size)*math.sin(angle), + y = (18 + card_size)*math.cos(angle) + } + local card = Card( card_pos.x + G.ROOM.T.w/2 - G.CARD_W*card_size/2, + card_pos.y + G.ROOM.T.h/2 - G.CARD_H*card_size/2, + card_size*G.CARD_W, card_size*G.CARD_H, pseudorandom_element(G.P_CARDS), G.P_CENTERS.c_base) + if Cryptid.enabled["Menu"] then card:set_ability(get_random_consumable('cry_splash',{"no_grc"},nil,nil,true), true, nil) end + if math.random() > 0.8 then card.sprite_facing = 'back'; card.facing = 'back' end + card.no_shadow = true + card.states.hover.can = false + card.states.drag.can = false + card.vortex = true and not args.no_vortex + card.T.r = angle + return card, card_pos + end + + G.vortex_time = G.TIMERS.REAL + local temp_del = nil + + for i = 1, 200 do + temp_del = temp_del or 3 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + blockable = false, + delay = temp_del, + func = (function() + local card, card_pos = make_splash_card({scale = 2 - i/300}) + local speed = math.max(2. - i*0.005, 0.001) + ease_value(card.T, 'scale', -card.T.scale, nil, nil, nil, 1.*speed, 'elastic') + ease_value(card.T, 'x', -card_pos.x, nil, nil, nil, 0.9*speed) + ease_value(card.T, 'y', -card_pos.y, nil, nil, nil, 0.9*speed) + local temp_pitch = i*0.007 + 0.6 + local temp_i = i + G.E_MANAGER:add_event(Event({ + blockable = false, + func = (function() + if card.T.scale <= 0 then + if temp_i < 30 then + play_sound('whoosh1', temp_pitch + math.random()*0.05, 0.25*(1 - temp_i/50)) + end + + if temp_i == 15 then + play_sound('whoosh_long',0.9, 0.7) + end + G.VIBRATION = G.VIBRATION + 0.1 + card:remove() + return true + end + end)})) + return true + end)})) + temp_del = temp_del + math.max(1/(i), math.max(0.2*(170-i)/500, 0.016)) + end + + --when faded to white, spit out the 'Fool's' cards and slowly have them settle in to place + G.E_MANAGER:add_event(Event({trigger = 'after',delay = 2.,func = (function() + G.SPLASH_BACK:remove() + G.SPLASH_BACK = G.SPLASH_FRONT + G.SPLASH_FRONT = nil + G:main_menu('splash') + return true;end)})) + return true + end) + })) +end + +function Game:main_menu(change_context) --True if main menu is accessed from the splash screen, false if it is skipped or accessed from the game + if change_context ~= 'splash' then + --Skip the timer to 14 seconds for all shaders that need it + G.TIMERS.REAL = 12 + G.TIMERS.TOTAL = 12 + else + --keep all sounds that came from splash screen + RESET_STATES(G.STATES.MENU) + end + + --Prepare the main menu, reset the default deck + self:prep_stage(G.STAGES.MAIN_MENU, G.STATES.MENU, true) + self.GAME.selected_back = Back(G.P_CENTERS.b_red) + + if (not G.SETTINGS.tutorial_complete) and G.SETTINGS.tutorial_progress.completed_parts['big_blind'] then G.SETTINGS.tutorial_complete = true end + + G.FUNCS.change_shadows{to_key = G.SETTINGS.GRAPHICS.shadows == 'On' and 1 or 2} + + ease_background_colour{new_colour = G.C.BLACK, contrast = 1} + + if G.SPLASH_FRONT then G.SPLASH_FRONT:remove(); G.SPLASH_FRONT = nil end + if G.SPLASH_BACK then G.SPLASH_BACK:remove(); G.SPLASH_BACK = nil end + G.SPLASH_BACK = Sprite(-30, -13, G.ROOM.T.w+60, G.ROOM.T.h+22, G.ASSET_ATLAS["ui_1"], {x = 2, y = 0}) + G.SPLASH_BACK:set_alignment({ + major = G.ROOM_ATTACH, + type = 'cm', + offset = {x=0,y=0} + }) + local splash_args = {mid_flash = change_context == 'splash' and 1.6 or 0.} + ease_value(splash_args, 'mid_flash', -(change_context == 'splash' and 1.6 or 0), nil, nil, nil, 4) + + G.SPLASH_BACK:define_draw_steps({{ + shader = 'splash', + send = { + {name = 'time', ref_table = G.TIMERS, ref_value = 'REAL_SHADER'}, + {name = 'vort_speed', val = 0.4}, + {name = 'colour_1', ref_table = G.C, ref_value = 'RED'}, + {name = 'colour_2', ref_table = G.C, ref_value = 'BLUE'}, + {name = 'mid_flash', ref_table = splash_args, ref_value = 'mid_flash'}, + {name = 'vort_offset', val = 0}, + }}}) + + --Display the unlocked decks and cards from the previous run + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + unlock_notify() + return true + end) + })) + + + local SC_scale = 1.1*(G.debug_splash_size_toggle and 0.8 or 1) + local CAI = { + TITLE_TOP_W = G.CARD_W, + TITLE_TOP_H = G.CARD_H, + } + self.title_top = CardArea( + 0, 0, + CAI.TITLE_TOP_W,CAI.TITLE_TOP_H, + {card_limit = 1, type = 'title'}) + + + G.SPLASH_LOGO = Sprite(0, 0, + 13*SC_scale, + 13*SC_scale*(G.ASSET_ATLAS["balatro"].py/G.ASSET_ATLAS["balatro"].px), + G.ASSET_ATLAS["balatro"], {x=0,y=0}) + + G.SPLASH_LOGO:set_alignment({ + major = G.title_top, + type = 'cm', + bond = 'Strong', + offset = {x=0,y=0} + }) + G.SPLASH_LOGO:define_draw_steps({{ + shader = 'dissolve', + }}) + + G.SPLASH_LOGO.dissolve_colours = {G.C.WHITE, G.C.WHITE} + G.SPLASH_LOGO.dissolve = 1 + + + local replace_card = Card(self.title_top.T.x, self.title_top.T.y, 1.2*G.CARD_W*SC_scale, 1.2*G.CARD_H*SC_scale, G.P_CARDS.S_A, G.P_CENTERS.c_base) + self.title_top:emplace(replace_card) + + replace_card:set_edition(G.P_CENTERS.e_cry_glitched and 'e_cry_glitched' or 'e_negative',true,true) + replace_card:set_seal('Gold', true, true) + replace_card.states.visible = false + replace_card.no_ui = true + replace_card.ambient_tilt = 0.0 + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = change_context == 'game' and 1.5 or 0, + blockable = false, + blocking = false, + func = (function() + if change_context == 'splash' then + replace_card.states.visible = true + replace_card:start_materialize({G.C.WHITE,G.C.WHITE}, true, 2.5) + play_sound('whoosh1', math.random()*0.1 + 0.3,0.3) + play_sound('crumple'..math.random(1,5), math.random()*0.2 + 0.6,0.65) + else + replace_card.states.visible = true + replace_card:start_materialize({G.C.WHITE,G.C.WHITE}, nil, 1.2) + end + G.VIBRATION = G.VIBRATION + 1 + return true + end)})) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = change_context == 'splash' and 1.8 or change_context == 'game' and 2 or 0.5, + blockable = false, + blocking = false, + func = (function() + play_sound('magic_crumple'..(change_context == 'splash' and 2 or 3), (change_context == 'splash' and 1 or 1.3), 0.9) + play_sound('whoosh1', 0.4, 0.8) + ease_value(G.SPLASH_LOGO, 'dissolve', -1, nil, nil, nil, change_context == 'splash' and 2.3 or 0.9) + G.VIBRATION = G.VIBRATION + 1.5 + return true + end)})) + + delay(0.1 + (change_context == 'splash' and 2 or change_context == 'game' and 1.5 or 0)) + + if replace_card and (G.P_CENTERS.j_blueprint.unlocked) then + local viable_unlockables = {} + for k, v in ipairs(self.P_LOCKED) do + if (v.set == 'Voucher' or v.set == 'Joker') and not v.demo then + viable_unlockables[#viable_unlockables+1] = v + end + end + if #viable_unlockables > 0 then + local card + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 4.04, + func = (function() + card = Card(self.title_top.T.x, self.title_top.T.y, 1.2*G.CARD_W*SC_scale, 1.2*G.CARD_H*SC_scale, nil, pseudorandom_element(viable_unlockables) or self.P_CENTERS.j_joker) + card.no_ui = #viable_unlockables == 0 + card.states.visible = false + replace_card.parent = nil + replace_card:start_dissolve({G.C.BLACK, G.C.ORANGE, G.C.RED, G.C.GOLD}) + return true + end)})) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 1.04, + func = (function() + card:start_materialize() + self.title_top:emplace(card) + return true + end)})) + end + end + + G.E_MANAGER:add_event(Event({func = function() G.CONTROLLER.lock_input = false; return true end})) + set_screen_positions() + + self.title_top:sort('order') + self.title_top:set_ranks() + self.title_top:align_cards() + self.title_top:hard_set_cards() + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = change_context == 'splash' and 4.05 or change_context == 'game' and 3 or 1.5, + blockable = false, + blocking = false, + func = (function() + set_main_menu_UI() + return true + end) + })) + + --Do all career stat unlock checking here as well + for k, v in pairs(G.PROFILES[G.SETTINGS.profile].career_stats) do + check_for_unlock({type = 'career_stat', statname = k}) + end + check_for_unlock({type = 'blind_discoveries'}) + if change_context ~= "splash" then + if not (G.ACHIEVEMENTS and G.ACHIEVEMENTS['ach_cry_used_crash'] and G.ACHIEVEMENTS['ach_cry_used_crash'].earned) then check_for_unlock({type = 'ach_cry_used_crash'}) end + if not (G.ACHIEVEMENTS and G.ACHIEVEMENTS['ach_cry_traffic_jam'] and G.ACHIEVEMENTS['ach_cry_traffic_jam'].earned) then check_for_unlock({type = 'win_challenge_startup'}) end + if not (G.ACHIEVEMENTS and G.ACHIEVEMENTS['ach_cry_perfectly_balanced'] and G.ACHIEVEMENTS['ach_cry_perfectly_balanced'].earned) then check_for_unlock({type = 'win_stake_startup'}) end + end + + G.E_MANAGER:add_event(Event({ + blockable = false, + func = function() + set_discover_tallies() + for i = 1, #G.CHALLENGES do + if (G.CHALLENGES[i].id == 'c_cry_rush_hour' or G.CHALLENGES[i].id == 'c_cry_rush_hour_ii' or G.CHALLENGES[i].id == 'c_cry_rush_hour_iii') and #G.CHALLENGES[i].restrictions.banned_other == 0 then + for k, v in pairs(G.P_BLINDS) do + if k ~= "bl_cry_clock" and k ~= "bl_cry_lavender_loop" and v.boss then + G.CHALLENGES[i].restrictions.banned_other[#G.CHALLENGES[i].restrictions.banned_other+1] = {id = k, type = 'blind'} + end + end + end + end + set_profile_progress() + G.REFRESH_ALERTS = true + return true + end + })) + + --VERSION + UIBox{ + definition = + {n=G.UIT.ROOT, config={align = "cm", colour = G.C.UI.TRANSPARENT_DARK}, nodes={ + {n=G.UIT.T, config={text = G.VERSION, scale = 0.3, colour = G.C.UI.TEXT_LIGHT}} + }}, + config = {align="tri", offset = {x=0,y=0}, major = G.ROOM_ATTACH, bond = 'Weak'} + } +end + +function Game:demo_cta() --True if main menu is accessed from the splash screen, false if it is skipped or accessed from the game + --G.TIMERS.REAL = 12 + --G.TIMERS.TOTAL = 12 + --Prepare the main menu, reset the default deck + self:prep_stage(G.STAGES.MAIN_MENU, G.STATES.DEMO_CTA, true) + + self.GAME.selected_back = Back(G.P_CENTERS.b_red) + + G.FUNCS.change_shadows{to_key = G.SETTINGS.GRAPHICS.shadows == 'On' and 1 or 2} + + ease_background_colour{new_colour = G.C.BLACK, contrast = 1} + + if G.SPLASH_FRONT then G.SPLASH_FRONT:remove(); G.SPLASH_FRONT = nil end + if G.SPLASH_BACK then G.SPLASH_BACK:remove(); G.SPLASH_BACK = nil end + G.SPLASH_BACK = Sprite(-30, -13, G.ROOM.T.w+60, G.ROOM.T.h+22, G.ASSET_ATLAS["ui_1"], {x = 2, y = 0}) + G.SPLASH_BACK:set_alignment({ + major = G.ROOM_ATTACH, + type = 'cm', + offset = {x=0,y=0} + }) + local splash_args = {mid_flash = 1.6} + ease_value(splash_args, 'mid_flash', -1.6, nil, nil, nil, 4) + + G.SPLASH_BACK:define_draw_steps({{ + shader = 'splash', + send = { + {name = 'time', ref_table = G.TIMERS, ref_value = 'REAL_SHADER'}, + {name = 'vort_speed', val = 0.4}, + {name = 'colour_1', ref_table = G.C, ref_value = 'RED'}, + {name = 'colour_2', ref_table = G.C, ref_value = 'BLUE'}, + {name = 'mid_flash', ref_table = splash_args, ref_value = 'mid_flash'}, + {name = 'vort_offset', val = 0}, + }}}) + + local SC_scale = 0.9*(G.debug_splash_size_toggle and 0.8 or 1) + + local CAI = { + TITLE_TOP_W = G.CARD_W, + TITLE_TOP_H = G.CARD_H, + } + self.title_top = CardArea( + 0, 0, + CAI.TITLE_TOP_W,CAI.TITLE_TOP_H, + {card_limit = 1, type = 'title'}) + + + G.SPLASH_LOGO = Sprite(0, 0, + 13*SC_scale, + 13*SC_scale*(G.ASSET_ATLAS["balatro"].py/G.ASSET_ATLAS["balatro"].px), + G.ASSET_ATLAS["balatro"], {x=0,y=0}) + + G.SPLASH_LOGO:set_alignment({ + major = G.title_top, + type = 'cm', + bond = 'Strong', + offset = {x=0,y=0} + }) + G.SPLASH_LOGO:define_draw_steps({{ + shader = 'dissolve', + }}) + + G.SPLASH_LOGO.dissolve_colours = {G.C.WHITE, G.C.WHITE} + G.SPLASH_LOGO.dissolve = 1 + + local replace_card = Card(self.title_top.T.x, self.title_top.T.y, 1.2*G.CARD_W*SC_scale, 1.2*G.CARD_H*SC_scale, G.P_CARDS.S_A, G.P_CENTERS.c_base) + self.title_top:emplace(replace_card) + + replace_card:set_edition(G.P_CENTERS.e_cry_glitched and 'e_cry_glitched' or 'e_negative',true,true) + replace_card:set_seal('Gold', true, true) + replace_card.states.visible = false + replace_card.no_ui = true + replace_card.ambient_tilt = 0.0 + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 1.1, + blockable = false, + blocking = false, + func = (function() + replace_card.states.visible = true + replace_card:start_materialize({G.C.WHITE,G.C.WHITE}, true, 2.5) + play_sound('whoosh1', math.random()*0.1 + 0.3,0.3) + play_sound('crumple'..math.random(1,5), math.random()*0.2 + 0.6,0.65) + return true + end)})) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 1.8, + blockable = false, + blocking = false, + func = (function() + play_sound('magic_crumple'..(2), 1, 0.9) + play_sound('whoosh1', 0.4, 0.8) + ease_value(G.SPLASH_LOGO, 'dissolve', -1, nil, nil, nil, 2.3) + return true + end)})) + + delay(0.1 + 2) + + G.E_MANAGER:add_event(Event({func = function() G.CONTROLLER.lock_input = false; return true end})) + set_screen_positions() + + self.title_top:sort('order') + self.title_top:set_ranks() + self.title_top:align_cards() + self.title_top:hard_set_cards() + + local playstack = Sprite(0,0,1.7,1.7,G.ASSET_ATLAS["playstack_logo"], {x=0, y=0}) + playstack.states.drag.can = false + local localthunk = Sprite(0,0,1*1390/560,1,G.ASSET_ATLAS["localthunk_logo"], {x=0, y=0}) + localthunk.states.drag.can = false + + self.MAIN_MENU_UI = UIBox{ + definition = {n=G.UIT.ROOT, config = {align = "cm",colour = G.C.CLEAR}, nodes={ + {n=G.UIT.R, config={align = "cm", padding = 0.3}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = {'Sign up for the next demo!'}, colours = {G.C.WHITE},shadow = true, rotate = true, float = true, bump = true, scale = 0.9, spacing = 1, pop_in = 4.5})}} + }}, + {n=G.UIT.R, config={align = "cm", padding = 0.3}, nodes={ + {n=G.UIT.C, config={align = "cl", minw = 5, minh = 1}, nodes={ + UIBox_button{button = 'go_to_menu', colour = G.C.ORANGE, minw = 2, minh = 1, label = {'BACK'}, scale = 0.4, col = true}, + }}, + UIBox_button{id = 'demo_cta_playbalatro', button = "go_to_playbalatro", colour = G.C.BLUE, minw = 7.65, minh = 1.95, label = {'PLAYBALATRO.COM'}, scale = 0.9, col = true}, + {n=G.UIT.C, config={align = "cr", minw = 5, minh = 1}, nodes={ + {n=G.UIT.O, config={object = localthunk}}, + {n=G.UIT.O, config={object = playstack}}, + }} + }} + }}, + config = {align="bmi", offset = {x=0,y=10}, major = G.ROOM_ATTACH, bond = 'Weak'} + } + self.MAIN_MENU_UI.states.visible = false + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 4.05, + blockable = false, + blocking = false, + func = (function() + self.MAIN_MENU_UI.states.visible = true + self.MAIN_MENU_UI.alignment.offset.y = 0 + self.MAIN_MENU_UI:align_to_major() + G.CONTROLLER:snap_to{node = self.MAIN_MENU_UI:get_UIE_by_ID('demo_cta_playbalatro')} + return true + end) + })) +end + +function Game:init_game_object() + local cards_played = {} + for _,v in ipairs(SMODS.Rank.obj_buffer) do + cards_played[v] = { suits = {}, total = 0 } + end + local bosses_used = {} + for k, v in pairs(G.P_BLINDS) do + if v.boss then bosses_used[k] = 0 end + end + return { + won = false, + round_scores = { + furthest_ante = {label = 'Ante', amt = 0}, + furthest_round = {label = 'Round', amt = 0}, + hand = {label = 'Best Hand', amt = 0}, + poker_hand = {label = 'Most Played Hand', amt = 0}, + new_collection = {label = 'New Discoveries', amt = 0}, + cards_played = {label = 'Cards Played', amt = 0}, + cards_discarded = {label = 'Cards Discarded', amt = 0}, + times_rerolled = {label = 'Times Rerolled', amt = 0}, + cards_purchased = {label = 'Cards Purchased', amt = 0}, + }, + joker_usage = {}, + consumeable_usage = {}, + hand_usage = {}, + last_tarot_planet = nil, + win_ante = 8, + stake = 1, + modifiers = {}, + starting_params = get_starting_params(), + banned_keys = {}, + round = 0, + probabilities = { + normal = 1, + }, + bosses_used = bosses_used, + pseudorandom = {}, + starting_deck_size = 52, + ecto_minus = 1, + pack_size = 2, + skips = 0, + STOP_USE = 0, + edition_rate = 1, + joker_rate = 20, + tarot_rate = 4, + planet_rate = 4, + spectral_rate = 0, + playing_card_rate = 0, + consumeable_buffer = 0, + joker_buffer = 0, + discount_percent = 0, + interest_cap = 25, + interest_amount = 1, + inflation = 0, + hands_played = 0, + unused_discards = 0, + perishable_rounds = 5, + rental_rate = 3, + cry_voucher_perishable_rounds = 8, + cry_voucher_rental_rate = 2, + cry_consumeable_rental_rate = 2, + cry_voucher_banana_odds = 12, + cry_consumeable_banana_odds = 4, + cry_pinned_consumeables = 0, + cry_shop_joker_price_modifier = 1, + blind = nil, + chips = to_big(0), + chips_text = '0', + voucher_text = '', + dollars = 0, + max_jokers = 0, + bankrupt_at = 0, + current_boss_streak = 0, + base_reroll_cost = 5, + blind_on_deck = nil, + sort = 'desc', + previous_round = { + dollars = 4 + }, + tags = {}, + tag_tally = 0, + pool_flags = {}, + used_jokers = {}, + used_vouchers = {}, + cry_owned_vouchers = {}, + current_round = { + current_hand = { + chips = to_big(0), + chip_text = '0', + mult = 0, + mult_text = '0', + chip_total = 0, + chip_total_text = '', + cry_asc_num = 0, + cry_asc_num_text = '', + handname = "", + hand_level = '' + }, + used_packs = {}, + cards_flipped = 0, + round_text = 'Round ', + idol_card = {suit = 'Spades', rank = 'Ace'}, + mail_card = {rank = 'Ace'}, + ancient_card = {suit = 'Spades'}, + castle_card = {suit = 'Spades'}, + hands_left = 0, + hands_played = 0, + discards_left = 0, + discards_used = 0, + dollars = 0, + reroll_cost = 5, + reroll_cost_increase = 0, + jokers_purchased = 0, + free_rerolls = 0, + round_dollars = 0, + dollars_to_be_earned = '!!!', + most_played_poker_hand = 'High Card', + }, + round_resets = { + hands = 1, + discards = 1, + reroll_cost = 1, + temp_reroll_cost = nil, + temp_handsize = nil, + ante = 1, + ante_disp = number_format(1), + blind_ante = 1, + blind_states = {Small = 'Select', Big = 'Upcoming', Boss = 'Upcoming'}, + loc_blind_states = {Small = '', Big = '', Boss = ''}, + blind_choices = {Small = 'bl_small', Big = 'bl_big'}, + boss_rerolled = false, + }, + round_bonus = { + next_hands = 0, + discards = 0, + }, + shop = { + joker_max = 2, + }, + cards_played = cards_played, + disabled_suits = {}, + disabled_ranks = {}, + hands = { + ["Flush Five"] = {visible = false, order = 1, mult = 16, chips = 160, s_mult = 16, s_chips = 160, level = 1, l_mult = 3, l_chips = 50, played = 0, played_this_round = 0, example = {{'S_A', true},{'S_A', true},{'S_A', true},{'S_A', true},{'S_A', true}}}, + ["Flush House"] = {visible = false, order = 2, mult = 14, chips = 140, s_mult = 14, s_chips = 140, level = 1, l_mult = 4, l_chips = 40, played = 0, played_this_round = 0, example = {{'D_7', true},{'D_7', true},{'D_7', true},{'D_4', true},{'D_4', true}}}, + ["Five of a Kind"] = {visible = false, order = 3, mult = 12, chips = 120, s_mult = 12, s_chips = 120, level = 1, l_mult = 3, l_chips = 35, played = 0, played_this_round = 0, example = {{'S_A', true},{'H_A', true},{'H_A', true},{'C_A', true},{'D_A', true}}}, + ["Straight Flush"] = {visible = true, order = 4, mult = 8, chips = 100, s_mult = 8, s_chips = 100, level = 1, l_mult = 4, l_chips = 40, played = 0, played_this_round = 0, example = {{'S_Q', true},{'S_J', true},{'S_T', true},{'S_9', true},{'S_8', true}}}, + ["Four of a Kind"] = {visible = true, order = 5, mult = 7, chips = 60, s_mult = 7, s_chips = 60, level = 1, l_mult = 3, l_chips = 30, played = 0, played_this_round = 0, example = {{'S_J', true},{'H_J', true},{'C_J', true},{'D_J', true},{'C_3', false}}}, + ["Full House"] = {visible = true, order = 6, mult = 4, chips = 40, s_mult = 4, s_chips = 40, level = 1, l_mult = 2, l_chips = 25, played = 0, played_this_round = 0, example = {{'H_K', true},{'C_K', true},{'D_K', true},{'S_2', true},{'D_2', true}}}, + ["Flush"] = {visible = true, order = 7, mult = 4, chips = 35, s_mult = 4, s_chips = 35, level = 1, l_mult = 2, l_chips = 15, played = 0, played_this_round = 0, example = {{'H_A', true},{'H_K', true},{'H_T', true},{'H_5', true},{'H_4', true}}}, + ["Straight"] = {visible = true, order = 8, mult = 4, chips = 30, s_mult = 4, s_chips = 30, level = 1, l_mult = 3, l_chips = 30, played = 0, played_this_round = 0, example = {{'D_J', true},{'C_T', true},{'C_9', true},{'S_8', true},{'H_7', true}}}, + ["Three of a Kind"] = {visible = true, order = 9, mult = 3, chips = 30, s_mult = 3, s_chips = 30, level = 1, l_mult = 2, l_chips = 20, played = 0, played_this_round = 0, example = {{'S_T', true},{'C_T', true},{'D_T', true},{'H_6', false},{'D_5', false}}}, + ["Two Pair"] = {visible = true, order = 10,mult = 2, chips = 20, s_mult = 2, s_chips = 20, level = 1, l_mult = 1, l_chips = 20, played = 0, played_this_round = 0, example = {{'H_A', true},{'D_A', true},{'C_Q', false},{'H_4', true},{'C_4', true}}}, + ["Pair"] = {visible = true, order = 11,mult = 2, chips = 10, s_mult = 2, s_chips = 10, level = 1, l_mult = 1, l_chips = 15, played = 0, played_this_round = 0, example = {{'S_K', false},{'S_9', true},{'D_9', true},{'H_6', false},{'D_3', false}}}, + ["High Card"] = {visible = true, order = 12,mult = 1, chips = 5, s_mult = 1, s_chips = 5, level = 1, l_mult = 1, l_chips = 10, played = 0, played_this_round = 0, example = {{'S_A', true},{'D_Q', false},{'D_9', false},{'C_4', false},{'D_3', false}}}, + } + } +end + +function Game:start_run(args) + args = args or {} + + local saveTable = args.savetext or nil + G.SAVED_GAME = nil + + self:prep_stage(G.STAGES.RUN, saveTable and saveTable.STATE or G.STATES.BLIND_SELECT) + + G.STAGE = G.STAGES.RUN + if saveTable then + check_for_unlock({type = 'continue_game'}) + end + + G.STATE_COMPLETE = false + G.RESET_BLIND_STATES = true + + if not saveTable then ease_background_colour_blind(G.STATE, 'Small Blind') + else ease_background_colour_blind(G.STATE, saveTable.BLIND.name:gsub("%s+", "") ~= '' and saveTable.BLIND.name or 'Small Blind') end + + local selected_back = saveTable and saveTable.BACK.name or (args.challenge and args.challenge.deck and args.challenge.deck.type) or (self.GAME.viewed_back and self.GAME.viewed_back.name) or self.GAME.selected_back and self.GAME.selected_back.name or 'Red Deck' + selected_back = get_deck_from_name(selected_back) + self.GAME = saveTable and saveTable.GAME or self:init_game_object() + if (not saveTable or not saveTable.GAME) and Talisman and Talisman.igo then self.GAME = Talisman.igo(self.GAME) end + Handy.UI.init() + self.GAME.modifiers = self.GAME.modifiers or {} + self.GAME.stake = args.stake or self.GAME.stake or 1 + self.GAME.STOP_USE = 0 + self.GAME.selected_back = Back(selected_back) + self.GAME.selected_back_key = selected_back + + G.C.UI_CHIPS[1], G.C.UI_CHIPS[2], G.C.UI_CHIPS[3], G.C.UI_CHIPS[4] = G.C.BLUE[1], G.C.BLUE[2], G.C.BLUE[3], G.C.BLUE[4] + G.C.UI_MULT[1], G.C.UI_MULT[2], G.C.UI_MULT[3], G.C.UI_MULT[4] = G.C.RED[1], G.C.RED[2], G.C.RED[3], G.C.RED[4] + + if not saveTable then + if false then + if self.GAME.stake >= 2 then + self.GAME.modifiers.no_blind_reward = self.GAME.modifiers.no_blind_reward or {} + self.GAME.modifiers.no_blind_reward.Small = true + end + if self.GAME.stake >= 3 then self.GAME.modifiers.scaling = 2 end + if self.GAME.stake >= 4 then self.GAME.modifiers.enable_eternals_in_shop = true end + if self.GAME.stake >= 5 then self.GAME.starting_params.discards = self.GAME.starting_params.discards - 1 end + if self.GAME.stake >= 6 then self.GAME.modifiers.scaling = 3 end + if self.GAME.stake >= 7 then self.GAME.modifiers.enable_perishables_in_shop = true end + if self.GAME.stake >= 8 then self.GAME.modifiers.enable_rentals_in_shop = true end + end SMODS.setup_stake(self.GAME.stake) + + self.GAME.selected_back:apply_to_run() + + if args.challenge then + self.GAME.challenge = args.challenge.id + self.GAME.challenge_tab = args.challenge + local _ch = args.challenge + if _ch.jokers then + for k, v in ipairs(_ch.jokers) do + G.E_MANAGER:add_event(Event({ + func = function() + local _joker = add_joker(v.id, v.edition, k ~= 1) + if v.eternal then _joker:set_eternal(true) end + if v.pinned then _joker.pinned = true end + return true + end + })) + end + end + if _ch.consumeables then + for k, v in ipairs(_ch.consumeables) do + G.E_MANAGER:add_event(Event({ + func = function() + add_joker(v.id, nil, k ~= 1) + return true + end + })) + end + end + if _ch.vouchers then + for k, v in ipairs(_ch.vouchers) do + G.GAME.used_vouchers[v.id] = true + G.GAME.cry_owned_vouchers[v.id] = true + G.E_MANAGER:add_event(Event({ + func = function() + G.GAME.starting_voucher_count = (G.GAME.starting_voucher_count or 0) + 1 + Card.apply_to_run(nil, G.P_CENTERS[v.id]) + return true + end + })) + end + end + if _ch.rules then + if _ch.rules.modifiers then + for k, v in ipairs(_ch.rules.modifiers) do + self.GAME.starting_params[v.id] = v.value + end + end + if _ch.rules.custom then + for k, v in ipairs(_ch.rules.custom) do + if v.id == 'no_reward' then + self.GAME.modifiers.no_blind_reward = self.GAME.modifiers.no_blind_reward or {} + self.GAME.modifiers.no_blind_reward.Small = true + self.GAME.modifiers.no_blind_reward.Big = true + self.GAME.modifiers.no_blind_reward.Boss = true + elseif v.id == 'no_reward_specific' then + self.GAME.modifiers.no_blind_reward = self.GAME.modifiers.no_blind_reward or {} + self.GAME.modifiers.no_blind_reward[v.value] = true + elseif v.value then + self.GAME.modifiers[v.id] = v.value + elseif v.id == 'no_shop_jokers' then + self.GAME.joker_rate = 0 + else + self.GAME.modifiers[v.id] = true + end + end + end + end + if _ch.restrictions then + if _ch.restrictions.banned_cards then + for k, v in ipairs(_ch.restrictions.banned_cards) do + G.GAME.banned_keys[v.id] = true + if v.ids then + for kk, vv in ipairs(v.ids) do + G.GAME.banned_keys[vv] = true + end + end + end + end + if _ch.restrictions.banned_tags then + for k, v in ipairs(_ch.restrictions.banned_tags) do + G.GAME.banned_keys[v.id] = true + end + end + if _ch.restrictions.banned_other then + for k, v in ipairs(_ch.restrictions.banned_other) do + G.GAME.banned_keys[v.id] = true + end + end + end + end + + self.GAME.round_resets.hands = self.GAME.starting_params.hands + self.GAME.round_resets.discards = self.GAME.starting_params.discards + self.GAME.round_resets.reroll_cost = self.GAME.starting_params.reroll_cost + self.GAME.dollars = self.GAME.starting_params.dollars + self.GAME.base_reroll_cost = self.GAME.starting_params.reroll_cost + self.GAME.round_resets.reroll_cost = self.GAME.base_reroll_cost + self.GAME.current_round.reroll_cost = self.GAME.base_reroll_cost + end + + G.GAME.chips_text = '' + G.GAME.cry_voucher_centers = {} + for k, v in pairs(G.P_CENTERS) do + if v.set == 'Voucher' then + G.GAME.cry_voucher_centers[k] = {config = {}} + G.GAME.cry_voucher_centers[k].config = copy_table(v.config) + end + end + + if not saveTable then + if args.seed then self.GAME.seeded = true end + self.GAME.pseudorandom.seed = args.seed or (not (G.SETTINGS.tutorial_complete or G.SETTINGS.tutorial_progress.completed_parts['big_blind']) and "TUTORIAL") or generate_starting_seed() + end + + for k, v in pairs(self.GAME.pseudorandom) do if v == 0 then self.GAME.pseudorandom[k] = pseudohash(k..self.GAME.pseudorandom.seed) end end + self.GAME.pseudorandom.hashed_seed = pseudohash(self.GAME.pseudorandom.seed) + if G.GAME.modifiers.cry_misprint_min and not args.savetext then + for k, v in pairs(G.GAME.hands) do + v.chips = to_big(cry_format(v.chips * cry_log_random(pseudoseed('cry_misprint'),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2g")) + v.mult = to_big(cry_format(v.mult * cry_log_random(pseudoseed('cry_misprint'),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2g")) + v.l_chips = cry_format(v.l_chips * cry_log_random(pseudoseed('cry_misprint'),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2g") + v.l_mult = cry_format(v.l_mult * cry_log_random(pseudoseed('cry_misprint'),G.GAME.modifiers.cry_misprint_min,G.GAME.modifiers.cry_misprint_max),"%.2g") + v.s_chips = v.chips + v.s_mult = v.mult + end + end + + G:save_settings() + + if not self.GAME.round_resets.blind_tags then + self.GAME.round_resets.blind_tags = {} + end + + if not saveTable then + if G.GAME.modifiers.cry_big_boss_rate and pseudorandom('cry_big_boss') < G.GAME.modifiers.cry_big_boss_rate then + self.GAME.round_resets.blind_choices.Big = get_new_boss() + elseif G.GAME.modifiers.cry_rush_hour_ii then + self.GAME.round_resets.blind_choices.Small = get_new_boss() + self.GAME.round_resets.blind_choices.Big = get_new_boss() + else + self.GAME.round_resets.blind_choices.Big = 'bl_big' + end + self.GAME.round_resets.blind_choices.Boss = get_new_boss() + if not self.GAME.modifiers.cry_no_vouchers then + if not G.GAME.modifiers.cry_voucher_restock_antes or G.GAME.round_resets.ante % G.GAME.modifiers.cry_voucher_restock_antes == 0 then + self.GAME.current_round.voucher = G.SETTINGS.tutorial_progress and G.SETTINGS.tutorial_progress.forced_voucher or get_next_voucher_key() + end + else + very_fair_quip = pseudorandom_element(G.localization.misc.very_fair_quips, pseudoseed("cry_very_fair")) + end + self.GAME.current_round.cry_voucher_edition = cry_get_next_voucher_edition() + self.GAME.current_round.cry_voucher_stickers = cry_get_next_voucher_stickers() + self.GAME.round_resets.blind_tags.Small = G.SETTINGS.tutorial_progress and G.SETTINGS.tutorial_progress.forced_tags and G.SETTINGS.tutorial_progress.forced_tags[1] or get_next_tag_key() + self.GAME.round_resets.blind_tags.Big = G.SETTINGS.tutorial_progress and G.SETTINGS.tutorial_progress.forced_tags and G.SETTINGS.tutorial_progress.forced_tags[2] or get_next_tag_key() + else + if self.GAME.round_resets.blind and self.GAME.round_resets.blind.key then + self.GAME.round_resets.blind = G.P_BLINDS[self.GAME.round_resets.blind.key] + end + end + G.CONTROLLER.locks.load = true + G.E_MANAGER:add_event(Event({ + no_delete = true, + trigger = 'after', + blocking = false,blockable = false, + delay = 3.5, + timer = 'TOTAL', + func = function() + G.CONTROLLER.locks.load = nil + return true + end + })) + + if saveTable and saveTable.ACTION then + G.E_MANAGER:add_event(Event({delay = 0.5, trigger = 'after', blocking = false,blockable = false,func = (function() + G.E_MANAGER:add_event(Event({func = (function() + G.E_MANAGER:add_event(Event({func = (function() + for k, v in pairs(G.I.CARD) do + if v.sort_id == saveTable.ACTION.card then + G.FUNCS.use_card({config = {ref_table = v}}, nil, true) + end + end + return true + end) + })) + return true + end) + })) + return true + end) + })) + end + + local CAI = { + discard_W = G.CARD_W, + discard_H = G.CARD_H, + deck_W = G.CARD_W*1.1, + deck_H = 0.95*G.CARD_H, + hand_W = 6*G.CARD_W, + hand_H = 0.95*G.CARD_H, + play_W = 5.3*G.CARD_W, + play_H = 0.95*G.CARD_H, + joker_W = 4.9*G.CARD_W, + joker_H = 0.95*G.CARD_H, + consumeable_W = 2.3*G.CARD_W, + consumeable_H = 0.95*G.CARD_H + } + + + if not G.GAME.modifiers.cry_beta then + self.consumeables = CardArea( + 0, 0, + CAI.consumeable_W, + CAI.consumeable_H, + {card_limit = self.GAME.starting_params.consumable_slots, type = 'joker', highlight_limit = 1e100}) + + self.jokers = CardArea( + 0, 0, + CAI.joker_W, + CAI.joker_H, + {card_limit = self.GAME.starting_params.joker_slots, type = 'joker', highlight_limit = 1e100}) + + else + self.jokers = CardArea( + 0, 0, + CAI.joker_W+CAI.consumeable_W, + CAI.joker_H, + {card_limit = self.GAME.starting_params.joker_slots+self.GAME.starting_params.consumable_slots-1, type = 'joker', highlight_limit = 1e100}) + self.consumeables = self.jokers + end + self.discard = CardArea( + 0, 0, + CAI.discard_W,CAI.discard_H, + {card_limit = 1e308, type = 'discard'}) + self.deck = CardArea( + 0, 0, + CAI.deck_W,CAI.deck_H, + {card_limit = 52, type = 'deck'}) + self.hand = CardArea( + 0, 0, + CAI.hand_W,CAI.hand_H, + {card_limit = self.GAME.starting_params.hand_size, type = 'hand'}) + self.play = CardArea( + 0, 0, + CAI.play_W,CAI.play_H, + {card_limit = 5, type = 'play'}) + + G.playing_cards = {} + + set_screen_positions() + + G.SPLASH_BACK = Sprite(-30, -6, G.ROOM.T.w+60, G.ROOM.T.h+12, G.ASSET_ATLAS["ui_1"], {x = 2, y = 0}) + G.SPLASH_BACK:set_alignment({ + major = G.play, + type = 'cm', + bond = 'Strong', + offset = {x=0,y=0} + }) + + G.ARGS.spin = { + amount = 0, + real = 0, + eased = 0 + } + + G.SPLASH_BACK:define_draw_steps({{ + shader = 'background', + send = { + {name = 'time', ref_table = G.TIMERS, ref_value = 'REAL_SHADER'}, + {name = 'spin_time', ref_table = G.TIMERS, ref_value = 'BACKGROUND'}, + {name = 'colour_1', ref_table = G.C.BACKGROUND, ref_value = 'C'}, + {name = 'colour_2', ref_table = G.C.BACKGROUND, ref_value = 'L'}, + {name = 'colour_3', ref_table = G.C.BACKGROUND, ref_value = 'D'}, + {name = 'contrast', ref_table = G.C.BACKGROUND, ref_value = 'contrast'}, + {name = 'spin_amount', ref_table = G.ARGS.spin, ref_value = 'amount'} + }}}) + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + blocking = false, + blockable = false, + func = (function() + local _dt = G.ARGS.spin.amount > G.ARGS.spin.eased and G.real_dt*2. or 0.3*G.real_dt + local delta = G.ARGS.spin.real - G.ARGS.spin.eased + if math.abs(delta) > _dt then delta = delta*_dt/math.abs(delta) end + G.ARGS.spin.eased = G.ARGS.spin.eased + delta + G.ARGS.spin.amount = _dt*(G.ARGS.spin.eased) + (1 - _dt)*G.ARGS.spin.amount + G.TIMERS.BACKGROUND = G.TIMERS.BACKGROUND - 60*(G.ARGS.spin.eased - G.ARGS.spin.amount)*_dt + end) + })) + + if saveTable then + local cardAreas = saveTable.cardAreas + for k, v in pairs(cardAreas) do + if G[k] then G[k]:load(v) + else + G['load_'..k] = v + print("ERROR LOADING GAME: Card area '"..k.."' not instantiated before load") end + end + + for k, v in pairs(G.I.CARD) do + if v.playing_card then + table.insert(G.playing_cards, v) + end + end + for k, v in pairs(G.I.CARDAREA) do + v:align_cards() + v:hard_set_cards() + end + table.sort(G.playing_cards, function (a, b) return a.playing_card > b.playing_card end ) + else + local card_protos = nil + local _de = nil + if args.challenge and args.challenge.deck then + _de = args.challenge.deck + end + + if _de and _de.cards then + card_protos = _de.cards + end + + if not card_protos then + card_protos = {} + for k, v in pairs(self.P_CARDS) do + if type(SMODS.Ranks[v.value].in_pool) == 'function' and not SMODS.Ranks[v.value]:in_pool({initial_deck = true}) + or type(SMODS.Suits[v.suit].in_pool) == 'function' and not SMODS.Suits[v.suit]:in_pool({initial_deck = true}) then + goto continue + end + local _ = nil + if self.GAME.starting_params.erratic_suits_and_ranks then + v, k = pseudorandom_element(G.P_CARDS, pseudoseed('erratic'), {starting_deck = true}) + end + local _r, _s = SMODS.Ranks[v.value].card_key, SMODS.Suits[v.suit].card_key + local keep, _e, _d, _g = true, nil, nil, nil + if _de then + if _de.yes_ranks and not _de.yes_ranks[_r] then keep = false end + if _de.no_ranks and _de.no_ranks[_r] then keep = false end + if _de.yes_suits and not _de.yes_suits[_s] then keep = false end + if _de.no_suits and _de.no_suits[_s] then keep = false end + if _de.enhancement then _e = _de.enhancement end + if _de.edition then _d = _de.edition end + if _de.gold_seal then _g = _de.gold_seal end + end + + if self.GAME.starting_params.no_faces and SMODS.Ranks[v.value].face then keep = false end + + if keep then card_protos[#card_protos+1] = {s=_s,r=_r,e=_e,d=_d,g=_g} end + ::continue:: + end + end + + if self.GAME.starting_params.extra_cards then + for k, v in pairs(self.GAME.starting_params.extra_cards) do + card_protos[#card_protos+1] = v + end + end + + table.sort(card_protos, function (a, b) return + ((a.s or '')..(a.r or '')..(a.e or '')..(a.d or '')..(a.g or '')) < + ((b.s or '')..(b.r or '')..(b.e or '')..(b.d or '')..(b.g or '')) end) + + for k, v in ipairs(card_protos) do + card_from_control(v) + end + + if G.GAME.modifiers.cry_ccd then + for k, v in pairs(G.playing_cards) do + v:set_ability(get_random_consumable('cry_ccd',{"no_doe", "no_grc"}, nil, nil, true), true, nil) + end + end + self.GAME.starting_deck_size = #G.playing_cards + end + + delay(0.5) + + if not saveTable then + G.GAME.current_round.discards_left = G.GAME.round_resets.discards + G.GAME.current_round.hands_left = G.GAME.round_resets.hands + self.deck:shuffle() + self.deck:hard_set_T() + reset_idol_card() + reset_mail_rank() + self.GAME.current_round.ancient_card.suit = nil + reset_ancient_card() + reset_castle_card() for _, mod in ipairs(SMODS.mod_list) do + if mod.reset_game_globals and type(mod.reset_game_globals) == 'function' then + mod.reset_game_globals(true) + end + end + end + + G.GAME.blind = Blind(0,0,2, 1) + self.deck:align_cards() + self.deck:hard_set_cards() + + self.HUD = UIBox{ + definition = create_UIBox_HUD(), + config = {align=('cli'), offset = {x=-0.7,y=0},major = G.ROOM_ATTACH} + } + self.HUD_blind = UIBox{ + definition = create_UIBox_HUD_blind(), + config = {major = G.HUD:get_UIE_by_ID('row_blind_bottom'), align = 'bmi', offset = {x=0,y=-10}, bond = 'Weak'} + } + self.HUD_tags = {} + + G.hand_text_area = { + chips = self.HUD:get_UIE_by_ID('hand_chips'), + mult = self.HUD:get_UIE_by_ID('hand_mult'), + ante = self.HUD:get_UIE_by_ID('ante_UI_count'), + round = self.HUD:get_UIE_by_ID('round_UI_count'), + chip_total = self.HUD:get_UIE_by_ID('hand_chip_total'), + handname = self.HUD:get_UIE_by_ID('hand_name'), + hand_level = self.HUD:get_UIE_by_ID('hand_level'), + game_chips = self.HUD:get_UIE_by_ID('chip_UI_count'), + blind_chips = self.HUD_blind:get_UIE_by_ID('HUD_blind_count'), + blind_spacer = self.HUD:get_UIE_by_ID('blind_spacer') + } + + check_and_set_high_score('most_money', G.GAME.dollars) + + if saveTable then + G.GAME.blind:load(saveTable.BLIND) + G.GAME.tags = {} + local tags = saveTable.tags or {} + for k, v in ipairs(tags) do + local _tag = Tag('tag_uncommon') + _tag:load(v) + add_tag(_tag, nil, true) + end + else + G.GAME.blind:set_blind(nil, nil, true) + reset_blinds() + end + + Cartomancer.update_tags_visibility() + G.FUNCS.blind_chip_UI_scale(G.hand_text_area.blind_chips) + + self.HUD:recalculate() + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = (function() + unlock_notify() + return true + end) + })) + +end + +function Game:update(dt) + nuGC(nil, nil, true) + + G.MAJORS = 0 + G.MINORS = 0 + + G.FRAMES.MOVE = G.FRAMES.MOVE + 1 + timer_checkpoint('start->discovery', 'update') + if not G.SETTINGS.tutorial_complete then G.FUNCS.tutorial_controller() end + timer_checkpoint('tallies', 'update') + modulate_sound(dt) + timer_checkpoint('sounds', 'update') + update_canvas_juice(dt) + timer_checkpoint('canvas and juice', 'update') + --Smooth out the dts to avoid any big jumps + self.TIMERS.REAL = self.TIMERS.REAL + dt + self.TIMERS.REAL_SHADER = G.SETTINGS.reduced_motion and 300 or self.TIMERS.REAL + self.TIMERS.UPTIME = self.TIMERS.UPTIME + dt + self.SETTINGS.DEMO.total_uptime = (self.SETTINGS.DEMO.total_uptime or 0) + dt + self.TIMERS.BACKGROUND = self.TIMERS.BACKGROUND + dt*(G.ARGS.spin and G.ARGS.spin.amount or 0) + self.real_dt = dt + + if require('debugplus.config').getValue('enableLongDT') and self.real_dt > 0.05 then print('LONG DT @ '..math.floor(G.TIMERS.REAL)..': '..self.real_dt) end + if not G.fbf or G.new_frame then + G.new_frame = false + + set_alerts() + timer_checkpoint('alerts', 'update') + + local http_resp = G.HTTP_MANAGER.in_channel:pop() + if http_resp then + G.ARGS.HIGH_SCORE_RESPONSE = http_resp + end + + + if G.SETTINGS.paused then dt = 0 end + + if G.STATE ~= G.ACC_state then G.ACC = 0 end + G.ACC_state = G.STATE + + if (G.STATE == G.STATES.HAND_PLAYED) or (G.STATE == G.STATES.NEW_ROUND) or Incantation and Incantation.accelerate then + G.ACC = math.min((G.ACC or 0) + dt*0.2*self.SETTINGS.GAMESPEED, 16) + elseif Handy.insta_cash_out.is_skipped then G.ACC = 999 + else + G.ACC = 0 + end + + self.SPEEDFACTOR = (G.STAGE == G.STAGES.RUN and not G.SETTINGS.paused and not G.screenwipe) and self.SETTINGS.GAMESPEED or 1 + self.SPEEDFACTOR = self.SPEEDFACTOR + math.max(0, math.abs(G.ACC) - 2) + self.SPEEDFACTOR = self.SPEEDFACTOR * Handy.speed_multiplier.value or 1 + + self.TIMERS.TOTAL = self.TIMERS.TOTAL + dt*(self.SPEEDFACTOR) + + self.C.DARK_EDITION[1] = 0.6+0.2*math.sin(self.TIMERS.REAL*1.3) + self.C.DARK_EDITION[3] = 0.6+0.2*(1- math.sin(self.TIMERS.REAL*1.3)) + self.C.DARK_EDITION[2] = math.min(self.C.DARK_EDITION[3], self.C.DARK_EDITION[1]) + + self.C.EDITION[1] = 0.7+0.2*(1+math.sin(self.TIMERS.REAL*1.5 + 0)) + self.C.EDITION[3] = 0.7+0.2*(1+math.sin(self.TIMERS.REAL*1.5 + 3)) + self.C.EDITION[2] = 0.7+0.2*(1+math.sin(self.TIMERS.REAL*1.5 + 6)) + for k, v in pairs(SMODS.Rarities) do + if v.gradient and type(v.gradient) == "function" then v:gradient(dt) end + end + + + self.E_MANAGER:update(self.real_dt) + timer_checkpoint('e_manager', 'update') + + if G.GAME.blind and G.boss_throw_hand and self.STATE == self.STATES.SELECTING_HAND then + if not self.boss_warning_text then + self.boss_warning_text = UIBox{ + definition = + {n=G.UIT.ROOT, config = {align = 'cm', colour = G.C.CLEAR, padding = 0.2}, nodes={ + {n=G.UIT.R, config = {align = 'cm', maxw = 1}, nodes={ + {n=G.UIT.O, config={object = DynaText({scale = 0.7, string = localize('ph_unscored_hand'), maxw = 9, colours = {G.C.WHITE},float = true, shadow = true, silent = true, pop_in = 0, pop_in_rate = 6})}}, + }}, + {n=G.UIT.R, config = {align = 'cm', maxw = 1}, nodes={ + {n=G.UIT.O, config={object = DynaText({scale = 0.6, string = G.GAME.blind:get_loc_debuff_text(), maxw = 9, colours = {G.C.WHITE},float = true, shadow = true, silent = true, pop_in = 0, pop_in_rate = 6})}}, + }} + }}, + config = { + align = 'cm', + offset ={x=0,y=-3.1}, + major = G.play, + } + } + self.boss_warning_text.attention_text = true + self.boss_warning_text.states.collide.can = false + G.GAME.blind.children.animatedSprite:juice_up(0.05, 0.02) + play_sound('chips1', math.random()*0.1 + 0.55, 0.12) + end + else + G.boss_throw_hand = nil + if self.boss_warning_text then + self.boss_warning_text:remove() + self.boss_warning_text = nil + end + end + + + if G.GAME.USING_RUN then self.STATE = self.STATES.SHOP end + if self.STATE == self.STATES.SELECTING_HAND then + if (not G.hand.cards[1]) and G.deck.cards[1] then + G.STATE = G.STATES.DRAW_TO_HAND + G.STATE_COMPLETE = false + else + self:update_selecting_hand(dt) + end + end + + if self.STATE == self.STATES.SHOP then + self:update_shop(dt) + end + + if self.STATE == self.STATES.PLAY_TAROT then + self:update_play_tarot(dt) + end + + if self.STATE == self.STATES.HAND_PLAYED then + self:update_hand_played(dt) + end + + if self.STATE == self.STATES.DRAW_TO_HAND then + self:update_draw_to_hand(dt) + end + + if self.STATE == self.STATES.NEW_ROUND then + self:update_new_round(dt) + end + + if self.STATE == self.STATES.BLIND_SELECT then + self:update_blind_select(dt) + end + + if self.STATE == self.STATES.ROUND_EVAL then + self:update_round_eval(dt) + end + + if G.STATE == G.STATES.SMODS_BOOSTER_OPENED then + SMODS.OPENED_BOOSTER.config.center:update_pack(dt) + end + + if self.STATE == self.STATES.TAROT_PACK then + self:update_arcana_pack(dt) + end + + if self.STATE == self.STATES.SPECTRAL_PACK then + self:update_spectral_pack(dt) + end + + if self.STATE == self.STATES.STANDARD_PACK then + self:update_standard_pack(dt) + end + + if self.STATE == self.STATES.BUFFOON_PACK then + self:update_buffoon_pack(dt) + end + + if self.STATE == self.STATES.PLANET_PACK then + self:update_celestial_pack(dt) + end + + if self.STATE == self.STATES.GAME_OVER then + self:update_game_over(dt) + end + + if self.STATE == self.STATES.MENU then + self:update_menu(dt) + end + timer_checkpoint('states', 'update') + --animate all animated objects + remove_nils(self.ANIMATIONS) + + for k, v in pairs(self.ANIMATIONS) do + v:animate(self.real_dt*self.SPEEDFACTOR) + end + timer_checkpoint('animate', 'update') + + --move and update all other moveables + G.exp_times.xy = math.exp(-50*self.real_dt) + G.exp_times.scale = math.exp(-60*self.real_dt) + G.exp_times.r = math.exp(-190*self.real_dt) + + local move_dt = math.min(1/20, self.real_dt) + + G.exp_times.max_vel = 70*move_dt + + for k, v in pairs(self.MOVEABLES) do + if v.FRAME.MOVE < G.FRAMES.MOVE then v:move(move_dt) end + end + timer_checkpoint('move', 'update') + + for k, v in pairs(self.MOVEABLES) do + v:update(dt*self.SPEEDFACTOR) + v.states.collide.is = false + end + timer_checkpoint('update', 'update') + end + + self.CONTROLLER:update(self.real_dt) + + --update loc strings if needed + if G.prev_small_state ~= G.GAME.round_resets.blind_states.Small or + G.prev_large_state ~= G.GAME.round_resets.blind_states.Big or + G.prev_boss_state ~= G.GAME.round_resets.blind_states.Boss or G.RESET_BLIND_STATES then + G.RESET_BLIND_STATES = nil + G.prev_small_state = G.GAME.round_resets.blind_states.Small + G.prev_large_state = G.GAME.round_resets.blind_states.Big + G.prev_boss_state = G.GAME.round_resets.blind_states.Boss + G.GAME.round_resets.loc_blind_states.Small = localize(G.GAME.round_resets.blind_states.Small,'blind_states') + G.GAME.round_resets.loc_blind_states.Big = localize(G.GAME.round_resets.blind_states.Big,'blind_states') + G.GAME.round_resets.loc_blind_states.Boss = localize(G.GAME.round_resets.blind_states.Boss,'blind_states') + end + + --Send all steam updates if needed + if G.STEAM and G.STEAM.send_control.update_queued and ( + G.STEAM.send_control.force or + G.STEAM.send_control.last_sent_stage ~= G.STAGE or + G.STEAM.send_control.last_sent_time < G.TIMERS.UPTIME - 120) then + if G.STEAM.userStats.storeStats() then + G.STEAM.send_control.force = false + G.STEAM.send_control.last_sent_stage = G.STAGE + G.STEAM.send_control.last_sent_time = G.TIMERS.UPTIME + G.STEAM.send_control.update_queued = false + else + G.DEBUG_VALUE = 'UNABLE TO STORE STEAM STATS' + end + end + + + if G.DEBUG then + local text_count,uie_count, card_count, uib_count, all = 0,0, 0, 0,0 + for k, v in pairs(G.STAGE_OBJECTS[G.STAGE]) do + all = all + 1 + if v:is(DynaText) then text_count = text_count + 1 end + if v:is(Card) then card_count = card_count + 1 end + if v:is(UIElement) then uie_count = uie_count + 1 end + if v:is(UIBox) then uib_count = uib_count + 1 end + end + + G.DEBUG_VALUE = 'text: '..text_count..'\n'.. + 'uie: '..uie_count..'\n'.. + 'card: '..card_count..'\n'.. + 'uib: '..uib_count..'\n'..'all: '..all + end + + --Save every 10 seconds, unless forced or paused/unpaused + if not GLOBAL_cry_member_count_delay then GLOBAL_cry_member_count_delay = 0 end + if (GLOBAL_cry_member_count_delay > 5) or not GLOBAL_cry_member_count then -- it doesn't need to update this frequently? but it also doesn't need to be higher tbh... + if update_cry_member_count then update_cry_member_count() end -- i honestly hate nil checks like this, wish there was a shorthand + GLOBAL_cry_member_count_delay = 0 + else + GLOBAL_cry_member_count_delay = GLOBAL_cry_member_count_delay + dt + end + if G.FILE_HANDLER and G.FILE_HANDLER and G.FILE_HANDLER.update_queued and ( + G.FILE_HANDLER.force or + G.FILE_HANDLER.last_sent_stage ~= G.STAGE or + ((G.FILE_HANDLER.last_sent_pause ~= G.SETTINGS.paused) and G.FILE_HANDLER.run) or + (not G.FILE_HANDLER.last_sent_time or (G.FILE_HANDLER.last_sent_time < (G.TIMERS.UPTIME - G.F_SAVE_TIMER)))) then + + if G.FILE_HANDLER.metrics then + G.SAVE_MANAGER.channel:push({ + type = 'save_metrics', + save_metrics = G.ARGS.save_metrics + }) + end + + if G.FILE_HANDLER.progress then + G.SAVE_MANAGER.channel:push({ + type = 'save_progress', + save_progress = G.ARGS.save_progress + }) + elseif G.FILE_HANDLER.settings then + G.SAVE_MANAGER.channel:push({ + type = 'save_settings', + save_settings = G.ARGS.save_settings, + profile_num = G.SETTINGS.profile, + save_profile = G.PROFILES[G.SETTINGS.profile] + }) + end + + if G.FILE_HANDLER.run then + G.SAVE_MANAGER.channel:push({ + type = 'save_run', + save_table = G.ARGS.save_run, + profile_num = G.SETTINGS.profile}) + G.SAVED_GAME = nil + end + + G.FILE_HANDLER.force = false + G.FILE_HANDLER.last_sent_stage = G.STAGE + G.FILE_HANDLER.last_sent_time = G.TIMERS.UPTIME + G.FILE_HANDLER.last_sent_pause = G.SETTINGS.paused + G.FILE_HANDLER.settings = nil + G.FILE_HANDLER.progress = nil + G.FILE_HANDLER.metrics = nil + G.FILE_HANDLER.run = nil + end +end + +function Game:draw() + G.FRAMES.DRAW = G.FRAMES.DRAW + 1 + --draw the room + reset_drawhash() + if G.OVERLAY_TUTORIAL and not G.OVERLAY_MENU then G.under_overlay = true end + timer_checkpoint('start->canvas', 'draw') + love.graphics.setCanvas{self.CANVAS} + love.graphics.push() + love.graphics.scale(G.CANV_SCALE) + + love.graphics.setShader() + love.graphics.clear(0,0,0,1) + + if G.SPLASH_BACK then + if G.debug_background_toggle then + love.graphics.clear({0,1,0,1}) + else + love.graphics.push() + G.SPLASH_BACK:translate_container() + G.SPLASH_BACK:draw() + love.graphics.pop() + end + end + + if not G.debug_UI_toggle then + + for k, v in pairs(self.I.NODE) do + if not v.parent then + love.graphics.push() + v:translate_container() + v:draw() + love.graphics.pop() + end + end + + for k, v in pairs(self.I.MOVEABLE) do + if not v.parent then + love.graphics.push() + v:translate_container() + v:draw() + love.graphics.pop() + end + end + + if G.SPLASH_LOGO then + love.graphics.push() + G.SPLASH_LOGO:translate_container() + G.SPLASH_LOGO:draw() + love.graphics.pop() + end + + if G.debug_splash_size_toggle then + for k, v in pairs(self.I.CARDAREA) do + if not v.parent then + love.graphics.push() + v:translate_container() + v:draw() + love.graphics.pop() + end + end + else + if (not self.OVERLAY_MENU) or (not self.F_HIDE_BG) then + timer_checkpoint('primatives', 'draw') + for k, v in pairs(self.I.UIBOX) do + if not v.attention_text and not v.parent and v ~= self.OVERLAY_MENU and v ~= self.screenwipe and v ~= self.OVERLAY_TUTORIAL and v ~= self.debug_tools and v ~= self.online_leaderboard and v ~= self.achievement_notification then + love.graphics.push() + v:translate_container() + v:draw() + love.graphics.pop() + end + end + timer_checkpoint('uiboxes', 'draw') + for k, v in pairs(self.I.CARDAREA) do + if not v.parent then + love.graphics.push() + v:translate_container() + v:draw() + love.graphics.pop() + end + end + + for k, v in pairs(self.I.CARD) do + if (not v.parent and v ~= self.CONTROLLER.dragging.target and v ~= self.CONTROLLER.focused.target) then + love.graphics.push() + v:translate_container() + v:draw() + love.graphics.pop() + end + end + + for k, v in pairs(self.I.UIBOX) do + if v.attention_text and v ~= self.debug_tools and v ~= self.online_leaderboard and v ~= self.achievement_notification then + love.graphics.push() + v:translate_container() + v:draw() + love.graphics.pop() + end + end + + if G.SPLASH_FRONT then + love.graphics.push() + G.SPLASH_FRONT:translate_container() + G.SPLASH_FRONT:draw() + love.graphics.pop() + end + + G.under_overlay = false + if self.OVERLAY_TUTORIAL then + love.graphics.push() + self.OVERLAY_TUTORIAL:translate_container() + self.OVERLAY_TUTORIAL:draw() + love.graphics.pop() + + if self.OVERLAY_TUTORIAL.highlights then + for k, v in ipairs(self.OVERLAY_TUTORIAL.highlights) do + love.graphics.push() + v:translate_container() + v:draw() + if v.draw_children then + v:draw_self() + v:draw_children() + end + love.graphics.pop() + end + end + end + end + if (self.OVERLAY_MENU) or (not self.F_HIDE_BG) then + if self.OVERLAY_MENU and self.OVERLAY_MENU ~= self.CONTROLLER.dragging.target then + love.graphics.push() + self.OVERLAY_MENU:translate_container() + self.OVERLAY_MENU:draw() + love.graphics.pop() + end + end + + if self.debug_tools then + if self.debug_tools ~= self.CONTROLLER.dragging.target then + love.graphics.push() + self.debug_tools:translate_container() + self.debug_tools:draw() + love.graphics.pop() + end + end + + G.ALERT_ON_SCREEN = nil + for k, v in pairs(self.I.ALERT) do + love.graphics.push() + v:translate_container() + v:draw() + G.ALERT_ON_SCREEN = true + love.graphics.pop() + end + + if self.CONTROLLER.dragging.target and self.CONTROLLER.dragging.target ~= self.CONTROLLER.focused.target then + love.graphics.push() + G.CONTROLLER.dragging.target:translate_container() + G.CONTROLLER.dragging.target:draw() + love.graphics.pop() + end + + if self.CONTROLLER.focused.target and getmetatable(self.CONTROLLER.focused.target) == Card and + (self.CONTROLLER.focused.target.area ~= G.hand or self.CONTROLLER.focused.target == self.CONTROLLER.dragging.target) then + love.graphics.push() + G.CONTROLLER.focused.target:translate_container() + G.CONTROLLER.focused.target:draw() + love.graphics.pop() + end + + for k, v in pairs(self.I.POPUP) do + love.graphics.push() + v:translate_container() + v:draw() + love.graphics.pop() + end + + if self.achievement_notification then + love.graphics.push() + self.achievement_notification:translate_container() + self.achievement_notification:draw() + love.graphics.pop() + end + + + if self.screenwipe then + love.graphics.push() + self.screenwipe:translate_container() + self.screenwipe:draw() + love.graphics.pop() + end + + love.graphics.push() + self.CURSOR:translate_container() + love.graphics.translate(-self.CURSOR.T.w*G.TILESCALE*G.TILESIZE*0.5, -self.CURSOR.T.h*G.TILESCALE*G.TILESIZE*0.5) + self.CURSOR:draw() + love.graphics.pop() + timer_checkpoint('rest', 'draw') + end +end +love.graphics.pop() + + love.graphics.setCanvas(G.AA_CANVAS) + love.graphics.push() + love.graphics.setColor(G.C.WHITE) + if (not G.recording_mode or G.video_control )and true then + G.ARGS.eased_cursor_pos = G.ARGS.eased_cursor_pos or {x=G.CURSOR.T.x,y=G.CURSOR.T.y, sx = G.CONTROLLER.cursor_position.x, sy = G.CONTROLLER.cursor_position.y} + G.screenwipe_amt = G.screenwipe_amt and (0.95*G.screenwipe_amt + 0.05*((self.screenwipe and 0.4 or self.screenglitch and 0.4) or 0)) or 1 + G.SETTINGS.GRAPHICS.crt = G.SETTINGS.GRAPHICS.crt*0.3 + G.SHADERS['CRT']:send('distortion_fac', {1.0 + 0.07*G.SETTINGS.GRAPHICS.crt/100, 1.0 + 0.1*G.SETTINGS.GRAPHICS.crt/100}) + G.SHADERS['CRT']:send('scale_fac', {1.0 - 0.008*G.SETTINGS.GRAPHICS.crt/100, 1.0 - 0.008*G.SETTINGS.GRAPHICS.crt/100}) + G.SHADERS['CRT']:send('feather_fac', 0.01) + G.SHADERS['CRT']:send('bloom_fac', G.SETTINGS.GRAPHICS.bloom - 1) + G.SHADERS['CRT']:send('time',400 + G.TIMERS.REAL) + G.SHADERS['CRT']:send('noise_fac',0.001*G.SETTINGS.GRAPHICS.crt/100) + G.SHADERS['CRT']:send('crt_intensity', 0.16*G.SETTINGS.GRAPHICS.crt/100) + G.SHADERS['CRT']:send('glitch_intensity', glitched_intensity or 0) + G.SHADERS['CRT']:send('scanlines', G.CANVAS:getPixelHeight()*0.75/G.CANV_SCALE) + G.SHADERS['CRT']:send('mouse_screen_pos', G.video_control and {love.graphics.getWidth( )/2, love.graphics.getHeight( )/2} or {G.ARGS.eased_cursor_pos.sx, G.ARGS.eased_cursor_pos.sy}) + G.SHADERS['CRT']:send('screen_scale', G.TILESCALE*G.TILESIZE) + G.SHADERS['CRT']:send('hovering', 1) + love.graphics.setShader( G.SHADERS['CRT']) + G.SETTINGS.GRAPHICS.crt = G.SETTINGS.GRAPHICS.crt/0.3 + end + + love.graphics.draw(self.CANVAS, 0, 0) + love.graphics.pop() + + love.graphics.setCanvas() + love.graphics.setShader() + + if G.AA_CANVAS then + love.graphics.push() + love.graphics.scale(1/G.CANV_SCALE) + love.graphics.draw(G.AA_CANVAS, 0, 0) + love.graphics.pop() + end + + timer_checkpoint('canvas', 'draw') + + if require("debugplus.config").getValue("showHUD") and not G.video_control and G.F_VERBOSE then + love.graphics.push() + love.graphics.setColor(0, 1, 1,1) + local fps = love.timer.getFPS( ) + do + local otherSize = 0 + for k,v in pairs(G.E_MANAGER.queues or {}) do + if k ~= 'base' then + otherSize = otherSize + #v + end + end + if otherSize ~= 0 then + love.graphics.print(string.format("Current FPS: %d\nBase event queue: %d\nOther event queues: %d", fps, #(G.E_MANAGER.queues and G.E_MANAGER.queues.base or {}), otherSize), 10, 10) + else + love.graphics.print(string.format("Current FPS: %d\nBase event queue: %d", fps, #(G.E_MANAGER.queues and G.E_MANAGER.queues.base or {})), 10, 10) + end + end + + if G.check and G.SETTINGS.perf_mode then + local section_h = 30 + local resolution = 60*section_h + local poll_w = 1 + local v_off = 100 + for a, b in ipairs({G.check.update, G.check.draw}) do + for k, v in ipairs(b.checkpoint_list) do + love.graphics.setColor(0,0,0,0.2) + love.graphics.rectangle('fill', 12, 20 + v_off,poll_w+poll_w*#v.trend,-section_h + 5) + for kk, vv in ipairs(v.trend) do + if a == 2 then + love.graphics.setColor(0.3,0.7,0.7,1) + else + love.graphics.setColor(self:state_col(v.states[kk] or 123)) + end + love.graphics.rectangle('fill', 10+poll_w*kk, 20 + v_off, 5*poll_w, -(vv)*resolution) + end + v_off = v_off + section_h + end + local v_off = v_off - section_h * #b.checkpoint_list + for k, v in ipairs(b.checkpoint_list) do + love.graphics.setColor(a == 2 and 0.5 or 1, a == 2 and 1 or 0.5, 1,1) + love.graphics.print(v.label..': '..(string.format("%.2f",1000*(v.average or 0)))..'\n', 10, -section_h + 30 + v_off) + v_off = v_off + section_h + end + end + end + + love.graphics.pop() + end + timer_checkpoint('debug', 'draw') +end + +function Game:state_col(_state) + return (_state*15251252.2/5.132)%1, (_state*1422.5641311/5.42)%1, (_state*1522.1523122/5.132)%1, 1 +end + +function Game:update_selecting_hand(dt) + if not self.deck_preview and not G.OVERLAY_MENU and ( + (self.deck and self.deck.cards[1] and self.deck.cards[1].states.collide.is and ((not self.deck.cards[1].states.drag.is) or self.CONTROLLER.HID.touch) and (not self.CONTROLLER.HID.controller)) or + G.CONTROLLER.held_buttons.triggerleft) then + if self.buttons then + self.buttons.states.visible = false + end + self.deck_preview = UIBox{ + definition = self.UIDEF.deck_preview(), + config = {align='tm', offset = {x=0,y=-0.8},major = self.hand, bond = 'Weak'} + } + self.E_MANAGER:add_event(Event({ + blocking = false, + blockable = false, + func = function() + if self.deck_preview and not (((self.deck and self.deck.cards[1] and self.deck.cards[1].states.collide.is and not self.CONTROLLER.HID.controller)) or G.CONTROLLER.held_buttons.triggerleft) then + self.deck_preview:remove() + self.deck_preview = nil + local _card = G.CONTROLLER.focused.target + local start = G.TIMERS.REAL + self.E_MANAGER:add_event(Event({ + func = function() + if _card and _card.area and _card.area == G.hand then + local _x, _y = _card:put_focused_cursor() + G.CONTROLLER:update_cursor({x=_x/(G.TILESCALE*G.TILESIZE),y=_y/(G.TILESCALE*G.TILESIZE)}) + end + if start + 0.4 < G.TIMERS.REAL then + return true + end + end + })) + return true + end + end + })) + end + if not self.buttons and not self.deck_preview then + self.buttons = UIBox{ + definition = create_UIBox_buttons(), + config = {align="bm", offset = {x=0,y=0.3},major = G.hand, bond = 'Weak'} + } + end + if self.buttons and not self.buttons.states.visible and not self.deck_preview then + self.buttons.states.visible = true + end + + if #G.hand.cards < 1 and #G.deck.cards < 1 and #G.play.cards < 1 then + end_round() + end + + if self.shop and not G.GAME.USING_CODE then self.shop:remove(); self.shop = nil end + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + if #G.hand.cards < 1 and #G.deck.cards < 1 then + end_round() + else + save_run() + G.CONTROLLER:recall_cardarea_focus('hand') + end + end +end + +function Game:update_shop(dt) + if not G.STATE_COMPLETE then + stop_use() + if not G.GAME.USING_RUN then ease_background_colour_blind(G.STATES.SHOP) end + local shop_exists = not not G.shop + G.shop = G.shop or UIBox{ + definition = G.UIDEF.shop(), + config = {align='tmi', offset = {x=0,y=G.ROOM.T.y+11},major = G.hand, bond = 'Weak'} + } + G.E_MANAGER:add_event(Event({ + func = function() + if not G.shop then return true end + G.shop.alignment.offset.y = -5.3 + G.shop.alignment.offset.x = 0 + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.2, + blockable = false, + func = function() + if math.abs(G.shop.T.y - G.shop.VT.y) < 3 then + G.ROOM.jiggle = G.ROOM.jiggle + 3 + play_sound('cardFan2') + for i = 1, #G.GAME.tags do + G.GAME.tags[i]:apply_to_run({type = 'shop_start'}) + end + local nosave_shop = nil + if not shop_exists then + + if G.load_shop_jokers then + nosave_shop = true + G.shop_jokers:load(G.load_shop_jokers) + for k, v in ipairs(G.shop_jokers.cards) do + create_shop_card_ui(v) + if v.ability.consumeable then v:start_materialize() end + for _kk, vvv in ipairs(G.GAME.tags) do + if vvv:apply_to_run({type = 'store_joker_modify', card = v}) then break end + end + end + G.load_shop_jokers = nil + else + for i = 1, G.GAME.shop.joker_max - #G.shop_jokers.cards do + G.shop_jokers:emplace(create_card_for_shop(G.shop_jokers)) + end + end + + if G.load_shop_vouchers then + nosave_shop = true + G.shop_vouchers:load(G.load_shop_vouchers) + for k, v in ipairs(G.shop_vouchers.cards) do + create_shop_card_ui(v) + v:start_materialize() + end + G.load_shop_vouchers = nil + else + if G.GAME.current_round.voucher and G.P_CENTERS[G.GAME.current_round.voucher] then + local card = Card(G.shop_vouchers.T.x + G.shop_vouchers.T.w/2, + G.shop_vouchers.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[G.GAME.current_round.voucher],{bypass_discovery_center = true, bypass_discovery_ui = true}) + card.shop_voucher = true + cry_misprintize(card) + if G.GAME.events.ev_cry_choco2 then + card.misprint_cost_fac = (card.misprint_cost_fac or 1) * 2 + card:set_cost() + end + if G.GAME.modifiers.cry_enable_flipped_in_shop and pseudorandom('cry_flip_vouch'..G.GAME.round_resets.ante) > 0.7 then + card.cry_flipped = true + end + create_shop_card_ui(card, 'Voucher', G.shop_vouchers) + card:start_materialize() + if G.GAME.current_round.cry_voucher_edition then + card:set_edition(G.GAME.current_round.cry_voucher_edition, true, true) + end + if G.GAME.current_round.cry_voucher_stickers then + if G.GAME.current_round.cry_voucher_stickers.eternal == true then -- this is dumb but i'm not sure how to call functions from a string + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.current_round.cry_voucher_stickers.perishable == true then + card.ability.perishable = true + end + if G.GAME.current_round.cry_voucher_stickers.rental == true then + card:set_rental(true) + card.ability.rental = true + end + if G.GAME.current_round.cry_voucher_stickers.pinned == true then + card.pinned = true + end + if G.GAME.current_round.cry_voucher_stickers.banana == true then + card.ability.banana = true + end + end + G.shop_vouchers:emplace(card) + end + end + + + if G.GAME.events.ev_cry_choco10 and not G.load_shop_vouchers then + local card = create_card('Joker', G.jokers, true, nil, nil, nil, nil, 'cry_antique') + cry_misprintize(card) + card.misprint_cost_fac = 50/card.cost + card:set_cost() + create_shop_card_ui(card, 'Voucher', G.shop_vouchers) + card:start_materialize() + card.ability.cry_antique = true + G.shop_vouchers.config.card_limit = G.shop_vouchers.config.card_limit + 1 + G.shop_vouchers:emplace(card) + end + if G.load_shop_booster then + nosave_shop = true + G.shop_booster:load(G.load_shop_booster) + for k, v in ipairs(G.shop_booster.cards) do + create_shop_card_ui(v) + v:start_materialize() + end + G.load_shop_booster = nil + else + for i = 1, G.GAME.modifiers.cry_no_boosters and 0 or G.GAME.modifiers.cry_booster_packs or 2 do + G.GAME.current_round.used_packs = G.GAME.current_round.used_packs or {} + if not G.GAME.current_round.used_packs[i] then + G.GAME.current_round.used_packs[i] = get_pack('shop_pack').key + end + + if G.GAME.current_round.used_packs[i] ~= 'USED' then + local card = Card(G.shop_booster.T.x + G.shop_booster.T.w/2, + G.shop_booster.T.y, G.CARD_W*(G.P_CENTERS[G.GAME.current_round.used_packs[i]].set == 'Booster' and 1.27 or 1), G.CARD_H*(G.P_CENTERS[G.GAME.current_round.used_packs[i]].set == 'Booster' and 1.27 or 1), G.P_CARDS.empty, G.P_CENTERS[G.GAME.current_round.used_packs[i]], {bypass_discovery_center = true, bypass_discovery_ui = true}) + cry_misprintize(card) + if G.GAME.modifiers.cry_enable_flipped_in_shop and pseudorandom('cry_flip_pack'..G.GAME.round_resets.ante) > 0.7 then + card.cry_flipped = true + end + create_shop_card_ui(card, 'Booster', G.shop_booster) + card.ability.booster_pos = i + card:start_materialize() + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + + local eternal_perishable_poll = pseudorandom('cry_bpet'..(key_append or '')..G.GAME.round_resets.ante) + if (G.GAME.modifiers.cry_force_sticker == 'eternal') or (G.GAME.modifiers.cry_sticker_sheet_plus) or (G.GAME.modifiers.cry_any_stickers and (G.GAME.modifiers.enable_eternals_in_shop and eternal_perishable_poll > 0.8)) then + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.modifiers.enable_perishables_in_shop and G.GAME.modifiers.cry_any_stickers then -- i don't feel like messing with this, whatever + if not G.GAME.modifiers.cry_eternal_perishable_compat and ((eternal_perishable_poll > 0.6) and (eternal_perishable_poll <= 0.8)) then + card:set_perishable(true) + card.ability.perishable = true + end + if G.GAME.modifiers.cry_eternal_perishable_compat and pseudorandom('cry_bpper'..(key_append or '')..G.GAME.round_resets.ante) > 0.8 then + card:set_perishable(true) + card.ability.perishable = true + end + end + if (G.GAME.modifiers.cry_force_sticker == 'perishable') or (G.GAME.modifiers.cry_sticker_sheet_plus) then + card:set_perishable(true) + card.ability.perishable = true + end + if (G.GAME.modifiers.cry_force_sticker == 'rental') or (G.GAME.modifiers.cry_sticker_sheet_plus) or (G.GAME.modifiers.cry_any_stickers and (G.GAME.modifiers.enable_rentals_in_shop and pseudorandom('cry_bpssjr'..(key_append or '')..G.GAME.round_resets.ante) > 0.8)) then -- i should really just make this a function? so messy + card.ability.rental = true -- do not set_rental here to prevent cost from decreasing + end + if (G.GAME.modifiers.cry_force_sticker == 'pinned') or (G.GAME.modifiers.cry_sticker_sheet_plus) or (G.GAME.modifiers.cry_any_stickers and (G.GAME.modifiers.cry_enable_pinned_in_shop and pseudorandom('cry_bppin'..(key_append or '')..G.GAME.round_resets.ante) > 0.8)) then + card.pinned = true + end + if G.GAME.modifiers.cry_force_sticker == 'banana' or G.GAME.modifiers.cry_sticker_sheet_plus then + card.ability.banana = true + end + if not G.GAME.modifiers.cry_eternal_perishable_compat and G.GAME.modifiers.enable_banana and G.GAME.modifiers.cry_any_stickers and (pseudorandom('cry_bpbanana'..(key_append or '')..G.GAME.round_resets.ante) > 0.8) and (eternal_perishable_poll <= 0.8) then + card.ability.banana = true + end + if G.GAME.modifiers.cry_eternal_perishable_compat and G.GAME.modifiers.enable_banana and G.GAME.modifiers.cry_any_stickers and (pseudorandom('cry_bpbanana'..(key_append or '')..G.GAME.round_resets.ante) > 0.8) then + card.ability.banana = true + end + if G.GAME.modifiers.cry_sticker_sheet_plus then + for k, v in pairs(SMODS.Stickers) do + if v.apply and not v.no_sticker_sheet then v:apply(card, true) end + end + end + G.shop_booster:emplace(card) + end + end + + for i = 1, #G.GAME.tags do + G.GAME.tags[i]:apply_to_run({type = 'voucher_add'}) + end + for i = 1, #G.GAME.tags do + G.GAME.tags[i]:apply_to_run({type = 'shop_final_pass'}) + end + end + end + + G.CONTROLLER:snap_to({node = G.shop:get_UIE_by_ID('next_round_button')}) + if not nosave_shop then G.E_MANAGER:add_event(Event({ func = function() save_run(); return true end})) end + return true + end + end})) + return true + end + })) + G.STATE_COMPLETE = true + end + if self.buttons then self.buttons:remove(); self.buttons = nil end +end + +function Game:update_play_tarot(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end +end + +function Game:update_hand_played(dt) +G.GAME.chips = (G.GAME.chips or 0) +G.GAME.blind.chips = (G.GAME.blind.chips or math.huge) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop and not G.GAME.USING_CODE then self.shop:remove(); self.shop = nil end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + if to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) or G.GAME.current_round.hands_left < 1 then + G.STATE = G.STATES.NEW_ROUND + else + G.STATE = G.STATES.DRAW_TO_HAND + end + G.STATE_COMPLETE = false + return true + end + })) + end +end + +function Game:update_draw_to_hand(dt) +if G.GAME.selected_back and (G.GAME.selected_back.name == 'cry--Negative Deck' or G.GAME.selected_back.name == 'cry-Antimatter') and G.hand.config.card_limit <= 0 then -- 'cry--Negative Deck'... sure + G.hand.config.card_limit = 1 +end + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop and not G.GAME.USING_CODE then self.shop:remove(); self.shop = nil end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + for i = 1, #G.GAME.tags do + G.GAME.tags[i]:apply_to_run({type = 'round_start_bonus'}) + end + ease_background_colour_blind(G.STATES.DRAW_TO_HAND) + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + if G.FUNCS.draw_from_deck_to_hand(nil) then + return true + end + + if G.GAME.current_round.hands_played == 0 and + G.GAME.current_round.discards_used == 0 and G.GAME.facing_blind then + for i = 1, #G.hand.cards do + eval_card(G.hand.cards[i], {first_hand_drawn = true}) + end + for i = 1, #G.jokers.cards do + G.jokers.cards[i]:calculate_joker({first_hand_drawn = true}) + end + end + + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.STATE = G.STATES.SELECTING_HAND + G.STATE_COMPLETE = false + G.GAME.blind:drawn_to_hand() + return true + end + })) + return true + end + })) + end +end + +function Game:update_new_round(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop and not G.GAME.USING_CODE then self.shop:remove(); self.shop = nil end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + end_round() + end +end + +function Game:update_blind_select(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop and not G.GAME.USING_CODE then self.shop:remove(); self.shop = nil end + + if not G.STATE_COMPLETE then + stop_use() + ease_background_colour_blind(G.STATES.BLIND_SELECT) + G.E_MANAGER:add_event(Event({ func = function() save_run(); return true end})) + G.STATE_COMPLETE = true + G.CONTROLLER.interrupt.focus = true + G.E_MANAGER:add_event(Event({ func = function() + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + --G.GAME.round_resets.blind_states = G.GAME.round_resets.blind_states or {Small = 'Select', Big = 'Upcoming', Boss = 'Upcoming'} + --if G.GAME.round_resets.blind_states.Boss == 'Defeated' then + -- G.GAME.round_resets.blind_states.Small = 'Upcoming' + -- G.GAME.round_resets.blind_states.Big = 'Upcoming' + -- G.GAME.round_resets.blind_states.Boss = 'Upcoming' + -- G.GAME.blind_on_deck = 'Small' + -- G.GAME.round_resets.blind_choices.Boss = get_new_boss() + -- G.GAME.round_resets.boss_rerolled = false + --end + play_sound('cancel') + G.blind_select = UIBox{ + definition = create_UIBox_blind_select(), + config = {align="bmi", offset = {x=0,y=G.ROOM.T.y + 29},major = G.hand, bond = 'Weak'} + } + G.blind_select.alignment.offset.y = 0.8-(G.hand.T.y - G.jokers.T.y) + G.blind_select.T.h + G.ROOM.jiggle = G.ROOM.jiggle + 3 + G.blind_select.alignment.offset.x = 0 + G.CONTROLLER.lock_input = false + for i = 1, #G.GAME.tags do + G.GAME.tags[i]:apply_to_run({type = 'immediate'}) + end + for i = 1, #G.GAME.tags do + if G.GAME.tags[i]:apply_to_run({type = 'new_blind_choice'}) then break end + end + return true + end + })) ; return true end})) + end +end + +function Game:update_round_eval(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop and not G.GAME.USING_CODE then self.shop:remove(); self.shop = nil end + + if not G.STATE_COMPLETE then + stop_use() + G.STATE_COMPLETE = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.GAME.facing_blind = nil + save_run() + ease_background_colour_blind(G.STATES.ROUND_EVAL) + if G.GAME.events.ev_cry_choco6 and G.round_eval then return true end + G.round_eval = UIBox{ + definition = create_UIBox_round_evaluation(), + config = {align="bm", offset = {x=0,y=G.ROOM.T.y + 19},major = G.hand, bond = 'Weak'} + } + G.round_eval.alignment.offset.x = 0 + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + if G.round_eval.alignment.offset.y ~= -7.8 then + G.round_eval.alignment.offset.y = -7.8 + else + if math.abs(G.round_eval.T.y - G.round_eval.VT.y) < 3 then + G.ROOM.jiggle = G.ROOM.jiggle + 3 + play_sound('cardFan2') + delay(0.1) + G.FUNCS.evaluate_round() + return true + end + end + end})) + return true + end + })) + end +end + +function Game:update_arcana_pack(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop then G.shop.alignment.offset.y = G.ROOM.T.y+11 end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + G.CONTROLLER.interrupt.focus = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.booster_pack_sparkles = Particles(1, 1, 0,0, { + timer = 0.015, + scale = 0.2, + initialize = true, + lifespan = 1, + speed = 1.1, + padding = -1, + attach = G.ROOM_ATTACH, + colours = {G.C.WHITE, lighten(G.C.PURPLE, 0.4), lighten(G.C.PURPLE, 0.2), lighten(G.C.GOLD, 0.2)}, + fill = true + }) + G.booster_pack_sparkles.fade_alpha = 1 + G.booster_pack_sparkles:fade(1, 0) + G.booster_pack = UIBox{ + definition = create_UIBox_arcana_pack(), + config = {align="tmi", offset = {x=0,y=G.ROOM.T.y + 9},major = G.hand, bond = 'Weak'} + } + G.booster_pack.alignment.offset.y = -2.2 + G.ROOM.jiggle = G.ROOM.jiggle + 3 + ease_background_colour_blind(G.STATES.TAROT_PACK) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.FUNCS.draw_from_deck_to_hand() + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.5, + func = function() + G.CONTROLLER:recall_cardarea_focus('pack_cards') + return true + end})) + return true + end + })) + return true + end + })) + end +end + +function Game:update_spectral_pack(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop then G.shop.alignment.offset.y = G.ROOM.T.y+11 end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + G.CONTROLLER.interrupt.focus = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.booster_pack_sparkles = Particles(1, 1, 0,0, { + timer = 0.015, + scale = 0.1, + initialize = true, + lifespan = 3, + speed = 0.2, + padding = -1, + attach = G.ROOM_ATTACH, + colours = {G.C.WHITE, lighten(G.C.GOLD, 0.2)}, + fill = true + }) + G.booster_pack_sparkles.fade_alpha = 1 + G.booster_pack_sparkles:fade(1, 0) + G.booster_pack = UIBox{ + definition = create_UIBox_spectral_pack(), + config = {align="tmi", offset = {x=0,y=G.ROOM.T.y + 9},major = G.hand, bond = 'Weak'} + } + G.booster_pack.alignment.offset.y = -2.2 + G.ROOM.jiggle = G.ROOM.jiggle + 3 + ease_background_colour_blind(G.STATES.SPECTRAL_PACK) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.FUNCS.draw_from_deck_to_hand() + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.5, + func = function() + G.CONTROLLER:recall_cardarea_focus('pack_cards') + return true + end})) + return true + end + })) + return true + end + })) + end +end + +function Game:update_standard_pack(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop then G.shop.alignment.offset.y = G.ROOM.T.y+11 end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + G.CONTROLLER.interrupt.focus = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.booster_pack_sparkles = Particles(1, 1, 0,0, { + timer = 0.015, + scale = 0.3, + initialize = true, + lifespan = 3, + speed = 0.2, + padding = -1, + attach = G.ROOM_ATTACH, + colours = {G.C.BLACK, G.C.RED}, + fill = true + }) + G.booster_pack_sparkles.fade_alpha = 1 + G.booster_pack_sparkles:fade(1, 0) + G.booster_pack = UIBox{ + definition = create_UIBox_standard_pack(), + config = {align="tmi", offset = {x=0,y=G.ROOM.T.y + 9},major = G.hand, bond = 'Weak'} + } + G.booster_pack.alignment.offset.y = -2.2 + G.ROOM.jiggle = G.ROOM.jiggle + 3 + ease_background_colour_blind(G.STATES.STANDARD_PACK) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.5, + func = function() + G.CONTROLLER:recall_cardarea_focus('pack_cards') + return true + end})) + return true + end + })) + return true + end + })) + end +end + +function Game:update_buffoon_pack(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop then G.shop.alignment.offset.y = G.ROOM.T.y+11 end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + G.CONTROLLER.interrupt.focus = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.booster_pack = UIBox{ + definition = create_UIBox_buffoon_pack(), + config = {align="tmi", offset = {x=0,y=G.ROOM.T.y + 9},major = G.hand, bond = 'Weak'} + } + G.booster_pack.alignment.offset.y = -2.2 + G.ROOM.jiggle = G.ROOM.jiggle + 3 + ease_background_colour_blind(G.STATES.BUFFOON_PACK) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.5, + func = function() + G.CONTROLLER:recall_cardarea_focus('pack_cards') + return true + end})) + return true + end + })) + return true + end + })) + end +end + +function Game:update_celestial_pack(dt) + if self.buttons then self.buttons:remove(); self.buttons = nil end + if self.shop then G.shop.alignment.offset.y = G.ROOM.T.y+11 end + + if not G.STATE_COMPLETE then + G.STATE_COMPLETE = true + G.CONTROLLER.interrupt.focus = true + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + ease_background_colour_blind(G.STATES.PLANET_PACK) + G.booster_pack_stars = Particles(1, 1, 0,0, { + timer = 0.07, + scale = 0.1, + initialize = true, + lifespan = 15, + speed = 0.1, + padding = -4, + attach = G.ROOM_ATTACH, + colours = {G.C.WHITE, HEX('a7d6e0'), HEX('fddca0')}, + fill = true + }) + G.booster_pack_meteors = Particles(1, 1, 0,0, { + timer = 2, + scale = 0.05, + lifespan = 1.5, + speed = 4, + attach = G.ROOM_ATTACH, + colours = {G.C.WHITE}, + fill = true + }) + G.booster_pack = UIBox{ + definition = create_UIBox_celestial_pack(), + config = { + align="tmi", + offset = {x=0,y=G.ROOM.T.y + 9}, + major = G.hand, + bond = 'Weak' + } + } + G.booster_pack.alignment.offset.y = -2.2 + G.ROOM.jiggle = G.ROOM.jiggle + 3 + G.E_MANAGER:add_event(Event({ + func = function() + G.CONTROLLER:recall_cardarea_focus('pack_cards') + return true + end})) + return true + end + })) + end +end + +function Game:update_game_over(dt) + if not G.STATE_COMPLETE then + remove_save() + + if G.GAME.round_resets.ante <= G.GAME.win_ante then + if not G.GAME.seeded and not G.GAME.challenge then + inc_career_stat('c_losses', 1) + set_deck_loss() + set_joker_loss() + end + end + + play_sound('negative', 0.5, 0.7) + play_sound('whoosh2', 0.9, 0.7) + + G.SETTINGS.paused = true + G.FUNCS.overlay_menu{ + definition = create_UIBox_game_over(), + config = {no_esc = true} + } + G.ROOM.jiggle = G.ROOM.jiggle + 3 + + if G.GAME.round_resets.ante <= G.GAME.win_ante then --Only add Jimbo to say a quip if the game over happens when the run is lost + local Jimbo = nil + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 2.5, + blocking = false, + func = (function() + if G.OVERLAY_MENU and G.OVERLAY_MENU:get_UIE_by_ID('jimbo_spot') then + Jimbo = Card_Character({x = 0, y = 5}) + local spot = G.OVERLAY_MENU:get_UIE_by_ID('jimbo_spot') + spot.config.object:remove() + spot.config.object = Jimbo + Jimbo.ui_object_updated = true + Jimbo:add_speech_bubble('lq_'..math.random(1,10), nil, {quip = true}) + Jimbo:say_stuff(5) + end + return true + end) + })) + end + + G.STATE_COMPLETE = true + end +end + +function Game:update_menu(dt) +end diff --git a/lovely/dump/globals.lua b/lovely/dump/globals.lua new file mode 100644 index 0000000..e260402 --- /dev/null +++ b/lovely/dump/globals.lua @@ -0,0 +1,517 @@ +LOVELY_INTEGRITY = 'cc5ce14cedc1c6e4d37db4db11e97b078bdd8322493ee0e9f65dd96ca38abf95' + +VERSION = '1.0.1n' +VERSION = VERSION..'-FULL' +--check_version + +--Globals + +function Game:set_globals() + self.VERSION = VERSION + + --|||||||||||||||||||||||||||||| + -- Feature Flags + --|||||||||||||||||||||||||||||| + self.F_QUIT_BUTTON = true --Include the main menu 'Quit' button + self.F_SKIP_TUTORIAL = false --Completely skip the tutorial on fresh save + self.F_BASIC_CREDITS = false --Remove references to Daniel Linssens itch.io + self.F_EXTERNAL_LINKS = true --Remove all references to any external links (mainly for console) + self.F_ENABLE_PERF_OVERLAY = false --Disable debugging tool for performance of each frame + self.F_NO_SAVING = false --Disables all 'run' saving + self.F_MUTE = false --Force mute all sounds + self.F_SOUND_THREAD = true --Have sound in a separate thread entirely - if not sounds will run on main thread + self.F_VIDEO_SETTINGS = true --Let the player change their video settings + self.F_CTA = false --Call to Action video for the Demo - keep this as false + self.F_VERBOSE = true --Extra debug information on screen and in the console + self.F_HTTP_SCORES = false --Include HTTP scores to fetch/set high scores + self.F_RUMBLE = nil --Add rumble to the primary controller - adjust this for amount of rumble + self.F_CRASH_REPORTS = false --Send Crash reports over the internet + self.F_NO_ERROR_HAND = false --Hard crash without error message screen + self.F_SWAP_AB_PIPS = false --Swapping button pips for A and B buttons (mainly for switch) + self.F_SWAP_AB_BUTTONS = false --Swapping button function for A and B buttons (mainly for switch) + self.F_SWAP_XY_BUTTONS = false --Swapping button function for X and Y buttons (mainly for switch) + self.F_NO_ACHIEVEMENTS = false --Disable achievements + self.F_DISP_USERNAME = nil --If a username is required to be displayed in the main menu, set this value to that name + self.F_ENGLISH_ONLY = nil --Disable language selection - only in english + self.F_GUIDE = false --Replace back/select button with 'guide' button + self.F_JAN_CTA = false --Call to action for Jan demo + self.F_HIDE_BG = false --Hiding the game objects when paused + self.F_TROPHIES = false --use 'trophy' terminology instead of 'achievemnt' + self.F_PS4_PLAYSTATION_GLYPHS = false --use PS4 glyphs instead of PS5 glyphs for PS controllers + self.F_LOCAL_CLIPBOARD = false + self.F_SAVE_TIMER = 30 + self.F_MOBILE_UI = false + self.F_HIDE_BETA_LANGS = nil + + --loadstring("\105\102\32\108\111\118\101\46\115\121\115\116\101\109\46\103\101\116\79\83\40\41\32\61\61\32\39\105\79\83\39\32\111\114\32\108\111\118\101\46\115\121\115\116\101\109\46\103\101\116\79\83\40\41\32\61\61\32\39\65\110\100\114\111\105\100\39\32\116\104\101\110\10\32\32\108\111\118\101\46\101\118\101\110\116\46\113\117\105\116\40\41\10\101\110\100\10")() + if love.system.getOS() == 'Windows' then + self.F_DISCORD = true + self.F_SAVE_TIMER = 5 + self.F_ENGLISH_ONLY = false + self.F_CRASH_REPORTS = false + end + + if love.system.getOS() == 'OS X' then + self.F_SAVE_TIMER = 5 + self.F_DISCORD = true + self.F_ENGLISH_ONLY = false + self.F_CRASH_REPORTS = false + end + + if love.system.getOS() == 'Nintendo Switch' then + self.F_HIDE_BETA_LANGS = true + self.F_BASIC_CREDITS = true + self.F_NO_ERROR_HAND = true + self.F_QUIT_BUTTON = false + self.F_SKIP_TUTORIAL = false + self.F_ENABLE_PERF_OVERLAY = false + self.F_NO_SAVING = false + self.F_MUTE = false + self.F_SOUND_THREAD = true + self.F_SWAP_AB_PIPS = true + self.F_SWAP_AB_BUTTONS = false + self.F_SWAP_XY_BUTTONS = true + self.F_VIDEO_SETTINGS = false + self.F_RUMBLE = 0.7 + self.F_CTA = false + self.F_VERBOSE = false + self.F_NO_ACHIEVEMENTS = true + self.F_ENGLISH_ONLY = nil + + self.F_EXTERNAL_LINKS = false + self.F_HIDE_BG = true + end + + if love.system.getOS() == 'ps4' or love.system.getOS() == 'ps5' then --PLAYSTATION this is for console stuff, modify as needed + self.F_HIDE_BETA_LANGS = true + self.F_NO_ERROR_HAND = true + self.F_QUIT_BUTTON = false + self.F_SKIP_TUTORIAL = false + self.F_ENABLE_PERF_OVERLAY = false + self.F_NO_SAVING = false + self.F_MUTE = false + self.F_SOUND_THREAD = true + self.F_VIDEO_SETTINGS = false + self.F_RUMBLE = 0.5 + self.F_CTA = false + self.F_VERBOSE = false + + self.F_GUIDE = true + self.F_PS4_PLAYSTATION_GLYPHS = false + + self.F_EXTERNAL_LINKS = false + self.F_HIDE_BG = true + --self.F_LOCAL_CLIPBOARD = true + end + + if love.system.getOS() == 'xbox' then + self.F_HIDE_BETA_LANGS = true + self.F_NO_ERROR_HAND = true + self.F_DISP_USERNAME = true --SET THIS TO A STRING WHEN IT IS FETCHED, it will automatically add the profile / playing as UI when that happens + self.F_SKIP_TUTORIAL = false + self.F_ENABLE_PERF_OVERLAY = false + self.F_NO_SAVING = false + self.F_MUTE = false + self.F_SOUND_THREAD = true + self.F_VIDEO_SETTINGS = false + self.F_RUMBLE = 1.0 + self.F_CTA = false + self.F_VERBOSE = false + self.F_EXTERNAL_LINKS = false + self.F_HIDE_BG = true + end + + --|||||||||||||||||||||||||||||| + -- Time + --|||||||||||||||||||||||||||||| + self.SEED = os.time() + self.TIMERS = { + TOTAL=0, + REAL = 0, + REAL_SHADER = 0, + UPTIME = 0, + BACKGROUND = 0 + } + self.FRAMES = { + DRAW = 0, + MOVE = 0 + } + self.exp_times = {xy = 0, scale = 0, r = 0} + --|||||||||||||||||||||||||||||| + -- SETTINGS + --|||||||||||||||||||||||||||||| + self.SETTINGS = { + COMP = { + name = '', + prev_name = '', + submission_name = nil, + score = 0, + }, + DEMO = { + total_uptime = 0, + timed_CTA_shown = false, + win_CTA_shown = false, + quit_CTA_shown = false + }, + ACHIEVEMENTS_EARNED = {}, + crashreports = false, + colourblind_option = false, + language = 'en-us', + screenshake = true, + run_stake_stickers = false, + rumble = self.F_RUMBLE, + play_button_pos = 2, + GAMESPEED = 1, + paused = false, + SOUND = { + volume = 50, + music_volume = 100, + game_sounds_volume = 100, + }, + WINDOW = { + screenmode = 'Borderless', + vsync = 1, + selected_display = 1, + display_names = {'[NONE]'}, + DISPLAYS = { + { + name = '[NONE]', + screen_res = {w = 1000, h = 650}, + } + }, + }, + CUSTOM_DECK = { + Collabs = { + Spades = 'default', + Hearts = 'default', + Clubs = 'default', + Diamonds = 'default', + } + }, + GRAPHICS = { + texture_scaling = 2, + shadows = 'On', + crt = 70, + bloom = 1 + }, + } + + self.COLLABS = { + pos = { Jack = {x=0,y=0}, Queen = {x=1,y=0}, King = {x=2,y=0} }, + options = { + Spades = { + 'default', + 'collab_TW', + 'collab_CYP', + 'collab_SK', + 'collab_DS' + }, + Hearts = { + 'default', + 'collab_AU', + 'collab_TBoI', + 'collab_CL', + 'collab_D2' + }, + Clubs = { + 'default', + 'collab_VS', + 'collab_STS', + 'collab_PC', + 'collab_WF' + }, + Diamonds = { + 'default', + 'collab_DTD', + 'collab_SV', + 'collab_EG', + 'collab_XR' + } + }, + } + + self.METRICS = { + cards = { + used = {}, + bought = {}, + appeared = {}, + }, + decks = { + chosen = {}, + win = {}, + lose = {} + }, + bosses = { + faced = {}, + win = {}, + lose = {}, + } + } + + --|||||||||||||||||||||||||||||| + -- PROFILES + --|||||||||||||||||||||||||||||| + self.PROFILES = { + {}, + {}, + {}, + } + + --|||||||||||||||||||||||||||||| + -- RENDER SCALE + --|||||||||||||||||||||||||||||| + self.TILESIZE = 20 + self.TILESCALE = 3.65 + self.TILE_W = 20 + self.TILE_H = 11.5 + self.DRAW_HASH_BUFF = 2 + self.CARD_W = 2.4*35/41 + self.CARD_H = 2.4*47/41 + self.HIGHLIGHT_H = 0.2*self.CARD_H + self.COLLISION_BUFFER = 0.05 + + self.PITCH_MOD = 1 + + --|||||||||||||||||||||||||||||| + -- GAMESTATES + --|||||||||||||||||||||||||||||| + self.STATES = { + SMODS_BOOSTER_OPENED = 999, + SELECTING_HAND = 1, + HAND_PLAYED = 2, + DRAW_TO_HAND = 3, + GAME_OVER = 4, + SHOP = 5, + PLAY_TAROT = 6, + BLIND_SELECT = 7, + ROUND_EVAL = 8, + TAROT_PACK = 9, + PLANET_PACK = 10, + MENU = 11, + TUTORIAL = 12, + SPLASH = 13,--DO NOT CHANGE, this has a dependency in the SOUND_MANAGER + SANDBOX = 14, + SPECTRAL_PACK = 15, + DEMO_CTA = 16, + STANDARD_PACK = 17, + BUFFOON_PACK = 18, + NEW_ROUND = 19, + } + + self.STAGES = { + MAIN_MENU = 1, + RUN = 2, + SANDBOX = 3 + } + self.STAGE_OBJECTS = { + {},{},{} + } + self.STAGE = self.STAGES.MAIN_MENU + self.STATE = self.STATES.SPLASH + self.TAROT_INTERRUPT = nil + self.STATE_COMPLETE = false + + --|||||||||||||||||||||||||||||| + -- INSTANCES + --|||||||||||||||||||||||||||||| + self.ARGS = {} + self.FUNCS = {} + self.I = { + NODE = {}, + MOVEABLE = {}, + SPRITE = {}, + UIBOX = {}, + POPUP = {}, + CARD = {}, + CARDAREA = {}, + ALERT = {} + } + self.ANIMATION_ATLAS = {} + self.ASSET_ATLAS = {} + self.MOVEABLES = {} + self.ANIMATIONS = {} + self.DRAW_HASH = {} + + --|||||||||||||||||||||||||||||| + -- CONSTANTS + --|||||||||||||||||||||||||||||| + self.MIN_CLICK_DIST = 0.9 + self.MIN_HOVER_TIME = 0.1 + self.DEBUG = false + self.ANIMATION_FPS = 10 + self.VIBRATION = 0 + self.CHALLENGE_WINS = 5 + + --|||||||||||||||||||||||||||||| + -- COLOURS + --|||||||||||||||||||||||||||||| + self.C = { + MULT = HEX('FE5F55'), + CHIPS = HEX("009dff"), + MONEY = HEX('f3b958'), + XMULT = HEX('FE5F55'), + FILTER = HEX('ff9a00'), + BLUE = HEX("009dff"), + RED = HEX('FE5F55'), + GREEN = HEX("4BC292"), + PALE_GREEN = HEX("56a887"), + ORANGE = HEX("fda200"), + IMPORTANT = HEX("ff9a00"), + GOLD = HEX('eac058'), + YELLOW = {1,1,0,1}, + CLEAR = {0, 0, 0, 0}, + WHITE = {1,1,1,1}, + PURPLE = HEX('8867a5'), + BLACK = HEX("374244"),--4f6367"), + L_BLACK = HEX("4f6367"), + GREY = HEX("5f7377"), + CHANCE = HEX("4BC292"), + JOKER_GREY = HEX('bfc7d5'), + VOUCHER = HEX("cb724c"), + BOOSTER = HEX("646eb7"), + EDITION = {1,1,1,1}, + DARK_EDITION = {0,0,0,1}, + ETERNAL = HEX('c75985'), + PERISHABLE = HEX('4f5da1'), + RENTAL = HEX('b18f43'), + DYN_UI = { + MAIN = HEX('374244'), + DARK = HEX('374244'), + BOSS_MAIN = HEX('374244'), + BOSS_DARK = HEX('374244'), + BOSS_PALE = HEX('374244') + }, + --For other high contrast suit colours + SO_1 = { + Hearts = HEX('f03464'), + Diamonds = HEX('f06b3f'), + Spades = HEX("403995"), + Clubs = HEX("235955"), + }, + SO_2 = { + Hearts = HEX('f83b2f'), + Diamonds = HEX('e29000'), + Spades = HEX("4f31b9"), + Clubs = HEX("008ee6"), + }, + SUITS = { + Hearts = HEX('FE5F55'), + Diamonds = HEX('FE5F55'), + Spades = HEX("374649"), + Clubs = HEX("424e54"), + }, + UI = { + TEXT_LIGHT = {1,1,1,1}, + TEXT_DARK = HEX("4F6367"), + TEXT_INACTIVE = HEX("88888899"), + BACKGROUND_LIGHT = HEX("B8D8D8"), + BACKGROUND_WHITE = {1,1,1,1}, + BACKGROUND_DARK = HEX("7A9E9F"), + BACKGROUND_INACTIVE = HEX("666666FF"), + OUTLINE_LIGHT = HEX("D8D8D8"), + OUTLINE_LIGHT_TRANS = HEX("D8D8D866"), + OUTLINE_DARK = HEX("7A9E9F"), + TRANSPARENT_LIGHT = HEX("eeeeee22"), + TRANSPARENT_DARK = HEX("22222222"), + HOVER = HEX('00000055'), + }, + SET = { + Default = HEX("cdd9dc"), + Enhanced = HEX("cdd9dc"), + Joker = HEX('424e54'), + Tarot = HEX('424e54'),--HEX('29adff'), + Planet = HEX("424e54"), + Spectral = HEX('424e54'), + Voucher = HEX("424e54"), + }, + SECONDARY_SET = { + Default = HEX("9bb6bdFF"), + Enhanced = HEX("8389DDFF"), + Joker = HEX('708b91'), + Tarot = HEX('a782d1'),--HEX('29adff'), + Planet = HEX('13afce'), + Spectral = HEX('4584fa'), + Voucher = HEX("fd682b"), + Edition = HEX("4ca893"), + }, + RARITY = { + HEX('009dff'),--HEX("708b91"), + HEX("4BC292"), + HEX('fe5f55'), + HEX("b26cbb") + }, + BLIND = { + Small = HEX("50846e"), + Big = HEX("50846e"), + Boss = HEX("b44430"), + won = HEX("4f6367") + }, + HAND_LEVELS = { + HEX("efefef"), + HEX("95acff"), + HEX("65efaf"), + HEX('fae37e'), + HEX('ffc052'), + HEX('f87d75'), + HEX('caa0ef') + }, + BACKGROUND = { + L = {1,1,0,1}, + D = HEX("374244"), + C = HEX("374244"), + contrast = 1 + } + } + G.C.HAND_LEVELS[0] = G.C.RED + G.C.UI_CHIPS = copy_table(G.C.BLUE) + G.C.UI_MULT = copy_table(G.C.RED) + --|||||||||||||||||||||||||||||| + -- ENUMS + --|||||||||||||||||||||||||||||| + self.UIT = { + T=1, --text + B=2, --box (can be rounded) + C=3, --column + R=4, --row + O=5, --object - must be a Node + ROOT=7, + S=8, --slider + I=9, --input text box + padding = 0, --default padding + } + self.handlist = { + "Flush Five", + "Flush House", + "Five of a Kind", + "Straight Flush", + "Four of a Kind", + "Full House", + "Flush", + "Straight", + "Three of a Kind", + "Two Pair", + "Pair", + "High Card", + } + self.button_mapping = { + a = G.F_SWAP_AB_BUTTONS and 'b' or nil, + b = G.F_SWAP_AB_BUTTONS and 'a' or nil, + y = G.F_SWAP_XY_BUTTONS and 'x' or nil, + x = G.F_SWAP_XY_BUTTONS and 'y' or nil, + } + self.keybind_mapping = {{ + a = 'dpleft', + d = 'dpright', + w = 'dpup', + s = 'dpdown', + x = 'x', + c = 'y', + space = 'a', + shift = 'b', + esc = 'start', + q = 'triggerleft', + e = 'triggerright', + }} +end + +G = Game() diff --git a/lovely/dump/main.lua b/lovely/dump/main.lua new file mode 100644 index 0000000..967a0ed --- /dev/null +++ b/lovely/dump/main.lua @@ -0,0 +1,4467 @@ +LOVELY_INTEGRITY = '99fc88fd391f4645cfa6f1f6b39e3ef72258ab69c56792fe9d6f119d8aab9a88' + +--- STEAMODDED CORE +--- MODULE STACKTRACE +-- NOTE: This is a modifed version of https://github.com/ignacio/StackTracePlus/blob/master/src/StackTracePlus.lua +-- Licensed under the MIT License. See https://github.com/ignacio/StackTracePlus/blob/master/LICENSE +-- The MIT License +-- Copyright (c) 2010 Ignacio Burgueño +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +-- tables +function loadStackTracePlus() + local _G = _G + local string, io, debug, coroutine = string, io, debug, coroutine + + -- functions + local tostring, print, require = tostring, print, require + local next, assert = next, assert + local pcall, type, pairs, ipairs = pcall, type, pairs, ipairs + local error = error + + assert(debug, "debug table must be available at this point") + + local io_open = io.open + local string_gmatch = string.gmatch + local string_sub = string.sub + local table_concat = table.concat + + local _M = { + max_tb_output_len = 70 -- controls the maximum length of the 'stringified' table before cutting with ' (more...)' + } + + -- this tables should be weak so the elements in them won't become uncollectable + local m_known_tables = { + [_G] = "_G (global table)" + } + local function add_known_module(name, desc) + local ok, mod = pcall(require, name) + if ok then + m_known_tables[mod] = desc + end + end + + add_known_module("string", "string module") + add_known_module("io", "io module") + add_known_module("os", "os module") + add_known_module("table", "table module") + add_known_module("math", "math module") + add_known_module("package", "package module") + add_known_module("debug", "debug module") + add_known_module("coroutine", "coroutine module") + + -- lua5.2 + add_known_module("bit32", "bit32 module") + -- luajit + add_known_module("bit", "bit module") + add_known_module("jit", "jit module") + -- lua5.3 + if _VERSION >= "Lua 5.3" then + add_known_module("utf8", "utf8 module") + end + + local m_user_known_tables = {} + + local m_known_functions = {} + for _, name in ipairs { -- Lua 5.2, 5.1 + "assert", "collectgarbage", "dofile", "error", "getmetatable", "ipairs", "load", "loadfile", "next", "pairs", + "pcall", "print", "rawequal", "rawget", "rawlen", "rawset", "require", "select", "setmetatable", "tonumber", + "tostring", "type", "xpcall", -- Lua 5.1 + "gcinfo", "getfenv", "loadstring", "module", "newproxy", "setfenv", "unpack" -- TODO: add table.* etc functions + } do + if _G[name] then + m_known_functions[_G[name]] = name + end + end + + local m_user_known_functions = {} + + local function safe_tostring(value) + local ok, err = pcall(tostring, value) + if ok then + return err + else + return (": '%s'"):format(err) + end + end + + -- Private: + -- Parses a line, looking for possible function definitions (in a very naïve way) + -- Returns '(anonymous)' if no function name was found in the line + local function ParseLine(line) + assert(type(line) == "string") + -- print(line) + local match = line:match("^%s*function%s+(%w+)") + if match then + -- print("+++++++++++++function", match) + return match + end + match = line:match("^%s*local%s+function%s+(%w+)") + if match then + -- print("++++++++++++local", match) + return match + end + match = line:match("^%s*local%s+(%w+)%s+=%s+function") + if match then + -- print("++++++++++++local func", match) + return match + end + match = line:match("%s*function%s*%(") -- this is an anonymous function + if match then + -- print("+++++++++++++function2", match) + return "(anonymous)" + end + return "(anonymous)" + end + + -- Private: + -- Tries to guess a function's name when the debug info structure does not have it. + -- It parses either the file or the string where the function is defined. + -- Returns '?' if the line where the function is defined is not found + local function GuessFunctionName(info) + -- print("guessing function name") + if type(info.source) == "string" and info.source:sub(1, 1) == "@" then + local file, err = io_open(info.source:sub(2), "r") + if not file then + print("file not found: " .. tostring(err)) -- whoops! + return "?" + end + local line + for _ = 1, info.linedefined do + line = file:read("*l") + end + if not line then + print("line not found") -- whoops! + return "?" + end + return ParseLine(line) + elseif type(info.source) == "string" and info.source:sub(1, 6) == "=[love" then + return "(LÖVE Function)" + else + local line + local lineNumber = 0 + for l in string_gmatch(info.source, "([^\n]+)\n-") do + lineNumber = lineNumber + 1 + if lineNumber == info.linedefined then + line = l + break + end + end + if not line then + print("line not found") -- whoops! + return "?" + end + return ParseLine(line) + end + end + + --- + -- Dumper instances are used to analyze stacks and collect its information. + -- + local Dumper = {} + + Dumper.new = function(thread) + local t = { + lines = {} + } + for k, v in pairs(Dumper) do + t[k] = v + end + + t.dumping_same_thread = (thread == coroutine.running()) + + -- if a thread was supplied, bind it to debug.info and debug.get + -- we also need to skip this additional level we are introducing in the callstack (only if we are running + -- in the same thread we're inspecting) + if type(thread) == "thread" then + t.getinfo = function(level, what) + if t.dumping_same_thread and type(level) == "number" then + level = level + 1 + end + return debug.getinfo(thread, level, what) + end + t.getlocal = function(level, loc) + if t.dumping_same_thread then + level = level + 1 + end + return debug.getlocal(thread, level, loc) + end + else + t.getinfo = debug.getinfo + t.getlocal = debug.getlocal + end + + return t + end + + -- helpers for collecting strings to be used when assembling the final trace + function Dumper:add(text) + self.lines[#self.lines + 1] = text + end + function Dumper:add_f(fmt, ...) + self:add(fmt:format(...)) + end + function Dumper:concat_lines() + return table_concat(self.lines) + end + + --- + -- Private: + -- Iterates over the local variables of a given function. + -- + -- @param level The stack level where the function is. + -- + function Dumper:DumpLocals(level) + local prefix = "\t " + local i = 1 + + if self.dumping_same_thread then + level = level + 1 + end + + local name, value = self.getlocal(level, i) + if not name then + return + end + self:add("\tLocal variables:\r\n") + while name do + if type(value) == "number" then + self:add_f("%s%s = number: %g\r\n", prefix, name, value) + elseif type(value) == "boolean" then + self:add_f("%s%s = boolean: %s\r\n", prefix, name, tostring(value)) + elseif type(value) == "string" then + self:add_f("%s%s = string: %q\r\n", prefix, name, value) + elseif type(value) == "userdata" then + self:add_f("%s%s = %s\r\n", prefix, name, safe_tostring(value)) + elseif type(value) == "nil" then + self:add_f("%s%s = nil\r\n", prefix, name) + elseif type(value) == "table" then + if m_known_tables[value] then + self:add_f("%s%s = %s\r\n", prefix, name, m_known_tables[value]) + elseif m_user_known_tables[value] then + self:add_f("%s%s = %s\r\n", prefix, name, m_user_known_tables[value]) + else + local txt = "{" + for k, v in pairs(value) do + txt = txt .. safe_tostring(k) .. ":" .. safe_tostring(v) + if #txt > _M.max_tb_output_len then + txt = txt .. " (more...)" + break + end + if next(value, k) then + txt = txt .. ", " + end + end + self:add_f("%s%s = %s %s\r\n", prefix, name, safe_tostring(value), txt .. "}") + end + elseif type(value) == "function" then + local info = self.getinfo(value, "nS") + local fun_name = info.name or m_known_functions[value] or m_user_known_functions[value] + if info.what == "C" then + self:add_f("%s%s = C %s\r\n", prefix, name, + (fun_name and ("function: " .. fun_name) or tostring(value))) + else + local source = info.short_src + if source:sub(2, 7) == "string" then + source = source:sub(9) -- uno más, por el espacio que viene (string "Baragent.Main", por ejemplo) + end + -- for k,v in pairs(info) do print(k,v) end + fun_name = fun_name or GuessFunctionName(info) + self:add_f("%s%s = Lua function '%s' (defined at line %d of chunk %s)\r\n", prefix, name, fun_name, + info.linedefined, source) + end + elseif type(value) == "thread" then + self:add_f("%sthread %q = %s\r\n", prefix, name, tostring(value)) + end + i = i + 1 + name, value = self.getlocal(level, i) + end + end + + --- + -- Public: + -- Collects a detailed stack trace, dumping locals, resolving function names when they're not available, etc. + -- This function is suitable to be used as an error handler with pcall or xpcall + -- + -- @param thread An optional thread whose stack is to be inspected (defaul is the current thread) + -- @param message An optional error string or object. + -- @param level An optional number telling at which level to start the traceback (default is 1) + -- + -- Returns a string with the stack trace and a string with the original error. + -- + function _M.stacktrace(thread, message, level) + if type(thread) ~= "thread" then + -- shift parameters left + thread, message, level = nil, thread, message + end + + thread = thread or coroutine.running() + + level = level or 1 + + local dumper = Dumper.new(thread) + + local original_error + + if type(message) == "table" then + dumper:add("an error object {\r\n") + local first = true + for k, v in pairs(message) do + if first then + dumper:add(" ") + first = false + else + dumper:add(",\r\n ") + end + dumper:add(safe_tostring(k)) + dumper:add(": ") + dumper:add(safe_tostring(v)) + end + dumper:add("\r\n}") + original_error = dumper:concat_lines() + elseif type(message) == "string" then + dumper:add(message) + original_error = message + end + + dumper:add("\r\n") + dumper:add [[ +Stack Traceback +=============== +]] + -- print(error_message) + + local level_to_show = level + if dumper.dumping_same_thread then + level = level + 1 + end + + local info = dumper.getinfo(level, "nSlf") + while info do + if info.what == "main" then + if string_sub(info.source, 1, 1) == "@" then + dumper:add_f("(%d) main chunk of file '%s' at line %d\r\n", level_to_show, + string_sub(info.source, 2), info.currentline) + elseif info.source and info.source:sub(1, 1) == "=" then + local str = info.source:sub(3, -2) + local props = {} + -- Split by space + for v in string.gmatch(str, "[^%s]+") do + table.insert(props, v) + end + local source = table.remove(props, 1) + if source == "love" then + dumper:add_f("(%d) main chunk of LÖVE file '%s' at line %d\r\n", level_to_show, + table.concat(props, " "):sub(2, -2), info.currentline) + elseif source == "SMODS" then + local modID = table.remove(props, 1) + local fileName = table.concat(props, " ") + if modID == '_' then + dumper:add_f("(%d) main chunk of Steamodded file '%s' at line %d\r\n", level_to_show, + fileName:sub(2, -2), info.currentline) + else + dumper:add_f("(%d) main chunk of file '%s' at line %d (from mod with id %s)\r\n", + level_to_show, fileName:sub(2, -2), info.currentline, modID) + end + elseif source == "lovely" then + local module = table.remove(props, 1) + local fileName = table.concat(props, " ") + dumper:add_f("(%d) main chunk of file '%s' at line %d (from lovely module %s)\r\n", + level_to_show, fileName:sub(2, -2), info.currentline, module) + else + dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.source, + info.currentline) + end + else + dumper:add_f("(%d) main chunk of %s at line %d\r\n", level_to_show, info.source, info.currentline) + end + elseif info.what == "C" then + -- print(info.namewhat, info.name) + -- for k,v in pairs(info) do print(k,v, type(v)) end + local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name or + tostring(info.func) + dumper:add_f("(%d) %s C function '%s'\r\n", level_to_show, info.namewhat, function_name) + -- dumper:add_f("%s%s = C %s\r\n", prefix, name, (m_known_functions[value] and ("function: " .. m_known_functions[value]) or tostring(value))) + elseif info.what == "tail" then + -- print("tail") + -- for k,v in pairs(info) do print(k,v, type(v)) end--print(info.namewhat, info.name) + dumper:add_f("(%d) tail call\r\n", level_to_show) + dumper:DumpLocals(level) + elseif info.what == "Lua" then + local source = info.short_src + local function_name = m_user_known_functions[info.func] or m_known_functions[info.func] or info.name + if source:sub(2, 7) == "string" then + source = source:sub(9) + end + local was_guessed = false + if not function_name or function_name == "?" then + -- for k,v in pairs(info) do print(k,v, type(v)) end + function_name = GuessFunctionName(info) + was_guessed = true + end + -- test if we have a file name + local function_type = (info.namewhat == "") and "function" or info.namewhat + if info.source and info.source:sub(1, 1) == "@" then + dumper:add_f("(%d) Lua %s '%s' at file '%s:%d'%s\r\n", level_to_show, function_type, function_name, + info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") + elseif info.source and info.source:sub(1, 1) == '#' then + dumper:add_f("(%d) Lua %s '%s' at template '%s:%d'%s\r\n", level_to_show, function_type, + function_name, info.source:sub(2), info.currentline, was_guessed and " (best guess)" or "") + elseif info.source and info.source:sub(1, 1) == "=" then + local str = info.source:sub(3, -2) + local props = {} + -- Split by space + for v in string.gmatch(str, "[^%s]+") do + table.insert(props, v) + end + local source = table.remove(props, 1) + if source == "love" then + dumper:add_f("(%d) LÖVE %s at file '%s:%d'%s\r\n", level_to_show, function_type, + table.concat(props, " "):sub(2, -2), info.currentline, was_guessed and " (best guess)" or "") + elseif source == "SMODS" then + local modID = table.remove(props, 1) + local fileName = table.concat(props, " ") + if modID == '_' then + dumper:add_f("(%d) Lua %s '%s' at Steamodded file '%s:%d' %s\r\n", level_to_show, + function_type, function_name, fileName:sub(2, -2), info.currentline, + was_guessed and " (best guess)" or "") + else + dumper:add_f("(%d) Lua %s '%s' at file '%s:%d' (from mod with id %s)%s\r\n", level_to_show, + function_type, function_name, fileName:sub(2, -2), info.currentline, modID, + was_guessed and " (best guess)" or "") + end + elseif source == "lovely" then + local module = table.remove(props, 1) + local fileName = table.concat(props, " ") + dumper:add_f("(%d) Lua %s '%s' at file '%s:%d' (from lovely module %s)%s\r\n", level_to_show, + function_type, function_name, fileName:sub(2, -2), info.currentline, module, + was_guessed and " (best guess)" or "") + else + dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type, + function_name, info.currentline, source) + end + else + dumper:add_f("(%d) Lua %s '%s' at line %d of chunk '%s'\r\n", level_to_show, function_type, + function_name, info.currentline, source) + end + dumper:DumpLocals(level) + else + dumper:add_f("(%d) unknown frame %s\r\n", level_to_show, info.what) + end + + level = level + 1 + level_to_show = level_to_show + 1 + info = dumper.getinfo(level, "nSlf") + end + + return dumper:concat_lines(), original_error + end + + -- + -- Adds a table to the list of known tables + function _M.add_known_table(tab, description) + if m_known_tables[tab] then + error("Cannot override an already known table") + end + m_user_known_tables[tab] = description + end + + -- + -- Adds a function to the list of known functions + function _M.add_known_function(fun, description) + if m_known_functions[fun] then + error("Cannot override an already known function") + end + m_user_known_functions[fun] = description + end + + return _M +end + +-- Note: The below code is not from the original StackTracePlus.lua +local stackTraceAlreadyInjected = false + +function getDebugInfoForCrash() + local version = VERSION + if not version or type(version) ~= "string" then + local versionFile = love.filesystem.read("version.jkr") + if versionFile then + version = versionFile:match("[^\n]*") .. " (best guess)" + else + version = "???" + end + end + local modded_version = MODDED_VERSION + if not modded_version or type(modded_version) ~= "string" then + local moddedSuccess, reqVersion = pcall(require, "SMODS.version") + if moddedSuccess and type(reqVersion) == "string" then + modded_version = reqVersion + else + modded_version = "???" + end + end + + local info = "Additional Context:\nBalatro Version: " .. version .. "\nModded Version: " .. + (modded_version) + local major, minor, revision, codename = love.getVersion() + info = info .. string.format("\nLÖVE Version: %d.%d.%d", major, minor, revision) + + local lovely_success, lovely = pcall(require, "lovely") + if lovely_success then + info = info .. "\nLovely Version: " .. lovely.version + end + if SMODS and SMODS.Mods then + local mod_strings = "" + local lovely_strings = "" + local i = 1 + local lovely_i = 1 + for _, v in pairs(SMODS.Mods) do + if (v.can_load and (not v.meta_mod or v.lovely_only)) or (v.lovely and not v.can_load and not v.disabled) then + if v.lovely_only or (v.lovely and not v.can_load) then + lovely_strings = lovely_strings .. "\n " .. lovely_i .. ": " .. v.name + lovely_i = lovely_i + 1 + if not v.can_load then + lovely_strings = lovely_strings .. "\n Has Steamodded mod that failed to load." + if #v.load_issues.dependencies > 0 then + lovely_strings = lovely_strings .. "\n Missing Dependencies:" + for k, v in ipairs(v.load_issues.dependencies) do + lovely_strings = lovely_strings .. "\n " .. k .. ". " .. v + end + end + if #v.load_issues.conflicts > 0 then + lovely_strings = lovely_strings .. "\n Conflicts:" + for k, v in ipairs(v.load_issues.conflicts) do + lovely_strings = lovely_strings .. "\n " .. k .. ". " .. v + end + end + if v.load_issues.outdated then + lovely_strings = lovely_strings .. "\n Outdated Mod." + end + if v.load_issues.main_file_not_found then + lovely_strings = lovely_strings .. "\n Main file not found. (" .. v.main_file ..")" + end + end + else + mod_strings = mod_strings .. "\n " .. i .. ": " .. v.name .. " by " .. + table.concat(v.author, ", ") .. " [ID: " .. v.id .. + (v.priority ~= 0 and (", Priority: " .. v.priority) or "") .. + (v.version and v.version ~= '0.0.0' and (", Version: " .. v.version) or "") .. + (v.lovely and (", Uses Lovely") or "") .. "]" + i = i + 1 + local debugInfo = v.debug_info + if debugInfo then + if type(debugInfo) == "string" then + if #debugInfo ~= 0 then + mod_strings = mod_strings .. "\n " .. debugInfo + end + elseif type(debugInfo) == "table" then + for kk, vv in pairs(debugInfo) do + if type(vv) ~= 'nil' then + vv = tostring(vv) + end + if #vv ~= 0 then + mod_strings = mod_strings .. "\n " .. kk .. ": " .. vv + end + end + end + end + end + end + end + info = info .. "\nSteamodded Mods:" .. mod_strings .. "\nLovely Mods:" .. lovely_strings + end + return info +end + +function injectStackTrace() + if (stackTraceAlreadyInjected) then + return + end + stackTraceAlreadyInjected = true + local STP = loadStackTracePlus() + local utf8 = require("utf8") + + -- Modifed from https://love2d.org/wiki/love.errorhandler + function love.errorhandler(msg) + msg = tostring(msg) + + if not sendErrorMessage then + function sendErrorMessage(msg) + print(msg) + end + end + if not sendInfoMessage then + function sendInfoMessage(msg) + print(msg) + end + end + + sendErrorMessage("Oops! The game crashed\n" .. STP.stacktrace(msg), 'StackTrace') + + if not love.window or not love.graphics or not love.event then + return + end + + if not love.graphics.isCreated() or not love.window.isOpen() then + local success, status = pcall(love.window.setMode, 800, 600) + if not success or not status then + return + end + end + + -- Reset state. + if love.mouse then + love.mouse.setVisible(true) + love.mouse.setGrabbed(false) + love.mouse.setRelativeMode(false) + if love.mouse.isCursorSupported() then + love.mouse.setCursor() + end + end + if love.joystick then + -- Stop all joystick vibrations. + for i, v in ipairs(love.joystick.getJoysticks()) do + v:setVibration() + end + end + if love.audio then + love.audio.stop() + end + + love.graphics.reset() + local font = love.graphics.setNewFont("resources/fonts/m6x11plus.ttf", 20) + + local background = {0, 0, 1} + if G and G.C and G.C.BLACK then + background = G.C.BLACK + end + love.graphics.clear(background) + love.graphics.origin() + + local trace = STP.stacktrace("", 3) + + local sanitizedmsg = {} + for char in msg:gmatch(utf8.charpattern) do + table.insert(sanitizedmsg, char) + end + sanitizedmsg = table.concat(sanitizedmsg) + + local err = {} + + table.insert(err, "Oops! The game crashed:") + if sanitizedmsg:find("Syntax error: game.lua:4: '=' expected near 'Game'") then + table.insert(err, + 'Duplicate installation of Steamodded detected! Please clean your installation: Steam Library > Balatro > Properties > Installed Files > Verify integrity of game files.') + else + table.insert(err, sanitizedmsg) + end + if #sanitizedmsg ~= #msg then + table.insert(err, "Invalid UTF-8 string in error message.") + end + + local success, msg = pcall(getDebugInfoForCrash) + if success and msg then + table.insert(err, '\n' .. msg) + sendInfoMessage(msg, 'StackTrace') + else + table.insert(err, "\n" .. "Failed to get additional context :/") + sendErrorMessage("Failed to get additional context :/\n" .. msg, 'StackTrace') + end + + for l in trace:gmatch("(.-)\n") do + table.insert(err, l) + end + + local p = table.concat(err, "\n") + + p = p:gsub("\t", "") + p = p:gsub("%[string \"(.-)\"%]", "%1") + + local scrollOffset = 0 + local endHeight = 0 + love.keyboard.setKeyRepeat(true) + + local function scrollDown(amt) + if amt == nil then + amt = 18 + end + scrollOffset = scrollOffset + amt + if scrollOffset > endHeight then + scrollOffset = endHeight + end + end + + local function scrollUp(amt) + if amt == nil then + amt = 18 + end + scrollOffset = scrollOffset - amt + if scrollOffset < 0 then + scrollOffset = 0 + end + end + + local pos = 70 + local arrowSize = 20 + + local function calcEndHeight() + local font = love.graphics.getFont() + local rw, lines = font:getWrap(p, love.graphics.getWidth() - pos * 2) + local lineHeight = font:getHeight() + local atBottom = scrollOffset == endHeight and scrollOffset ~= 0 + endHeight = #lines * lineHeight - love.graphics.getHeight() + pos * 2 + if (endHeight < 0) then + endHeight = 0 + end + if scrollOffset > endHeight or atBottom then + scrollOffset = endHeight + end + end + + local function draw() + if not love.graphics.isActive() then + return + end + love.graphics.clear(background) + calcEndHeight() + love.graphics.printf(p, pos, pos - scrollOffset, love.graphics.getWidth() - pos * 2) + if scrollOffset ~= endHeight then + love.graphics.polygon("fill", love.graphics.getWidth() - (pos / 2), + love.graphics.getHeight() - arrowSize, love.graphics.getWidth() - (pos / 2) + arrowSize, + love.graphics.getHeight() - (arrowSize * 2), love.graphics.getWidth() - (pos / 2) - arrowSize, + love.graphics.getHeight() - (arrowSize * 2)) + end + if scrollOffset ~= 0 then + love.graphics.polygon("fill", love.graphics.getWidth() - (pos / 2), arrowSize, + love.graphics.getWidth() - (pos / 2) + arrowSize, arrowSize * 2, + love.graphics.getWidth() - (pos / 2) - arrowSize, arrowSize * 2) + end + love.graphics.present() + end + + local fullErrorText = p + local function copyToClipboard() + if not love.system then + return + end + love.system.setClipboardText(fullErrorText) + p = p .. "\nCopied to clipboard!" + end + + p = p .. "\n\nPress ESC to exit\nPress R to restart the game" + if love.system then + p = p .. "\nPress Ctrl+C or tap to copy this error" + end + + if G then + -- Kill threads (makes restarting possible) + if G.SOUND_MANAGER and G.SOUND_MANAGER.channel then + G.SOUND_MANAGER.channel:push({ + type = 'kill' + }) + end + if G.SAVE_MANAGER and G.SAVE_MANAGER.channel then + G.SAVE_MANAGER.channel:push({ + type = 'kill' + }) + end + if G.HTTP_MANAGER and G.HTTP_MANAGER.channel then + G.HTTP_MANAGER.channel:push({ + type = 'kill' + }) + end + end + + return function() + love.event.pump() + + for e, a, b, c in love.event.poll() do + if e == "quit" then + return 1 + elseif e == "keypressed" and a == "escape" then + return 1 + elseif e == "keypressed" and a == "c" and love.keyboard.isDown("lctrl", "rctrl") then + copyToClipboard() + elseif e == "keypressed" and a == "r" then + SMODS.restart_game() + elseif e == "keypressed" and a == "down" then + scrollDown() + elseif e == "keypressed" and a == "up" then + scrollUp() + elseif e == "keypressed" and a == "pagedown" then + scrollDown(love.graphics.getHeight()) + elseif e == "keypressed" and a == "pageup" then + scrollUp(love.graphics.getHeight()) + elseif e == "keypressed" and a == "home" then + scrollOffset = 0 + elseif e == "keypressed" and a == "end" then + scrollOffset = endHeight + elseif e == "wheelmoved" then + scrollUp(b * 20) + elseif e == "gamepadpressed" and b == "dpdown" then + scrollDown() + elseif e == "gamepadpressed" and b == "dpup" then + scrollUp() + elseif e == "gamepadpressed" and b == "a" then + return "restart" + elseif e == "gamepadpressed" and b == "x" then + copyToClipboard() + elseif e == "gamepadpressed" and (b == "b" or b == "back" or b == "start") then + return 1 + elseif e == "touchpressed" then + local name = love.window.getTitle() + if #name == 0 or name == "Untitled" then + name = "Game" + end + local buttons = {"OK", "Cancel", "Restart"} + if love.system then + buttons[4] = "Copy to clipboard" + end + local pressed = love.window.showMessageBox("Quit " .. name .. "?", "", buttons) + if pressed == 1 then + return 1 + elseif pressed == 3 then + return "restart" + elseif pressed == 4 then + copyToClipboard() + end + end + end + + draw() + + if love.timer then + love.timer.sleep(0.1) + end + end + + end +end + +injectStackTrace() + +-- ---------------------------------------------- +-- --------MOD CORE API STACKTRACE END----------- + + +local Cartomancer_replacements = { + { + find = [[ + for k, v in ipairs%(G%.playing_cards%) do + if v%.base%.suit then table%.insert%(SUITS%[v%.base%.suit%], v%) end]], + -- Steamodded<0917b + find_alt = [[ + for k, v in ipairs%(G%.playing_cards%) do + table%.insert%(SUITS%[v%.base%.suit%], v%)]], + place = [[ +local SUITS_SORTED = Cartomancer.tablecopy(SUITS) +for k, v in ipairs(G.playing_cards) do + if v.base.suit then + local greyed + if unplayed_only and not ((v.area and v.area == G.deck) or v.ability.wheel_flipped) then + greyed = true + end + local card_string = v:cart_to_string() + if greyed then + card_string = card_string .. "Greyed" -- for some reason format doesn't work and final string is `sGreyed` + end + if greyed and Cartomancer.SETTINGS.deck_view_hide_drawn_cards then + -- Ignore this card. + elseif not Cartomancer.SETTINGS.deck_view_stack_enabled then + -- Don't stack cards + local _scale = 0.7 + local copy = copy_card(v, nil, _scale) + + copy.greyed = greyed + copy.stacked_quantity = 1 + table.insert(SUITS_SORTED[v.base.suit], copy) + + elseif not SUITS[v.base.suit][card_string] then + -- Initiate stack + table.insert(SUITS_SORTED[v.base.suit], card_string) + + local _scale = 0.7 + local copy = copy_card(v, nil, _scale) + + copy.greyed = greyed + copy.stacked_quantity = 1 + + SUITS[v.base.suit][card_string] = copy + else + -- Stack cards + local stacked_card = SUITS[v.base.suit][card_string] + stacked_card.stacked_quantity = stacked_card.stacked_quantity + 1 + end + end]] + }, + + { + find = "card_limit = #SUITS%[suit_map%[j%]%],", + place = "card_limit = #SUITS_SORTED[suit_map[j]]," + }, + + { + find = [[ +for i = 1%, %#SUITS%[suit_map%[j%]%] do + if SUITS%[suit_map%[j%]%]%[i%] then + local greyed%, _scale = nil%, 0%.7 + if unplayed_only and not %(%(SUITS%[suit_map%[j%]%]%[i%]%.area and SUITS%[suit_map%[j%]%]%[i%]%.area == G%.deck%) or SUITS%[suit_map%[j%]%]%[i%]%.ability%.wheel_flipped%) then + greyed = true + end + local copy = copy_card%(SUITS%[suit_map%[j%]%]%[i%]%, nil%, _scale%) + copy%.greyed = greyed + copy%.T%.x = view_deck%.T%.x %+ view_deck%.T%.w %/ 2 + copy%.T%.y = view_deck%.T%.y + + copy:hard_set_T%(%) + view_deck:emplace%(copy%) + end + end]], + place = [[ +for i = 1%, %#SUITS_SORTED%[suit_map%[j%]%] do + local card + if not Cartomancer.SETTINGS.deck_view_stack_enabled then + card = SUITS_SORTED%[suit_map%[j%]%]%[i%] + else + local card_string = SUITS_SORTED%[suit_map%[j%]%]%[i%] + card = SUITS%[suit_map%[j%]%]%[card_string%] + end + + card%.T%.x = view_deck%.T%.x %+ view_deck%.T%.w%/2 + card%.T%.y = view_deck%.T%.y + card:create_quantity_display%(%) + + card:hard_set_T%(%) + view_deck:emplace%(card%) +end]] + }, + + { + find = ' modded and {n = G.UIT.R, config = {align = "cm"}, nodes = {', + place = [=[ + not unplayed_only and Cartomancer.add_unique_count() or nil, + modded and {n = G.UIT.R, config = {align = "cm"}, nodes = {]=] + }, + +} + + +-- Mom, can we have lovely patches for overrides.lua? +-- No, we have lovely patches at home + +-- Lovely patches at home: + +local Cartomancer_nfs_read +local Cartomancer_nfs_read_override = function (containerOrName, nameOrSize, sizeOrNil) + local data, size = Cartomancer_nfs_read(containerOrName, nameOrSize, sizeOrNil) + + if type(containerOrName) ~= "string" then + return data, size + end + local overrides = '/overrides.lua' + if containerOrName:sub(-#overrides) ~= overrides then + return data, size + end + + local replaced = 0 + local total_replaced = 0 + for _, v in ipairs(Cartomancer_replacements) do + data, replaced = string.gsub(data, v.find, v.place) + + if replaced == 0 and v.find_alt then + data, replaced = string.gsub(data, v.find_alt, v.place) + end + + if replaced == 0 then + print("Failed to replace " .. v.find .. " for overrides.lua") + else + total_replaced = total_replaced + 1 + end + end + + print("Totally applied " .. total_replaced .. " replacements to overrides.lua") + + -- We no longer need this override + NFS.read = Cartomancer_nfs_read + + return data, size +end + +if (love.system.getOS() == 'OS X' ) and (jit.arch == 'arm64' or jit.arch == 'arm') then jit.off() end +do + local logger = require("debugplus.logger") + logger.registerLogHandler() +end +require "engine/object" +require "bit" +require "engine/string_packer" +require "engine/controller" +require "back" +require "tag" +require "engine/event" +require "engine/node" +require "engine/moveable" +require "engine/sprite" +require "engine/animatedsprite" +require "functions/misc_functions" +require "game" +require "globals" +require "engine/ui" +require "functions/UI_definitions" +require "functions/state_events" +require "functions/common_events" +require "functions/button_callbacks" +require "functions/misc_functions" +require "functions/test_functions" +require "card" +require "cardarea" +require "blind" +require "card_character" +require "engine/particles" +require "engine/text" +require "challenges" + +math.randomseed( G.SEED ) + +function love.run() + if love.load then love.load(love.arg.parseGameArguments(arg), arg) end + + -- We don't want the first frame's dt to include time taken by love.load. + if love.timer then love.timer.step() end + + local dt = 0 + local dt_smooth = 1/100 + local run_time = 0 + + -- Main loop time. + return function() + run_time = love.timer.getTime() + -- Process events. + if love.event and G and G.CONTROLLER then + love.event.pump() + local _n,_a,_b,_c,_d,_e,_f,touched + for name, a,b,c,d,e,f in love.event.poll() do + if name == "quit" then + if not love.quit or not love.quit() then + return a or 0 + end + end + if name == 'touchpressed' then + touched = true + elseif name == 'mousepressed' then + _n,_a,_b,_c,_d,_e,_f = name,a,b,c,d,e,f + else + love.handlers[name](a,b,c,d,e,f) + end + end + if _n then + love.handlers['mousepressed'](_a,_b,_c,touched) + end + end + + -- Update dt, as we'll be passing it to update + if love.timer then dt = love.timer.step() end + dt_smooth = math.min(0.8*dt_smooth + 0.2*dt, 0.1) + -- Call update and draw + if love.update then love.update(dt_smooth) end -- will pass 0 if love.timer is disabled + + if love.graphics and love.graphics.isActive() then + if love.draw then love.draw() end + love.graphics.present() + end + + run_time = math.min(love.timer.getTime() - run_time, 0.1) + G.FPS_CAP = G.FPS_CAP or 500 + if run_time < 1./G.FPS_CAP then love.timer.sleep(1./G.FPS_CAP - run_time) end + end +end + +Cryptid = {} +Cryptid.memepack = {} +Cryptid.aliases = {} +Cryptid.food = {} +Cryptid.M_jokers = {} +Cryptid.Megavouchers = {} +function love.load() + G:start_up() + --Steam integration + local os = love.system.getOS() + if os == 'OS X' or os == 'Windows' then + local st = nil + --To control when steam communication happens, make sure to send updates to steam as little as possible + if os == 'OS X' then + local dir = love.filesystem.getSourceBaseDirectory() + local old_cpath = package.cpath + package.cpath = package.cpath .. ';' .. dir .. '/?.so' + st = require 'luasteam' + package.cpath = old_cpath + else + st = require 'luasteam' + end + + st.send_control = { + last_sent_time = -200, + last_sent_stage = -1, + force = false, + } + if not (st.init and st:init()) then + st = nil + end + --Set up the render window and the stage for the splash screen, then enter the gameloop with :update + G.STEAM = st + else + end + + --Set the mouse to invisible immediately, this visibility is handled in the G.CONTROLLER + love.mouse.setVisible(false) +end + +function love.quit() + --Steam integration + if G.SOUND_MANAGER then G.SOUND_MANAGER.channel:push({type = 'stop'}) end + if G.STEAM then G.STEAM:shutdown() end +end + +function love.update( dt ) + --Perf monitoring checkpoint + timer_checkpoint(nil, 'update', true) + G:update(dt) +end + +function love.draw() + --Perf monitoring checkpoint + timer_checkpoint(nil, 'draw', true) + G:draw() + do + local console = require("debugplus.console") + console.doConsoleRender() + timer_checkpoint('DebugPlus Console', 'draw') + end +end + +function love.keypressed(key) +if Handy.controller.process_key(key, false) then return end +local console = require("debugplus.console") +if not console.consoleHandleKey(key) then return end + if not _RELEASE_MODE and G.keybind_mapping[key] then love.gamepadpressed(G.CONTROLLER.keyboard_controller, G.keybind_mapping[key]) + else + G.CONTROLLER:set_HID_flags('mouse') + G.CONTROLLER:key_press(key) + end +end + +function love.keyreleased(key) +if Handy.controller.process_key(key, true) then return end + if not _RELEASE_MODE and G.keybind_mapping[key] then love.gamepadreleased(G.CONTROLLER.keyboard_controller, G.keybind_mapping[key]) + else + G.CONTROLLER:set_HID_flags('mouse') + G.CONTROLLER:key_release(key) + end +end + +function love.gamepadpressed(joystick, button) + button = G.button_mapping[button] or button + G.CONTROLLER:set_gamepad(joystick) + G.CONTROLLER:set_HID_flags('button', button) + G.CONTROLLER:button_press(button) +end + +function love.gamepadreleased(joystick, button) + button = G.button_mapping[button] or button + G.CONTROLLER:set_gamepad(joystick) + G.CONTROLLER:set_HID_flags('button', button) + G.CONTROLLER:button_release(button) +end + +function love.mousepressed(x, y, button, touch) +if not touch and Handy.controller.process_mouse(button, false) then return end + G.CONTROLLER:set_HID_flags(touch and 'touch' or 'mouse') + if button == 1 then + G.CONTROLLER:queue_L_cursor_press(x, y) + end + if button == 2 then + G.CONTROLLER:queue_R_cursor_press(x, y) + end +end + + +function love.mousereleased(x, y, button) +if Handy.controller.process_mouse(button, true) then return end + if button == 1 then G.CONTROLLER:L_cursor_release(x, y) end +end + +function love.mousemoved(x, y, dx, dy, istouch) + G.CONTROLLER.last_touch_time = G.CONTROLLER.last_touch_time or -1 + if next(love.touch.getTouches()) ~= nil then + G.CONTROLLER.last_touch_time = G.TIMERS.UPTIME + end + G.CONTROLLER:set_HID_flags(G.CONTROLLER.last_touch_time > G.TIMERS.UPTIME - 0.2 and 'touch' or 'mouse') +end + +function love.joystickaxis( joystick, axis, value ) + if math.abs(value) > 0.2 and joystick:isGamepad() then + G.CONTROLLER:set_gamepad(joystick) + G.CONTROLLER:set_HID_flags('axis') + end +end + +if false then + if G.F_NO_ERROR_HAND then return end + msg = tostring(msg) + + if G.SETTINGS.crashreports and _RELEASE_MODE and G.F_CRASH_REPORTS then + local http_thread = love.thread.newThread([[ + local https = require('https') + CHANNEL = love.thread.getChannel("http_channel") + + while true do + --Monitor the channel for any new requests + local request = CHANNEL:demand() + if request then + https.request(request) + end + end + ]]) + local http_channel = love.thread.getChannel('http_channel') + http_thread:start() + local httpencode = function(str) + local char_to_hex = function(c) + return string.format("%%%02X", string.byte(c)) + end + str = str:gsub("\n", "\r\n"):gsub("([^%w _%%%-%.~])", char_to_hex):gsub(" ", "+") + return str + end + + + local error = msg + local file = string.sub(msg, 0, string.find(msg, ':')) + local function_line = string.sub(msg, string.len(file)+1) + function_line = string.sub(function_line, 0, string.find(function_line, ':')-1) + file = string.sub(file, 0, string.len(file)-1) + local trace = debug.traceback() + local boot_found, func_found = false, false + for l in string.gmatch(trace, "(.-)\n") do + if string.match(l, "boot.lua") then + boot_found = true + elseif boot_found and not func_found then + func_found = true + trace = '' + function_line = string.sub(l, string.find(l, 'in function')+12)..' line:'..function_line + end + + if boot_found and func_found then + trace = trace..l..'\n' + end + end + + http_channel:push('https://958ha8ong3.execute-api.us-east-2.amazonaws.com/?error='..httpencode(error)..'&file='..httpencode(file)..'&function_line='..httpencode(function_line)..'&trace='..httpencode(trace)..'&version='..(G.VERSION)) + end + + if not love.window or not love.graphics or not love.event then + return + end + + if not love.graphics.isCreated() or not love.window.isOpen() then + local success, status = pcall(love.window.setMode, 800, 600) + if not success or not status then + return + end + end + + -- Reset state. + if love.mouse then + love.mouse.setVisible(true) + love.mouse.setGrabbed(false) + love.mouse.setRelativeMode(false) + end + if love.joystick then + -- Stop all joystick vibrations. + for i,v in ipairs(love.joystick.getJoysticks()) do + v:setVibration() + end + end + if love.audio then love.audio.stop() end + love.graphics.reset() + local font = love.graphics.setNewFont("resources/fonts/m6x11plus.ttf", 20) + + love.graphics.clear(G.C.BLACK) + love.graphics.origin() + + + local p = 'Oops! Something went wrong:\n'..msg..'\n\n'..(not _RELEASE_MODE and debug.traceback() or G.SETTINGS.crashreports and + 'Since you are opted in to sending crash reports, LocalThunk HQ was sent some useful info about what happened.\nDon\'t worry! There is no identifying or personal information. If you would like\nto opt out, change the \'Crash Report\' setting to Off' or + 'Crash Reports are set to Off. If you would like to send crash reports, please opt in in the Game settings.\nThese crash reports help us avoid issues like this in the future') + + local function draw() + local pos = love.window.toPixels(70) + love.graphics.push() + love.graphics.clear(G.C.BLACK) + love.graphics.setColor(1., 1., 1., 1.) + love.graphics.printf(p, font, pos, pos, love.graphics.getWidth() - pos) + love.graphics.pop() + love.graphics.present() + + end + + while true do + love.event.pump() + + for e, a, b, c in love.event.poll() do + if e == "quit" then + return + elseif e == "keypressed" and a == "escape" then + return + elseif e == "touchpressed" then + local name = love.window.getTitle() + if #name == 0 or name == "Untitled" then name = "Game" end + local buttons = {"OK", "Cancel"} + local pressed = love.window.showMessageBox("Quit "..name.."?", "", buttons) + if pressed == 1 then + return + end + end + end + + draw() + + if love.timer then + love.timer.sleep(0.1) + end + end + +end + +function love.resize(w, h) + if w/h < 1 then --Dont allow the screen to be too square, since pop in occurs above and below screen + h = w/1 + end + + --When the window is resized, this code resizes the Canvas, then places the 'room' or gamearea into the middle without streching it + if w/h < G.window_prev.orig_ratio then + G.TILESCALE = G.window_prev.orig_scale*w/G.window_prev.w + else + G.TILESCALE = G.window_prev.orig_scale*h/G.window_prev.h + end + + if G.ROOM then + G.ROOM.T.w = G.TILE_W + G.ROOM.T.h = G.TILE_H + G.ROOM_ATTACH.T.w = G.TILE_W + G.ROOM_ATTACH.T.h = G.TILE_H + + if w/h < G.window_prev.orig_ratio then + G.ROOM.T.x = G.ROOM_PADDING_W + G.ROOM.T.y = (h/(G.TILESIZE*G.TILESCALE) - (G.ROOM.T.h+G.ROOM_PADDING_H))/2 + G.ROOM_PADDING_H/2 + else + G.ROOM.T.y = G.ROOM_PADDING_H + G.ROOM.T.x = (w/(G.TILESIZE*G.TILESCALE) - (G.ROOM.T.w+G.ROOM_PADDING_W))/2 + G.ROOM_PADDING_W/2 + end + + G.ROOM_ORIG = { + x = G.ROOM.T.x, + y = G.ROOM.T.y, + r = G.ROOM.T.r + } + + if G.buttons then G.buttons:recalculate() end + if G.HUD then G.HUD:recalculate() end + end + + G.WINDOWTRANS = { + x = 0, y = 0, + w = G.TILE_W+2*G.ROOM_PADDING_W, + h = G.TILE_H+2*G.ROOM_PADDING_H, + real_window_w = w, + real_window_h = h + } + + G.CANV_SCALE = 1 + + if love.system.getOS() == 'Windows' and false then --implement later if needed + local render_w, render_h = love.window.getDesktopDimensions(G.SETTINGS.WINDOW.selcted_display) + local unscaled_dims = love.window.getFullscreenModes(G.SETTINGS.WINDOW.selcted_display)[1] + + local DPI_scale = math.floor((0.5*unscaled_dims.width/render_w + 0.5*unscaled_dims.height/render_h)*500 + 0.5)/500 + + if DPI_scale > 1.1 then + G.CANV_SCALE = 1.5 + + G.AA_CANVAS = love.graphics.newCanvas(G.WINDOWTRANS.real_window_w*G.CANV_SCALE, G.WINDOWTRANS.real_window_h*G.CANV_SCALE, {type = '2d', readable = true}) + G.AA_CANVAS:setFilter('linear', 'linear') + else + G.AA_CANVAS = nil + end + end + + G.CANVAS = love.graphics.newCanvas(w*G.CANV_SCALE, h*G.CANV_SCALE, {type = '2d', readable = true}) + G.CANVAS:setFilter('linear', 'linear') +end + +Handy = setmetatable({ + last_clicked_area = nil, + last_clicked_card = nil, + + utils = {}, +}, {}) + +--- @generic T +--- @generic S +--- @param target T +--- @param source S +--- @param ... any +--- @return T | S +function Handy.utils.table_merge(target, source, ...) + assert(type(target) == "table", "Target is not a table") + local tables_to_merge = { source, ... } + if #tables_to_merge == 0 then + return target + end + + for k, t in ipairs(tables_to_merge) do + assert(type(t) == "table", string.format("Expected a table as parameter %d", k)) + end + + for i = 1, #tables_to_merge do + local from = tables_to_merge[i] + for k, v in pairs(from) do + if type(k) == "number" then + table.insert(target, v) + elseif type(k) == "string" then + if type(v) == "table" then + target[k] = target[k] or {} + target[k] = Handy.utils.table_merge(target[k], v) + else + target[k] = v + end + end + end + end + + return target +end + +function Handy.utils.table_contains(t, value) + for i = #t, 1, -1 do + if t[i] and t[i] == value then + return true + end + end + return false +end + +-- + +Handy.config = { + default = { + notifications_level = 3, + + insta_highlight = { + enabled = true, + }, + insta_buy_or_sell = { + enabled = true, + key_1 = "Shift", + key_2 = nil, + }, + insta_use = { + enabled = true, + key_1 = "Ctrl", + key_2 = nil, + }, + move_highlight = { + enabled = true, + + swap = { + enabled = true, + key_1 = "Shift", + key_2 = nil, + }, + to_end = { + enabled = true, + key_1 = "Ctrl", + key_2 = nil, + }, + + dx = { + one_left = { + enabled = true, + key_1 = "Left", + key_2 = nil, + }, + one_right = { + enabled = true, + key_1 = "Right", + key_2 = nil, + }, + }, + }, + + insta_cash_out = { + enabled = true, + key_1 = "Enter", + key_2 = nil, + }, + insta_booster_skip = { + enabled = true, + key_1 = "Enter", + key_2 = nil, + }, + + dangerous_actions = { + enabled = false, + + immediate_buy_and_sell = { + enabled = true, + key_1 = "Middle Mouse", + key_2 = nil, + + queue = { + enabled = false, + }, + }, + + nopeus_unsafe = { + enabled = true, + }, + }, + + speed_multiplier = { + enabled = true, + + key_1 = "Alt", + key_2 = nil, + }, + + shop_reroll = { + enabled = true, + key_1 = "Q", + key_2 = nil, + }, + play_and_discard = { + enabled = true, + play = { + enabled = true, + key_1 = nil, + key_2 = nil, + }, + discard = { + enabled = true, + key_1 = nil, + key_2 = nil, + }, + }, + + nopeus_interaction = { + enabled = true, + + key_1 = "]", + key_2 = nil, + }, + + not_just_yet_interaction = { + enabled = true, + key_1 = "Enter", + key_2 = nil, + }, + }, + current = {}, + + save = function() + if Handy.current_mod then + Handy.current_mod.config = Handy.config.current + SMODS.save_mod_config(Handy.current_mod) + end + end, +} +Handy.config.current = Handy.utils.table_merge({}, Handy.config.default) + +-- + +Handy.fake_events = { + check = function(arg) + local fake_event = { + UIBox = arg.UIBox, + config = { + ref_table = arg.card, + button = arg.button, + id = arg.id, + }, + } + arg.func(fake_event) + return fake_event.config.button ~= nil, fake_event.config.button + end, + execute = function(arg) + if type(arg.func) == "function" then + arg.func({ + UIBox = arg.UIBox, + config = { + ref_table = arg.card, + button = arg.button, + id = arg.id, + }, + }) + end + end, +} +Handy.controller = { + bind_module = nil, + bind_key = nil, + bind_button = nil, + + update_bind_button_text = function(text) + local button_text = Handy.controller.bind_button.children[1].children[1] + button_text.config.text_drawable = nil + button_text.config.text = text + button_text:update_text() + button_text.UIBox:recalculate() + end, + init_bind = function(button) + button.config.button = nil + Handy.controller.bind_button = button + Handy.controller.bind_module = button.config.ref_table.module + Handy.controller.bind_key = button.config.ref_table.key + + Handy.controller.update_bind_button_text( + "[" .. (Handy.controller.bind_module[Handy.controller.bind_key] or "None") .. "]" + ) + end, + complete_bind = function(key) + Handy.controller.bind_module[Handy.controller.bind_key] = key + Handy.controller.update_bind_button_text(key or "None") + + Handy.controller.bind_button.config.button = "handy_init_keybind_change" + Handy.controller.bind_button = nil + Handy.controller.bind_module = nil + Handy.controller.bind_key = nil + end, + cancel_bind = function() + Handy.controller.update_bind_button_text(Handy.controller.bind_module[Handy.controller.bind_key] or "None") + + Handy.controller.bind_button.config.button = "handy_init_keybind_change" + Handy.controller.bind_button = nil + Handy.controller.bind_module = nil + Handy.controller.bind_key = nil + end, + + process_bind = function(key) + if not Handy.controller.bind_button then + return false + end + local parsed_key = Handy.controller.parse(key) + if parsed_key == "Escape" then + parsed_key = nil + end + Handy.controller.complete_bind(parsed_key) + Handy.config.save() + return true + end, + + parse_table = { + ["mouse1"] = "Left Mouse", + ["mouse2"] = "Right Mouse", + ["mouse3"] = "Middle Mouse", + ["mouse4"] = "Mouse 4", + ["mouse5"] = "Mouse 5", + ["wheelup"] = "Wheel Up", + ["wheeldown"] = "Wheel Down", + ["lshift"] = "Shift", + ["rshift"] = "Shift", + ["lctrl"] = "Ctrl", + ["rctrl"] = "Ctrl", + ["lalt"] = "Alt", + ["ralt"] = "Alt", + ["lgui"] = "GUI", + ["rgui"] = "GUI", + ["return"] = "Enter", + ["kpenter"] = "Enter", + ["pageup"] = "Page Up", + ["pagedown"] = "Page Down", + ["numlock"] = "Num Lock", + ["capslock"] = "Caps Lock", + ["scrolllock"] = "Scroll Lock", + }, + resolve_table = { + ["Left Mouse"] = { "mouse1" }, + ["Right Mouse"] = { "mouse2" }, + ["Middle Mouse"] = { "mouse3" }, + ["Mouse 4"] = { "mouse4" }, + ["Mouse 5"] = { "mouse5" }, + ["Wheel Up"] = { "wheelup" }, + ["Wheel Down"] = { "wheeldown" }, + ["Shift"] = { "lshift", "rshift" }, + ["Ctrl"] = { "lctrl", "rctrl" }, + ["Alt"] = { "lalt", "ralt" }, + ["GUI"] = { "lgui", "rgui" }, + ["Enter"] = { "return", "kpenter" }, + ["Page Up"] = { "pageup" }, + ["Page Down"] = { "pagedown" }, + ["Num Lock"] = { "numlock" }, + ["Caps Lock"] = { "capslock" }, + ["Scroll Lock"] = { "scrolllock" }, + }, + + mouse_to_key_table = { + [1] = "mouse1", + [2] = "mouse2", + [3] = "mouse3", + [4] = "mouse4", + [5] = "mouse5", + }, + wheel_to_key_table = { + [1] = "wheelup", + [2] = "wheeldown", + }, + + mouse_buttons = { + ["Left Mouse"] = 1, + ["Right Mouse"] = 2, + ["Middle Mouse"] = 3, + ["Mouse 4"] = 4, + ["Mouse 5"] = 5, + }, + wheel_buttons = { + ["Wheel Up"] = 1, + ["Wheel Down"] = 2, + }, + + parse = function(raw_key) + if not raw_key then + return nil + end + if Handy.controller.parse_table[raw_key] then + return Handy.controller.parse_table[raw_key] + elseif string.sub(raw_key, 1, 2) == "kp" then + return "NUM " .. string.sub(raw_key, 3) + else + return string.upper(string.sub(raw_key, 1, 1)) .. string.sub(raw_key, 2) + end + end, + resolve = function(parsed_key) + if not parsed_key then + return nil + end + if Handy.controller.resolve_table[parsed_key] then + return unpack(Handy.controller.resolve_table[parsed_key]) + elseif string.sub(parsed_key, 1, 4) == "NUM " then + return "kp" .. string.sub(parsed_key, 5) + else + local str = string.gsub(string.lower(parsed_key), "%s+", "") + return str + end + end, + is_down = function(...) + local parsed_keys = { ... } + for i = 1, #parsed_keys do + local parsed_key = parsed_keys[i] + if parsed_key and parsed_key ~= "Unknown" then + if Handy.controller.wheel_buttons[parsed_key] then + -- Well, skip + elseif Handy.controller.mouse_buttons[parsed_key] then + if love.mouse.isDown(Handy.controller.mouse_buttons[parsed_key]) then + return true + end + else + local success, is_down = pcall(function() + return love.keyboard.isDown(Handy.controller.resolve(parsed_key)) + end) + if success and is_down then + return true + end + end + end + end + return false + end, + is = function(raw_key, ...) + if not raw_key then + return false + end + local parsed_keys = { ... } + for i = 1, #parsed_keys do + local parsed_key = parsed_keys[i] + if parsed_key then + local resolved_key_1, resolved_key_2 = Handy.controller.resolve(parsed_key) + if raw_key and raw_key ~= "Unknown" and (raw_key == resolved_key_1 or raw_key == resolved_key_2) then + return true + end + end + end + return false + end, + + is_module_key_down = function(module) + return module and module.enabled and Handy.controller.is_down(module.key_1, module.key_2) + end, + is_module_key = function(module, raw_key) + return module and module.enabled and Handy.controller.is(raw_key, module.key_1, module.key_2) + end, + + process_key = function(key, released) + if not released then + if Handy.controller.process_bind(key) then + return true + end + + Handy.move_highlight.use(key) + Handy.speed_multiplier.use(key) + Handy.shop_reroll.use(key) + Handy.play_and_discard.use(key) + end + Handy.insta_booster_skip.use(key, released) + Handy.insta_cash_out.use(key, released) + Handy.not_just_yet_interaction.use(key, released) + Handy.dangerous_actions.toggle_queue(key, released) + Handy.UI.state_panel.update(key, released) + return false + end, + process_mouse = function(mouse, released) + local key = Handy.controller.mouse_to_key_table[mouse] + if not released then + if Handy.controller.process_bind(key) then + return true + end + + Handy.move_highlight.use(key) + Handy.speed_multiplier.use(key) + Handy.shop_reroll.use(key) + Handy.play_and_discard.use(key) + end + Handy.insta_booster_skip.use(key, released) + Handy.insta_cash_out.use(key, released) + Handy.not_just_yet_interaction.use(key, released) + Handy.dangerous_actions.toggle_queue(key, released) + Handy.UI.state_panel.update(key, released) + return false + end, + process_wheel = function(wheel) + local key = Handy.controller.wheel_to_key_table[wheel] + + if Handy.controller.process_bind(key) then + return true + end + + Handy.move_highlight.use(key) + Handy.speed_multiplier.use(key) + Handy.nopeus_interaction.use(key) + Handy.shop_reroll.use(key) + Handy.play_and_discard.use(key) + Handy.UI.state_panel.update(key, false) + end, + process_card_click = function(card) + if Handy.insta_actions.use(card) then + return true + end + Handy.last_clicked_card = card + Handy.last_clicked_area = card.area + return false + end, + process_card_hover = function(card) + if Handy.insta_highlight.use(card) then + return true + end + if Handy.dangerous_actions.use(card) then + return true + end + return false + end, + process_update = function(dt) + Handy.insta_booster_skip.update() + Handy.insta_cash_out.update() + Handy.not_just_yet_interaction.update() + Handy.UI.update(dt) + end, +} + +-- + +Handy.insta_cash_out = { + is_hold = false, + + is_skipped = false, + is_button_created = false, + dollars = nil, + + can_execute = function(check) + if check then + return not not ( + Handy.insta_cash_out.is_hold + and G.STAGE == G.STAGES.RUN + and Handy.insta_cash_out.is_skipped + and not G.SETTINGS.paused + and G.round_eval + ) + else + return not not ( + Handy.insta_cash_out.is_hold + and G.STAGE == G.STAGES.RUN + and not Handy.insta_cash_out.is_skipped + and Handy.insta_cash_out.dollars + and not G.SETTINGS.paused + and G.round_eval + ) + end + end, + execute = function() + Handy.insta_cash_out.is_skipped = true + + if Handy.insta_cash_out.is_button_created then + G.GAME.current_round.dollars = Handy.insta_cash_out.dollars + Handy.insta_cash_out.dollars = nil + end + G.E_MANAGER:add_event(Event({ + trigger = "immediate", + func = function() + G.FUNCS.cash_out({ + config = { + id = "cash_out_button", + }, + }) + return true + end, + })) + return true + end, + + use = function(key, released) + if Handy.controller.is_module_key(Handy.config.current.insta_cash_out, key) then + Handy.insta_cash_out.is_hold = not released + end + return false + end, + + update = function() + if not Handy.config.current.insta_cash_out.enabled then + Handy.insta_cash_out.is_hold = false + end + return Handy.insta_cash_out.can_execute() and Handy.insta_cash_out.execute() or false + end, + + update_state_panel = function(state, key, released) + -- if G.STAGE ~= G.STAGES.RUN then + -- return false + -- end + -- if Handy.config.current.notifications_level < 4 then + -- return false + -- end + -- if Handy.insta_cash_out.can_execute(true) then + -- state.items.insta_cash_out = { + -- text = "Skip Cash Out", + -- hold = false, + -- order = 10, + -- } + -- return true + -- end + -- return false + end, +} + +Handy.insta_booster_skip = { + is_hold = false, + is_skipped = false, + + can_execute = function(check) + if check then + return not not ( + Handy.insta_booster_skip.is_hold + and G.STAGE == G.STAGES.RUN + and not G.SETTINGS.paused + and G.booster_pack + ) + end + return not not ( + Handy.insta_booster_skip.is_hold + and not Handy.insta_booster_skip.is_skipped + and G.STAGE == G.STAGES.RUN + and not G.SETTINGS.paused + and G.booster_pack + and Handy.fake_events.check({ + func = G.FUNCS.can_skip_booster, + }) + ) + end, + execute = function() + Handy.insta_booster_skip.is_skipped = true + G.E_MANAGER:add_event(Event({ + func = function() + G.FUNCS.skip_booster() + return true + end, + })) + return true + end, + + use = function(key, released) + if Handy.controller.is_module_key(Handy.config.current.insta_booster_skip, key) then + Handy.insta_booster_skip.is_hold = not released + end + return false + end, + + update = function() + if not Handy.config.current.insta_booster_skip.enabled then + Handy.insta_booster_skip.is_hold = false + end + return Handy.insta_booster_skip.can_execute() and Handy.insta_booster_skip.execute() or false + end, + + update_state_panel = function(state, key, released) + if G.STAGE ~= G.STAGES.RUN then + return false + end + if Handy.config.current.notifications_level < 4 then + return false + end + if Handy.insta_booster_skip.can_execute(true) then + state.items.insta_booster_skip = { + text = "Skip Booster Packs", + hold = Handy.insta_booster_skip.is_hold, + order = 10, + } + return true + end + return false + end, +} + +Handy.insta_highlight = { + can_execute = function(card) + return G.STAGE == G.STAGES.RUN + and Handy.config.current.insta_highlight.enabled + and card + and card.area == G.hand + -- TODO: fix it + and not next(love.touch.getTouches()) + and love.mouse.isDown(1) + and not card.highlighted + end, + execute = function(card) + card.area:add_to_highlighted(card) + return false + end, + + use = function(card) + return Handy.insta_highlight.can_execute(card) and Handy.insta_highlight.execute(card) or false + end, + + update_state_panel = function(state, key, released) end, +} + +Handy.insta_actions = { + get_actions = function() + return { + buy_or_sell = Handy.controller.is_module_key_down(Handy.config.current.insta_buy_or_sell), + use = Handy.controller.is_module_key_down(Handy.config.current.insta_use), + } + end, + can_execute = function(card, buy_or_sell, use) + return not not (G.STAGE == G.STAGES.RUN and (buy_or_sell or use) and card and card.area) + end, + execute = function(card, buy_or_sell, use, only_sell) + local target_button = nil + local is_shop_button = false + local is_custom_button = false + local is_playable_consumeable = false + + local base_background = G.UIDEF.card_focus_ui(card) + local base_attach = base_background:get_UIE_by_ID("ATTACH_TO_ME").children + local card_buttons = G.UIDEF.use_and_sell_buttons(card) + local result_funcs = {} + for _, node in ipairs(card_buttons.nodes) do + if node.config and node.config.func then + result_funcs[node.config.func] = node + end + end + local is_booster_pack_card = (G.pack_cards and card.area == G.pack_cards) and not card.ability.consumeable + + if use then + if card.area == G.hand and card.ability.consumeable then + local success, playale_consumeable_button = pcall(function() + -- G.UIDEF.use_and_sell_buttons(G.hand.highlighted[1]).nodes[1].nodes[2].nodes[1].nodes[1] + return card_buttons.nodes[1].nodes[2].nodes[1].nodes[1] + end) + if success and playale_consumeable_button then + target_button = playale_consumeable_button + is_custom_button = true + is_playable_consumeable = true + end + elseif result_funcs.can_select_alchemical or result_funcs.can_select_crazy_card then + -- Prevent cards to be selected when usage is required: + -- Alchemical cards, Cines + else + target_button = base_attach.buy_and_use + or (not is_booster_pack_card and base_attach.use) + or card.children.buy_and_use_button + is_shop_button = target_button == card.children.buy_and_use_button + end + elseif buy_or_sell then + target_button = card.children.buy_button + or result_funcs.can_select_crazy_card -- Cines + or result_funcs.can_select_alchemical -- Alchemical cards + or result_funcs.can_use_mupack -- Multipacks + or result_funcs.can_reserve_card -- Code cards, for example + or base_attach.buy + or base_attach.redeem + or base_attach.sell + or (is_booster_pack_card and base_attach.use) + + if only_sell and target_button ~= base_attach.sell then + target_button = nil + end + is_shop_button = target_button == card.children.buy_button + end + + if target_button and not is_custom_button and not is_shop_button then + for _, node in ipairs(card_buttons.nodes) do + if target_button == node then + is_custom_button = true + end + end + end + + local target_button_UIBox + local target_button_definition + + local cleanup = function() + base_background:remove() + if target_button_UIBox and is_custom_button then + target_button_UIBox:remove() + end + end + + if target_button then + if is_playable_consumeable then + card.area:add_to_highlighted(card) + if not card.highlighted then + cleanup() + return false + end + end + + target_button_UIBox = (is_custom_button and UIBox({ + definition = target_button, + config = {}, + })) or target_button + target_button_definition = (is_custom_button and target_button) + or (is_shop_button and target_button.definition) + or target_button.definition.nodes[1] + + local check, button = Handy.fake_events.check({ + func = G.FUNCS[target_button_definition.config.func], + button = nil, + id = target_button_definition.config.id, + card = card, + UIBox = target_button_UIBox, + }) + if check then + Handy.fake_events.execute({ + func = G.FUNCS[button or target_button_definition.config.button], + button = nil, + id = target_button_definition.config.id, + card = card, + UIBox = target_button_UIBox, + }) + cleanup() + return true + end + end + + cleanup() + return false + end, + + use = function(card) + if card.ability and card.ability.handy_dangerous_actions_used then + return true + end + + local actions = Handy.insta_actions.get_actions() + + return Handy.insta_actions.can_execute(card, actions.buy_or_sell, actions.use) + and Handy.insta_actions.execute(card, actions.buy_or_sell, actions.use) + or false + end, + + update_state_panel = function(state, key, released) + if G.STAGE ~= G.STAGES.RUN then + return false + end + if Handy.config.current.notifications_level < 4 then + return false + end + local result = false + local actions = Handy.insta_actions.get_actions() + if actions.use then + state.items.insta_use = { + text = "Quick use", + hold = true, + order = 10, + } + result = true + end + if actions.buy_or_sell then + state.items.quick_buy_and_sell = { + text = "Quick buy and sell", + hold = true, + order = 11, + } + result = true + end + return result + end, +} + +Handy.move_highlight = { + dx = { + one_left = -1, + one_right = 1, + }, + + get_dx = function(key, area) + for module_key, module in pairs(Handy.config.current.move_highlight.dx) do + if Handy.controller.is_module_key(module, key) then + return Handy.move_highlight.dx[module_key] + end + end + return nil + end, + get_actions = function(key, area) + return { + swap = Handy.controller.is_module_key_down(Handy.config.current.move_highlight.swap), + to_end = Handy.controller.is_module_key_down(Handy.config.current.move_highlight.to_end), + } + end, + + can_swap = function(key, area) + if not area then + return false + end + return not Handy.utils.table_contains({ + G.pack_cards, + G.shop_jokers, + G.shop_booster, + G.shop_vouchers, + }, area) + end, + cen_execute = function(key, area) + return not not ( + Handy.config.current.move_highlight.enabled + and G.STAGE == G.STAGES.RUN + and area + and area.highlighted + and area.highlighted[1] + and Handy.utils.table_contains({ + G.consumeables, + G.jokers, + G.cine_quests, + G.pack_cards, + G.shop_jokers, + G.shop_booster, + G.shop_vouchers, + }, area) + ) + end, + execute = function(key, area) + local dx = Handy.move_highlight.get_dx(key, area) + if not dx then + return false + end + + local current_card = area.highlighted[1] + for current_index = #area.cards, 1, -1 do + if area.cards[current_index] == current_card then + local actions = Handy.move_highlight.get_actions(key, area) + local next_index = actions.to_end and (dx > 0 and #area.cards or 1) + or ((#area.cards + current_index + dx - 1) % #area.cards) + 1 + if current_index == next_index then + return + end + local next_card = area.cards[next_index] + if not next_card then + return + end + if actions.swap and Handy.move_highlight.can_swap(key, area) then + if actions.to_end or next_index == 1 or next_index == #area.cards then + table.remove(area.cards, current_index) + table.insert(area.cards, next_index, current_card) + else + area.cards[next_index] = current_card + area.cards[current_index] = next_card + end + else + area:remove_from_highlighted(current_card) + area:add_to_highlighted(next_card) + end + return + end + end + end, + + use = function(key, area) + area = area or Handy.last_clicked_area + return Handy.move_highlight.cen_execute(key, area) and Handy.move_highlight.execute(key, area) or false + end, + + update_state_panel = function(state, key, released) end, +} + +Handy.dangerous_actions = { + sell_queue = {}, + + sell_next_card = function() + local card = table.remove(Handy.dangerous_actions.sell_queue, 1) + if not card then + stop_use() + return + end + + G.GAME.STOP_USE = 0 + Handy.insta_actions.execute(card, true, false, true) + + G.E_MANAGER:add_event(Event({ + blocking = false, + func = function() + if card.ability then + card.ability.handy_dangerous_actions_used = nil + end + return true + end, + })) + Handy.dangerous_actions.sell_next_card() + end, + + can_execute = function(card) + return G.STAGE == G.STAGES.RUN + and Handy.config.current.dangerous_actions.enabled + and card + and not (card.ability and card.ability.handy_dangerous_actions_used) + end, + execute = function(card) + if Handy.controller.is_module_key_down(Handy.config.current.dangerous_actions.immediate_buy_and_sell) then + if Handy.config.current.dangerous_actions.immediate_buy_and_sell.queue.enabled then + if not card.ability then + card.ability = {} + end + card.ability.handy_dangerous_actions_used = true + + table.insert(Handy.dangerous_actions.sell_queue, card) + Handy.UI.state_panel.update(nil, nil) + return false + else + local result = Handy.insta_actions.execute(card, true, false) + if result then + if not card.ability then + card.ability = {} + end + card.ability.handy_dangerous_actions_used = true + + G.CONTROLLER.locks.selling_card = nil + G.CONTROLLER.locks.use = nil + G.GAME.STOP_USE = 0 + + G.E_MANAGER:add_event(Event({ + func = function() + if card.ability then + card.ability.handy_dangerous_actions_used = nil + end + return true + end, + })) + end + return result + end + end + return false + end, + + use = function(card) + return Handy.dangerous_actions.can_execute(card) and Handy.dangerous_actions.execute(card) or false + end, + + toggle_queue = function(key, released) + if Handy.controller.is_module_key(Handy.config.current.dangerous_actions.immediate_buy_and_sell, key) then + if released then + Handy.dangerous_actions.sell_next_card() + else + Handy.dangerous_actions.sell_queue = {} + end + end + end, + + update_state_panel = function(state, key, released) + if G.STAGE ~= G.STAGES.RUN then + return false + end + + if not Handy.config.current.dangerous_actions.enabled then + return false + end + if Handy.config.current.notifications_level < 2 then + return false + end + if Handy.controller.is_module_key_down(Handy.config.current.dangerous_actions.immediate_buy_and_sell) then + state.dangerous = true + state.items.dangerous_hint = { + text = "[Unsafe] Bugs can appear!", + dangerous = true, + hold = true, + order = 99999999, + } + if state.items.quick_buy_and_sell then + state.items.quick_buy_and_sell.dangerous = true + elseif Handy.insta_actions.get_actions().buy_or_sell then + local text = "Quick sell" + if Handy.config.current.dangerous_actions.immediate_buy_and_sell.queue.enabled then + text = text .. " [" .. #Handy.dangerous_actions.sell_queue .. " in queue]" + end + state.items.quick_buy_and_sell = { + text = text, + hold = true, + order = 11, + dangerous = true, + } + end + return true + end + return false + end, +} + +Handy.speed_multiplier = { + value = 1, + + get_actions = function(key) + return { + multiply = key == Handy.controller.wheel_to_key_table[1], + divide = key == Handy.controller.wheel_to_key_table[2], + } + end, + can_execute = function(key) + return Handy.config.current.speed_multiplier.enabled + and not G.OVERLAY_MENU + and Handy.controller.is_module_key_down(Handy.config.current.speed_multiplier) + end, + + execute = function(key) + local actions = Handy.speed_multiplier.get_actions(key) + if actions.multiply then + Handy.speed_multiplier.multiply() + end + if actions.divide then + Handy.speed_multiplier.divide() + end + return false + end, + + multiply = function() + Handy.speed_multiplier.value = math.min(512, Handy.speed_multiplier.value * 2) + end, + divide = function() + Handy.speed_multiplier.value = math.max(0.001953125, Handy.speed_multiplier.value / 2) + end, + + use = function(key) + return Handy.speed_multiplier.can_execute(key) and Handy.speed_multiplier.execute(key) or false + end, + + update_state_panel = function(state, key, released) + if not key or not Handy.speed_multiplier.can_execute(key) then + return false + end + if Handy.config.current.notifications_level < 3 then + return false + end + + local actions = Handy.speed_multiplier.get_actions(key) + + if actions.multiply or actions.divide then + state.items.change_speed_multiplier = { + text = "Game speed multiplier: " + .. ( + Handy.speed_multiplier.value >= 1 and Handy.speed_multiplier.value + or ("1/" .. (1 / Handy.speed_multiplier.value)) + ), + hold = false, + order = 5, + } + return true + end + return false + end, +} + +Handy.shop_reroll = { + can_execute = function(key) + return G.STATE == G.STATES.SHOP + and Handy.fake_events.check({ func = G.FUNCS.can_reroll, button = "reroll_shop" }) + and Handy.controller.is_module_key(Handy.config.current.shop_reroll, key) + end, + execute = function(key) + G.FUNCS.reroll_shop() + return false + end, + + use = function(key) + return Handy.shop_reroll.can_execute(key) and Handy.shop_reroll.execute(key) or false + end, +} + +Handy.play_and_discard = { + get_actions = function(key) + return { + discard = Handy.controller.is_module_key(Handy.config.current.play_and_discard.discard, key), + play = Handy.controller.is_module_key(Handy.config.current.play_and_discard.play, key), + } + end, + + can_execute = function(play, discard) + return not not ( + Handy.config.current.play_and_discard.enabled + and G.STATE == G.STATES.SELECTING_HAND + and ( + (discard and Handy.fake_events.check({ + func = G.FUNCS.can_discard, + })) or (play and Handy.fake_events.check({ + func = G.FUNCS.can_play, + })) + ) + ) + end, + execute = function(play, discard) + if discard then + Handy.fake_events.execute({ + func = G.FUNCS.discard_cards_from_highlighted, + }) + elseif play then + Handy.fake_events.execute({ + func = G.FUNCS.play_cards_from_highlighted, + }) + end + return false + end, + + use = function(key) + local actions = Handy.play_and_discard.get_actions(key) + return Handy.play_and_discard.can_execute(actions.play, actions.discard) + and Handy.play_and_discard.execute(actions.play, actions.discard) + or false + end, +} + +Handy.nopeus_interaction = { + is_present = function() + return type(Nopeus) == "table" + end, + + get_actions = function(key) + return { + increase = key == Handy.controller.wheel_to_key_table[1], + decrease = key == Handy.controller.wheel_to_key_table[2], + } + end, + + can_dangerous = function() + return not not ( + Handy.config.current.dangerous_actions.enabled + and Handy.config.current.dangerous_actions.nopeus_unsafe.enabled + ) + end, + can_execute = function(key) + return not not ( + Handy.config.current.nopeus_interaction.enabled + and Handy.nopeus_interaction.is_present() + and not G.OVERLAY_MENU + and Handy.controller.is_module_key_down(Handy.config.current.nopeus_interaction) + ) + end, + execute = function(key) + local actions = Handy.nopeus_interaction.get_actions(key) + if actions.increase then + Handy.nopeus_interaction.increase() + end + if actions.decrease then + Handy.nopeus_interaction.decrease() + end + end, + + change = function(dx) + if not Handy.nopeus_interaction.is_present() then + G.SETTINGS.FASTFORWARD = 0 + elseif Nopeus.Optimised then + G.SETTINGS.FASTFORWARD = math.min( + Handy.nopeus_interaction.can_dangerous() and 4 or 3, + math.max(0, (G.SETTINGS.FASTFORWARD or 0) + dx) + ) + else + G.SETTINGS.FASTFORWARD = math.min( + Handy.nopeus_interaction.can_dangerous() and 3 or 2, + math.max(0, (G.SETTINGS.FASTFORWARD or 0) + dx) + ) + end + end, + increase = function() + Handy.nopeus_interaction.change(1) + end, + decrease = function() + Handy.nopeus_interaction.change(-1) + end, + + use = function(key) + return Handy.nopeus_interaction.can_execute(key) and Handy.nopeus_interaction.execute(key) or false + end, + + update_state_panel = function(state, key, released) + if not Handy.nopeus_interaction.is_present() then + return false + end + if not key or not Handy.nopeus_interaction.can_execute(key) then + return false + end + + local actions = Handy.nopeus_interaction.get_actions(key) + + if actions.increase or actions.decrease then + local states = { + Nopeus.Off, + Nopeus.Planets, + Nopeus.On, + Nopeus.Unsafe, + } + if Nopeus.Optimised then + states = { + Nopeus.Off, + Nopeus.Planets, + Nopeus.On, + Nopeus.Optimised, + Nopeus.Unsafe, + } + end + + local is_dangerous = G.SETTINGS.FASTFORWARD == (#states - 1) + + if is_dangerous then + state.dangerous = true + if Handy.config.current.notifications_level < 2 then + return false + end + else + if Handy.config.current.notifications_level < 3 then + return false + end + end + + state.items.change_nopeus_fastforward = { + text = "Nopeus fast-forward: " .. states[(G.SETTINGS.FASTFORWARD or 0) + 1], + hold = false, + order = 4, + dangerous = is_dangerous, + } + return true + end + return false + end, +} + +Handy.not_just_yet_interaction = { + is_present = function() + return G and G.FUNCS and G.FUNCS.njy_endround ~= nil + end, + + can_execute = function(check) + return not not ( + Handy.not_just_yet_interaction.is_present() + and GLOBAL_njy_vanilla_override + and G.STATE_COMPLETE + and G.buttons + and G.buttons.states + and G.buttons.states.visible + and G.GAME + and G.GAME.chips + and G.GAME.blind + and G.GAME.blind.chips + and to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) + ) + end, + execute = function() + stop_use() + G.STATE = G.STATES.NEW_ROUND + end_round() + end, + + use = function(key, released) + if Handy.controller.is_module_key(Handy.config.current.not_just_yet_interaction, key) then + GLOBAL_njy_vanilla_override = not released + end + return false + end, + + update = function() + if not Handy.config.current.not_just_yet_interaction.enabled then + GLOBAL_njy_vanilla_override = nil + end + return Handy.not_just_yet_interaction.can_execute() and Handy.not_just_yet_interaction.execute() or false + end, +} + +-- + +-- + +Handy.UI = { + counter = 1, + C = { + TEXT = HEX("FFFFFF"), + BLACK = HEX("000000"), + RED = HEX("FF0000"), + + DYN_BASE_APLHA = { + CONTAINER = 0.6, + + TEXT = 1, + TEXT_DANGEROUS = 1, + }, + + DYN = { + CONTAINER = HEX("000000"), + + TEXT = HEX("FFFFFF"), + TEXT_DANGEROUS = HEX("FFEEEE"), + }, + }, + state_panel = { + element = nil, + + title = nil, + items = nil, + + previous_state = { + dangerous = false, + title = {}, + items = {}, + sub_items = {}, + hold = false, + }, + current_state = { + dangerous = false, + title = {}, + items = {}, + sub_items = {}, + hold = false, + }, + + get_definition = function() + local state_panel = Handy.UI.state_panel + + local items_raw = {} + for _, item in pairs(state_panel.current_state.items) do + table.insert(items_raw, item) + end + + table.sort(items_raw, function(a, b) + return a.order < b.order + end) + + local items = {} + for _, item in ipairs(items_raw) do + table.insert(items, { + n = G.UIT.R, + config = { + align = "cm", + padding = 0.035, + }, + nodes = { + { + n = G.UIT.T, + config = { + text = item.text, + scale = 0.225, + colour = item.dangerous and Handy.UI.C.DYN.TEXT_DANGEROUS or Handy.UI.C.DYN.TEXT, + shadow = true, + }, + }, + }, + }) + end + + return { + n = G.UIT.ROOT, + config = { align = "cm", padding = 0.1, r = 0.1, colour = G.C.CLEAR, id = "handy_state_panel" }, + nodes = { + { + n = G.UIT.C, + config = { + align = "cm", + padding = 0.125, + r = 0.1, + colour = Handy.UI.C.DYN.CONTAINER, + }, + nodes = { + { + n = G.UIT.R, + config = { + align = "cm", + }, + nodes = { + { + n = G.UIT.T, + config = { + text = state_panel.current_state.title.text, + scale = 0.3, + colour = Handy.UI.C.DYN.TEXT, + shadow = true, + id = "handy_state_title", + }, + }, + }, + }, + { + n = G.UIT.R, + config = { + align = "cm", + }, + nodes = { + { + n = G.UIT.C, + config = { + align = "cm", + id = "handy_state_items", + }, + nodes = items, + }, + }, + }, + }, + }, + }, + } + end, + emplace = function() + if Handy.UI.state_panel.element then + Handy.UI.state_panel.element:remove() + end + local element = UIBox({ + definition = Handy.UI.state_panel.get_definition(), + config = { + instance_type = "ALERT", + align = "cm", + major = G.ROOM_ATTACH, + can_collide = false, + offset = { + x = 0, + y = 3.5, + }, + }, + }) + Handy.UI.state_panel.element = element + Handy.UI.state_panel.title = element:get_UIE_by_ID("handy_state_title") + Handy.UI.state_panel.items = element:get_UIE_by_ID("handy_state_items") + end, + + update = function(key, released) + local state_panel = Handy.UI.state_panel + + local state = { + dangerous = false, + title = {}, + items = {}, + sub_items = {}, + } + + local is_changed = false + + for _, part in ipairs({ + Handy.speed_multiplier, + Handy.insta_booster_skip, + Handy.insta_cash_out, + Handy.insta_actions, + Handy.insta_highlight, + Handy.move_highlight, + Handy.nopeus_interaction, + Handy.dangerous_actions, + }) do + local temp_result = part.update_state_panel(state, key, released) + is_changed = is_changed or temp_result or false + end + + if is_changed then + if state.dangerous then + state.title.text = "Dangerous actions" + else + state.title.text = "Quick actions" + end + + for _, item in pairs(state.items) do + if item.hold then + state.hold = true + end + end + + local color = Handy.UI.C.DYN.CONTAINER + local target_color = state.dangerous and Handy.UI.C.RED or Handy.UI.C.BLACK + color[1] = target_color[1] + color[2] = target_color[2] + color[3] = target_color[3] + + Handy.UI.counter = 0 + state_panel.previous_state = state_panel.current_state + state_panel.current_state = state + + state_panel.emplace() + else + state_panel.current_state.hold = false + end + end, + }, + + update = function(dt) + if Handy.UI.state_panel.current_state.hold then + Handy.UI.counter = 0 + elseif Handy.UI.counter < 1 then + Handy.UI.counter = Handy.UI.counter + dt + end + local multiplier = math.min(1, math.max(0, (1 - Handy.UI.counter) * 2)) + for key, color in pairs(Handy.UI.C.DYN) do + color[4] = (Handy.UI.C.DYN_BASE_APLHA[key] or 1) * multiplier + end + end, +} + +function Handy.UI.init() + Handy.UI.counter = 1 + Handy.UI.state_panel.emplace() + Handy.UI.update(0) +end + +-- + +local love_update_ref = love.update +function love.update(dt, ...) + love_update_ref(dt, ...) + Handy.controller.process_update(dt) +end + +local wheel_moved_ref = love.wheelmoved or function() end +function love.wheelmoved(x, y) + wheel_moved_ref(x, y) + Handy.controller.process_wheel(y > 0 and 1 or 2) +end + +-- + +function Handy.emplace_steamodded() + Handy.current_mod = SMODS.current_mod + Handy.config.current = Handy.utils.table_merge({}, Handy.config.default, SMODS.current_mod.config) + + Handy.current_mod.extra_tabs = function() + return { + { + label = "Overall", + tab_definition_function = function() + return Handy.UI.get_config_tab("Overall") + end, + }, + { + label = "Interactions", + tab_definition_function = function() + return Handy.UI.get_config_tab("Interactions") + end, + }, + { + label = "Dangerous", + tab_definition_function = function() + return Handy.UI.get_config_tab("Dangerous") + end, + }, + { + label = "Keybinds", + tab_definition_function = function() + return Handy.UI.get_config_tab("Keybinds") + end, + }, + { + label = "More keybinds", + tab_definition_function = function() + return Handy.UI.get_config_tab("Keybinds 2") + end, + }, + } + end + + G.E_MANAGER:add_event(Event({ + func = function() + G.njy_keybind = nil + return true + end, + })) +end + +function G.FUNCS.handy_toggle_module_enabled(arg, module) + if not module then + return + end + module.enabled = arg + if module == Handy.config.current.speed_multiplier then + Handy.speed_multiplier.value = 1 + elseif + module == Handy.config.current.dangerous_actions + or module == Handy.config.current.nopeus_interaction + or module == Handy.config.current.dangerous_actions.nopeus_unsafe + then + Handy.nopeus_interaction.change(0) + end + Handy.config.save() +end + +function G.FUNCS.handy_change_notifications_level(arg) + Handy.config.current.notifications_level = arg.to_key + Handy.config.save() +end + +function G.FUNCS.handy_init_keybind_change(e) + Handy.controller.init_bind(e) +end + +Handy.UI.PARTS = { + create_module_checkbox = function(module, label, text_prefix, text_lines, skip_keybinds) + local desc_lines = { + { n = G.UIT.R, config = { minw = 5.25 } }, + } + + if skip_keybinds then + table.insert(desc_lines, { + n = G.UIT.R, + config = { padding = 0.025 }, + nodes = { + { + n = G.UIT.T, + config = { + text = text_prefix .. " " .. text_lines[1], + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + }, + }) + else + local key_desc = module.key_2 + and { + { + n = G.UIT.T, + config = { + text = text_prefix .. " [", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + ref_table = module, + ref_value = "key_1", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + text = "] or [", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + ref_table = module, + ref_value = "key_2", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + text = "] " .. text_lines[1], + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + } + or { + { + n = G.UIT.T, + config = { + text = text_prefix .. " [", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + ref_table = module, + ref_value = "key_1", + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + { + n = G.UIT.T, + config = { + text = "] " .. text_lines[1], + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + } + table.insert(desc_lines, { + n = G.UIT.R, + config = { padding = 0.025 }, + nodes = key_desc, + }) + end + + for i = 2, #text_lines do + table.insert(desc_lines, { + n = G.UIT.R, + config = { padding = 0.025 }, + nodes = { + { + n = G.UIT.T, + config = { + text = text_lines[i], + scale = 0.3, + colour = G.C.TEXT_LIGHT, + }, + }, + }, + }) + end + + local label_lines = {} + if type(label) == "string" then + label = { label } + end + for i = 1, #label do + table.insert(label_lines, { + n = G.UIT.R, + config = { minw = 2.75 }, + nodes = { + { + n = G.UIT.T, + config = { + text = label[i], + scale = 0.4, + colour = G.C.WHITE, + }, + }, + }, + }) + end + + return { + n = G.UIT.R, + config = { align = "cm" }, + nodes = { + { + n = G.UIT.C, + config = { align = "cm" }, + nodes = label_lines, + }, + { + n = G.UIT.C, + config = { align = "cm" }, + nodes = { + create_toggle({ + callback = function(b) + return G.FUNCS.handy_toggle_module_enabled(b, module) + end, + label_scale = 0.4, + label = "", + ref_table = module, + ref_value = "enabled", + w = 0, + }), + }, + }, + { + n = G.UIT.C, + config = { minw = 0.1 }, + }, + { + n = G.UIT.C, + config = { align = "cm" }, + nodes = desc_lines, + }, + }, + } + end, + + create_module_section = function(label) + return { + n = G.UIT.R, + config = { align = "cm", padding = 0.1 }, + nodes = { + { + n = G.UIT.T, + config = { text = label, colour = G.C.WHITE, scale = 0.4, align = "cm" }, + }, + }, + } + end, + create_module_keybind = function(module, label, plus, dangerous) + return { + n = G.UIT.R, + config = { align = "cm", padding = 0.05 }, + nodes = { + { + n = G.UIT.C, + config = { align = "c", minw = 4 }, + nodes = { + { + n = G.UIT.T, + config = { text = label, colour = G.C.WHITE, scale = 0.35 }, + }, + }, + }, + { + n = G.UIT.C, + config = { align = "cm", minw = 0.75 }, + }, + UIBox_button({ + label = { module.key_1 or "None" }, + col = true, + colour = dangerous and G.C.MULT or G.C.CHIPS, + scale = 0.35, + minw = 2.75, + minh = 0.45, + ref_table = { + module = module, + key = "key_1", + }, + button = "handy_init_keybind_change", + }), + { + n = G.UIT.C, + config = { align = "cm", minw = 0.6 }, + nodes = { + { + n = G.UIT.T, + config = { text = plus and "+" or "or", colour = G.C.WHITE, scale = 0.3 }, + }, + }, + }, + UIBox_button({ + label = { module.key_2 or "None" }, + col = true, + colour = dangerous and G.C.MULT or G.C.CHIPS, + scale = 0.35, + minw = 2.75, + minh = 0.45, + ref_table = { + module = module, + key = "key_2", + }, + button = "handy_init_keybind_change", + }), + }, + } + end, +} + +Handy.UI.get_config_tab_overall = function() + return { + { + n = G.UIT.R, + config = { padding = 0.05, align = "cm" }, + nodes = { + create_option_cycle({ + minw = 3, + label = "Notifications level", + scale = 0.8, + options = { + "None", + "Dangerous", + "Game state", + "All", + }, + opt_callback = "handy_change_notifications_level", + current_option = Handy.config.current.notifications_level, + }), + }, + }, + { n = G.UIT.R, config = { padding = 0.05 }, nodes = {} }, + { + n = G.UIT.R, + nodes = { + { + n = G.UIT.C, + nodes = { + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.insta_highlight, + "Quick Highlight", + "Hold [Left Mouse]", + { + "and", + "hover cards in hand to highlight", + }, + true + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.insta_buy_or_sell, + "Quick Buy/Sell", + "Hold", + { + "to", + "buy or sell card on Left-Click", + "instead of selection", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox(Handy.config.current.insta_use, "Quick use", "Hold", { + "to", + "use (if possible) card on Left-Click", + "instead of selection", + "(overrides Quick Buy/Sell)", + }), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.move_highlight, + "Move highlight", + "Press", + { + "[" + .. tostring(Handy.config.current.move_highlight.dx.one_left.key_1) + .. "] or [" + .. tostring(Handy.config.current.move_highlight.dx.one_right.key_1) + .. "]", + "to move highlight in card area.", + "Hold [" + .. tostring(Handy.config.current.move_highlight.swap.key_1) + .. "] to move card instead.", + "Hold [" + .. tostring(Handy.config.current.move_highlight.to_end.key_1) + .. "] to move to first/last card", + }, + true + ), + }, + }, + { + n = G.UIT.C, + config = { minw = 4 }, + nodes = { + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.insta_cash_out, + "Quick Cash Out", + "Press", + { + "to", + "speedup animation and", + "skip Cash Out stage", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.insta_booster_skip, + { "Quick skip", "Booster Packs" }, + "Hold", + { + "to", + "skip booster pack", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.speed_multiplier, + "Speed Multiplier", + "Hold", + { + "and", + "[Wheel Up] to multiply or", + "[Wheel Down] to divide game speed", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.shop_reroll, + "Shop Reroll", + "Press", + { + "to", + "reroll a shop", + } + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.play_and_discard, + "Play/Discard", + "Press", + { + "[" .. tostring(Handy.config.current.play_and_discard.play.key_1) .. "] to play a hand", + "or [" + .. tostring(Handy.config.current.play_and_discard.discard.key_1) + .. "] to discard", + }, + true + ), + }, + }, + }, + }, + } +end + +Handy.UI.get_config_tab_interactions = function() + return { + { + n = G.UIT.R, + nodes = { + { + n = G.UIT.C, + nodes = { + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.nopeus_interaction, + { "Nopeus:", "fast-forward" }, + "Hold", + { + "and", + "[Wheel Up] to increase or", + "[Wheel Down] to decrease", + "fast-forward setting", + } + ), + { + n = G.UIT.R, + config = { minh = 0.25 }, + }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.not_just_yet_interaction, + { "NotJustYet:", "End round" }, + "Press", + { + "to", + "end round", + } + ), + }, + }, + }, + }, + } +end + +Handy.UI.get_config_tab_dangerous = function() + return { + -- { + -- n = G.UIT.R, + -- config = { padding = 0.05, align = "cm" }, + -- nodes = { + + -- }, + -- }, + -- { n = G.UIT.R, config = { padding = 0.05 }, nodes = {} }, + { + n = G.UIT.R, + nodes = { + { + n = G.UIT.C, + nodes = { + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.dangerous_actions, + { "Dangerous", "actions" }, + "Enable", + { + "unsafe controls. They're", + "designed to be speed-first,", + "which can cause bugs or crashes", + }, + true + ), + { n = G.UIT.R, config = { minh = 0.5 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.dangerous_actions.immediate_buy_and_sell, + "Instant Sell", + "Hold", + { + "to", + "sell card on hover", + "very fast", + } + ), + { n = G.UIT.R, config = { minh = 0.1 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.dangerous_actions.immediate_buy_and_sell.queue, + "Sell Queue", + "Start", + { + "selling cards only when", + "keybind was released", + }, + true + ), + { n = G.UIT.R, config = { minh = 0.25 } }, + Handy.UI.PARTS.create_module_checkbox( + Handy.config.current.dangerous_actions.nopeus_unsafe, + { "Nopeus: Unsafe", "fast-forward" }, + "Allow", + { + "increase fast-forward", + 'setting to "Unsafe"', + }, + true + ), + }, + }, + }, + }, + } +end + +Handy.UI.get_config_tab_keybinds = function() + return { + Handy.UI.PARTS.create_module_section("Quick Actions"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.insta_buy_or_sell, "Quick Buy/Sell"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.insta_use, "Quick Use"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.insta_cash_out, "Quick Cash Out"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.insta_booster_skip, "Quick skip Booster Packs"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.shop_reroll, "Shop reroll"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.play_and_discard.play, "Play hand"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.play_and_discard.discard, "Discard"), + Handy.UI.PARTS.create_module_keybind( + Handy.config.current.dangerous_actions.immediate_buy_and_sell, + "Instant Buy/Sell", + false, + true + ), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.not_just_yet_interaction, "NotJustYet: End round"), + } +end + +Handy.UI.get_config_tab_keybinds_2 = function() + return { + Handy.UI.PARTS.create_module_section("Game state"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.speed_multiplier, "Speed Multiplier"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.nopeus_interaction, "Nopeus: fast-forward"), + Handy.UI.PARTS.create_module_section("Move highlight"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.move_highlight.dx.one_left, "Move one left"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.move_highlight.dx.one_right, "Move one right"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.move_highlight.swap, "Move card"), + Handy.UI.PARTS.create_module_keybind(Handy.config.current.move_highlight.to_end, "Move to end"), + } +end + +Handy.UI.get_config_tab = function(_tab) + local result = { + n = G.UIT.ROOT, + config = { align = "cm", padding = 0.05, colour = G.C.CLEAR, minh = 5, minw = 5 }, + nodes = {}, + } + if _tab == "Overall" then + result.nodes = Handy.UI.get_config_tab_overall() + elseif _tab == "Interactions" then + result.nodes = Handy.UI.get_config_tab_interactions() + elseif _tab == "Dangerous" then + result.nodes = Handy.UI.get_config_tab_dangerous() + elseif _tab == "Keybinds" then + result.nodes = Handy.UI.get_config_tab_keybinds() + elseif _tab == "Keybinds 2" then + result.nodes = Handy.UI.get_config_tab_keybinds_2() + end + return result +end + +--- STEAMODDED CORE +--- MODULE CORE + +SMODS = {} +MODDED_VERSION = require'SMODS.version' +SMODS.id = 'Steamodded' +SMODS.version = MODDED_VERSION:gsub('%-STEAMODDED', '') +SMODS.can_load = true +SMODS.meta_mod = true + +-- Include lovely and nativefs modules +local nativefs = require "nativefs" +local lovely = require "lovely" +local json = require "json" + +local lovely_mod_dir = lovely.mod_dir:gsub("/$", "") +NFS = nativefs +-- make lovely_mod_dir an absolute path. +-- respects symlink/.. combos +NFS.setWorkingDirectory(lovely_mod_dir) +lovely_mod_dir = NFS.getWorkingDirectory() +-- make sure NFS behaves the same as love.filesystem +NFS.setWorkingDirectory(love.filesystem.getSaveDirectory()) + +JSON = json + +local function set_mods_dir() + local love_dirs = { + love.filesystem.getSaveDirectory(), + love.filesystem.getSourceBaseDirectory() + } + for _, love_dir in ipairs(love_dirs) do + if lovely_mod_dir:sub(1, #love_dir) == love_dir then + -- relative path from love_dir + SMODS.MODS_DIR = lovely_mod_dir:sub(#love_dir+2) + if nfs_success then + -- make sure NFS behaves the same as love.filesystem. + -- not perfect: NFS won't read from both getSaveDirectory() + -- and getSourceBaseDirectory() + NFS.setWorkingDirectory(love_dir) + end + return + end + end + SMODS.MODS_DIR = lovely_mod_dir +end +set_mods_dir() + +local function find_self(directory, target_filename, target_line, depth) + depth = depth or 1 + if depth > 3 then return end + for _, filename in ipairs(NFS.getDirectoryItems(directory)) do + local file_path = directory .. "/" .. filename + local file_type = NFS.getInfo(file_path).type + if file_type == 'directory' or file_type == 'symlink' then + local f = find_self(file_path, target_filename, target_line, depth+1) + if f then return f end + elseif filename == target_filename then + local first_line = NFS.read(file_path):match('^(.-)\n') + if first_line == target_line then + -- use parent directory + return directory:match('^(.+/)') + end + end + end +end + +SMODS.path = find_self(SMODS.MODS_DIR, 'core.lua', '--- STEAMODDED CORE') + +Cartomancer_nfs_read = NFS.read +NFS.read = Cartomancer_nfs_read_override + + +for _, path in ipairs { + "src/ui.lua", + "src/index.lua", + "src/utils.lua", + "src/overrides.lua", + "src/game_object.lua", + "src/logging.lua", + "src/compat_0_9_8.lua", + "src/loader.lua", +} do + assert(load(NFS.read(SMODS.path..path), ('=[SMODS _ "%s"]'):format(path)))() +end + +local lovely = require("lovely") +local nativefs = require("nativefs") + +if not nativefs.getInfo(lovely.mod_dir .. "/Talisman") then + error( + 'Could not find proper Talisman folder.\nPlease make sure the folder for Talisman is named exactly "Talisman" and not "Talisman-main" or anything else.') +end + +Talisman = {config_file = {disable_anims = true, break_infinity = "omeganum", score_opt_id = 2}} +if nativefs.read(lovely.mod_dir.."/Talisman/config.lua") then + Talisman.config_file = STR_UNPACK(nativefs.read(lovely.mod_dir.."/Talisman/config.lua")) + + if Talisman.config_file.break_infinity and type(Talisman.config_file.break_infinity) ~= 'string' then + Talisman.config_file.break_infinity = "omeganum" + end +end +if not SMODS or not JSON then + local createOptionsRef = create_UIBox_options + function create_UIBox_options() + contents = createOptionsRef() + local m = UIBox_button({ + minw = 5, + button = "talismanMenu", + label = { + "Talisman" + }, + colour = G.C.GOLD + }) + table.insert(contents.nodes[1].nodes[1].nodes[1].nodes, #contents.nodes[1].nodes[1].nodes[1].nodes + 1, m) + return contents + end +end + +Talisman.config_tab = function() + tal_nodes = {{n=G.UIT.R, config={align = "cm"}, nodes={ + {n=G.UIT.O, config={object = DynaText({string = "Select features to enable:", colours = {G.C.WHITE}, shadow = true, scale = 0.4})}}, + }},create_toggle({label = "Disable Scoring Animations", ref_table = Talisman.config_file, ref_value = "disable_anims", + callback = function(_set_toggle) + nativefs.write(lovely.mod_dir .. "/Talisman/config.lua", STR_PACK(Talisman.config_file)) + end}), + create_option_cycle({ + label = "Score Limit (requires game restart)", + scale = 0.8, + w = 6, + options = {"Vanilla (e308)", "BigNum (ee308)", "OmegaNum (e10##1000)"}, + opt_callback = 'talisman_upd_score_opt', + current_option = Talisman.config_file.score_opt_id, + })} + return { + n = G.UIT.ROOT, + config = { + emboss = 0.05, + minh = 6, + r = 0.1, + minw = 10, + align = "cm", + padding = 0.2, + colour = G.C.BLACK + }, + nodes = tal_nodes + } + end +G.FUNCS.talismanMenu = function(e) + local tabs = create_tabs({ + snap_to_nav = true, + tabs = { + { + label = "Talisman", + chosen = true, + tab_definition_function = Talisman.config_tab + }, + }}) + G.FUNCS.overlay_menu{ + definition = create_UIBox_generic_options({ + back_func = "options", + contents = {tabs} + }), + config = {offset = {x=0,y=10}} + } +end +G.FUNCS.talisman_upd_score_opt = function(e) + Talisman.config_file.score_opt_id = e.to_key + local score_opts = {"", "bignumber", "omeganum"} + Talisman.config_file.break_infinity = score_opts[e.to_key] + nativefs.write(lovely.mod_dir .. "/Talisman/config.lua", STR_PACK(Talisman.config_file)) +end +if Talisman.config_file.break_infinity then + Big, err = nativefs.load(lovely.mod_dir.."/Talisman/big-num/"..Talisman.config_file.break_infinity..".lua") + if not err then Big = Big() else Big = nil end + Notations = nativefs.load(lovely.mod_dir.."/Talisman/big-num/notations.lua")() + -- We call this after init_game_object to leave room for mods that add more poker hands + Talisman.igo = function(obj) + for _, v in pairs(obj.hands) do + v.chips = to_big(v.chips) + v.mult = to_big(v.mult) + v.s_chips = to_big(v.s_chips) + v.s_mult = to_big(v.s_mult) + v.l_chips = to_big(v.l_chips) + v.l_mult = to_big(v.l_mult) + end + return obj + end + + local nf = number_format + function number_format(num, e_switch_point) + if type(num) == 'table' then + num = to_big(num) + G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 + if num < to_big(e_switch_point or G.E_SWITCH_POINT) then + return nf(num:to_number(), e_switch_point) + else + return Notations.Balatro:format(num, 3) + end + else return nf(num, e_switch_point) end + end + + local mf = math.floor + function math.floor(x) + if type(x) == 'table' then return x:floor() end + return mf(x) + end + + local l10 = math.log10 + function math.log10(x) + if type(x) == 'table' then return l10(math.min(x:to_number(),1e300)) end--x:log10() end + return l10(x) + end + + local lg = math.log + function math.log(x, y) + if not y then y = 2.718281828459045 end + if type(x) == 'table' then return lg(math.min(x:to_number(),1e300),y) end --x:log(y) end + return lg(x,y) + end + + if SMODS then + function SMODS.get_blind_amount(ante) + local k = to_big(0.75) + local scale = G.GAME.modifiers.scaling + local amounts = { + to_big(300), + to_big(700 + 100*scale), + to_big(1400 + 600*scale), + to_big(2100 + 2900*scale), + to_big(15000 + 5000*scale*math.log(scale)), + to_big(12000 + 8000*(scale+1)*(0.4*scale)), + to_big(10000 + 25000*(scale+1)*((scale/4)^2)), + to_big(50000 * (scale+1)^2 * (scale/7)^2) + } + + if ante < 1 then return to_big(100) end + if ante <= 8 then + local amount = amounts[ante] + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + end + local a, b, c, d = amounts[8], amounts[8]/amounts[7], ante-8, 1 + 0.2*(ante-8) + local amount = math.floor(a*(b + (b*k*c)^d)^c) + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + end + end + -- There's too much to override here so we just fully replace this function + -- Note that any ante scaling tweaks will need to manually changed... + local gba = get_blind_amount + function get_blind_amount(ante) + if G.GAME.modifiers.scaling and G.GAME.modifiers.scaling > 3 then return SMODS.get_blind_amount(ante) end + if type(to_big(1)) == 'number' then return gba(ante) end + local k = to_big(0.75) + if not G.GAME.modifiers.scaling or G.GAME.modifiers.scaling == 1 then + local amounts = { + to_big(300), to_big(800), to_big(2000), to_big(5000), to_big(11000), to_big(20000), to_big(35000), to_big(50000) + } + if ante < 1 then return to_big(100) end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = a*(b+(k*c)^d)^c + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + elseif G.GAME.modifiers.scaling == 2 then + local amounts = { + to_big(300), to_big(900), to_big(2600), to_big(8000), to_big(20000), to_big(36000), to_big(60000), to_big(100000) + --300, 900, 2400, 7000, 18000, 32000, 56000, 90000 + } + if ante < 1 then return to_big(100) end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = a*(b+(k*c)^d)^c + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + elseif G.GAME.modifiers.scaling == 3 then + local amounts = { + to_big(300), to_big(1000), to_big(3200), to_big(9000), to_big(25000), to_big(60000), to_big(110000), to_big(200000) + --300, 1000, 3000, 8000, 22000, 50000, 90000, 180000 + } + if ante < 1 then return to_big(100) end + if ante <= 8 then return amounts[ante] end + local a, b, c, d = amounts[8],1.6,ante-8, 1 + 0.2*(ante-8) + local amount = a*(b+(k*c)^d)^c + if (amount:lt(R.E_MAX_SAFE_INTEGER)) then + local exponent = to_big(10)^(math.floor(amount:log10() - to_big(1))):to_number() + amount = math.floor(amount / exponent):to_number() * exponent + end + amount:normalize() + return amount + end + end + + function check_and_set_high_score(score, amt) + if G.GAME.round_scores[score] and to_big(math.floor(amt)) > to_big(G.GAME.round_scores[score].amt) then + G.GAME.round_scores[score].amt = to_big(math.floor(amt)) + end + if G.GAME.seeded then return end + --[[if G.PROFILES[G.SETTINGS.profile].high_scores[score] and math.floor(amt) > G.PROFILES[G.SETTINGS.profile].high_scores[score].amt then + if G.GAME.round_scores[score] then G.GAME.round_scores[score].high_score = true end + G.PROFILES[G.SETTINGS.profile].high_scores[score].amt = math.floor(amt) + G:save_settings() + end--]] --going to hold off on modifying this until proper save loading exists + end + + local sn = scale_number + function scale_number(number, scale, max, e_switch_point) + if not Big then return sn(number, scale, max, e_switch_point) end + scale = to_big(scale) + G.E_SWITCH_POINT = G.E_SWITCH_POINT or 100000000000 + if not number or not is_number(number) then return scale end + if not max then max = 10000 end + if to_big(number).e and to_big(number).e == 10^1000 then + scale = scale*math.floor(math.log(max*10, 10))/7 + end + if to_big(number) >= to_big(e_switch_point or G.E_SWITCH_POINT) then + if (to_big(to_big(number):log10()) <= to_big(999)) then + scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.log(1000000*10, 10)) + else + scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.max(7,string.len(number_format(number))-1)) + end + elseif to_big(number) >= to_big(max) then + scale = scale*math.floor(math.log(max*10, 10))/math.floor(math.log(number*10, 10)) + end + return math.min(3, scale:to_number()) + end + + local tsj = G.FUNCS.text_super_juice + function G.FUNCS.text_super_juice(e, _amount) + if _amount > 2 then _amount = 2 end + return tsj(e, _amount) + end + + local max = math.max + --don't return a Big unless we have to - it causes nativefs to break + function math.max(x, y) + if type(x) == 'table' or type(y) == 'table' then + x = to_big(x) + y = to_big(y) + if (x > y) then + return x + else + return y + end + else return max(x,y) end + end + + local min = math.min + function math.min(x, y) + if type(x) == 'table' or type(y) == 'table' then + x = to_big(x) + y = to_big(y) + if (x < y) then + return x + else + return y + end + else return min(x,y) end + end + + local sqrt = math.sqrt + function math.sqrt(x) + if type(x) == 'table' then + if getmetatable(x) == BigMeta then return x:sqrt() end + if getmetatable(x) == OmegaMeta then return x:pow(0.5) end + end + return sqrt(x) + end + + + + local old_abs = math.abs + function math.abs(x) + if type(x) == 'table' then + x = to_big(x) + if (x < to_big(0)) then + return -1 * x + else + return x + end + else return old_abs(x) end + end +end + +function is_number(x) + if type(x) == 'number' then return true end + if type(x) == 'table' and ((x.e and x.m) or (x.array and x.sign)) then return true end + return false +end + +function to_big(x, y) + if Big and Big.m then + return Big:new(x,y) + elseif Big and Big.array then + local result = Big:create(x) + result.sign = y or result.sign or x.sign or 1 + return result + elseif is_number(x) then + return x * 10^(y or 0) + + elseif type(x) == "nil" then + return 0 + else + if ((#x>=2) and ((x[2]>=2) or (x[2]==1) and (x[1]>308))) then + return 1e309 + end + if (x[2]==1) then + return math.pow(10,x[1]) + end + return x[1]*(y or 1); + end +end +function to_number(x) + if type(x) == 'table' and (getmetatable(x) == BigMeta or getmetatable(x) == OmegaMeta) then + return x:to_number() + else + return x + end +end + +--patch to remove animations +local cest = card_eval_status_text +function card_eval_status_text(a,b,c,d,e,f) + if not Talisman.config_file.disable_anims then cest(a,b,c,d,e,f) end +end +local jc = juice_card +function juice_card(x) + if not Talisman.config_file.disable_anims then jc(x) end +end +function tal_uht(config, vals) + local col = G.C.GREEN + if vals.chips and G.GAME.current_round.current_hand.chips ~= vals.chips then + local delta = (is_number(vals.chips) and is_number(G.GAME.current_round.current_hand.chips)) and (vals.chips - G.GAME.current_round.current_hand.chips) or 0 + if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED + elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta) + else delta = number_format(delta) + end + if type(vals.chips) == 'string' then delta = vals.chips end + G.GAME.current_round.current_hand.chips = vals.chips + if G.hand_text_area.chips.config.object then + G.hand_text_area.chips:update(0) + end + end + if vals.mult and G.GAME.current_round.current_hand.mult ~= vals.mult then + local delta = (is_number(vals.mult) and is_number(G.GAME.current_round.current_hand.mult))and (vals.mult - G.GAME.current_round.current_hand.mult) or 0 + if to_big(delta) < to_big(0) then delta = number_format(delta); col = G.C.RED + elseif to_big(delta) > to_big(0) then delta = '+'..number_format(delta) + else delta = number_format(delta) + end + if type(vals.mult) == 'string' then delta = vals.mult end + G.GAME.current_round.current_hand.mult = vals.mult + if G.hand_text_area.mult.config.object then + G.hand_text_area.mult:update(0) + end + end + if vals.handname and G.GAME.current_round.current_hand.handname ~= vals.handname then + G.GAME.current_round.current_hand.handname = vals.handname + end + if vals.chip_total then G.GAME.current_round.current_hand.chip_total = vals.chip_total;G.hand_text_area.chip_total.config.object:pulse(0.5) end + if vals.level and G.GAME.current_round.current_hand.hand_level ~= ' '..localize('k_lvl')..tostring(vals.level) then + if vals.level == '' then + G.GAME.current_round.current_hand.hand_level = vals.level + else + G.GAME.current_round.current_hand.hand_level = ' '..localize('k_lvl')..tostring(vals.level) + if type(vals.level) == 'number' then + G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[math.min(vals.level, 7)] + else + G.hand_text_area.hand_level.config.colour = G.C.HAND_LEVELS[1] + end + end + end + return true +end +local uht = update_hand_text +function update_hand_text(config, vals) + if Talisman.config_file.disable_anims then + if G.latest_uht then + local chips = G.latest_uht.vals.chips + local mult = G.latest_uht.vals.mult + if not vals.chips then vals.chips = chips end + if not vals.mult then vals.mult = mult end + end + G.latest_uht = {config = config, vals = vals} + else uht(config, vals) + end +end +local upd = Game.update +function Game:update(dt) + upd(self, dt) + if G.latest_uht and G.latest_uht.config and G.latest_uht.vals then + tal_uht(G.latest_uht.config, G.latest_uht.vals) + G.latest_uht = nil + end + if Talisman.dollar_update then + G.HUD:get_UIE_by_ID('dollar_text_UI').config.object:update() + G.HUD:recalculate() + Talisman.dollar_update = false + end +end +--scoring coroutine +local oldplay = G.FUNCS.evaluate_play + +function G.FUNCS.evaluate_play() + G.SCORING_COROUTINE = coroutine.create(oldplay) + G.LAST_SCORING_YIELD = love.timer.getTime() + G.CARD_CALC_COUNTS = {} -- keys = cards, values = table containing numbers + local success, err = coroutine.resume(G.SCORING_COROUTINE) + if not success then + error(err) + end +end + + +local oldupd = love.update +function love.update(dt, ...) + oldupd(dt, ...) + if G.SCORING_COROUTINE then + if collectgarbage("count") > 1024*1024 then + collectgarbage("collect") + end + if coroutine.status(G.SCORING_COROUTINE) == "dead" then + G.SCORING_COROUTINE = nil + G.FUNCS.exit_overlay_menu() + local totalCalcs = 0 + for i, v in pairs(G.CARD_CALC_COUNTS) do + totalCalcs = totalCalcs + v[1] + end + G.GAME.LAST_CALCS = totalCalcs + else + G.SCORING_TEXT = nil + if not G.OVERLAY_MENU then + G.scoring_text = {"Calculating...", "", "", ""} + G.SCORING_TEXT = { + {n = G.UIT.C, nodes = { + {n = G.UIT.R, config = {padding = 0.1, align = "cm"}, nodes = { + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 1}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 1, silent = true})}}, + }},{n = G.UIT.R, nodes = { + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 2}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}}, + }},{n = G.UIT.R, nodes = { + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 3}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}}, + }},{n = G.UIT.R, nodes = { + {n=G.UIT.O, config={object = DynaText({string = {{ref_table = G.scoring_text, ref_value = 4}}, colours = {G.C.UI.TEXT_LIGHT}, shadow = true, pop_in = 0, scale = 0.4, silent = true})}}, + }}}}} + G.FUNCS.overlay_menu({ + definition = + {n=G.UIT.ROOT, minw = G.ROOM.T.w*5, minh = G.ROOM.T.h*5, config={align = "cm", padding = 9999, offset = {x = 0, y = -3}, r = 0.1, colour = {G.C.GREY[1], G.C.GREY[2], G.C.GREY[3],0.7}}, nodes= G.SCORING_TEXT}, + config = {align="cm", offset = {x=0,y=0}, major = G.ROOM_ATTACH, bond = 'Weak'} + }) + else + + if G.OVERLAY_MENU and G.scoring_text then + local totalCalcs = 0 + for i, v in pairs(G.CARD_CALC_COUNTS) do + totalCalcs = totalCalcs + v[1] + end + local jokersYetToScore = #G.jokers.cards + #G.play.cards - #G.CARD_CALC_COUNTS + G.scoring_text[1] = "Calculating..." + G.scoring_text[2] = "Elapsed calculations: "..tostring(totalCalcs) + G.scoring_text[3] = "Cards yet to score: "..tostring(jokersYetToScore) + G.scoring_text[4] = "Calculations last played hand: " .. tostring(G.GAME.LAST_CALCS or "Unknown") + end + + end + --this coroutine allows us to stagger GC cycles through + --the main source of waste in terms of memory (especially w joker retriggers) is through local variables that become garbage + --this practically eliminates the memory overhead of scoring + --event queue overhead seems to not exist if Talismans Disable Scoring Animations is off. + --event manager has to wait for scoring to finish until it can keep processing events anyways. + + + G.LAST_SCORING_YIELD = love.timer.getTime() + + local success, msg = coroutine.resume(G.SCORING_COROUTINE) + if not success then + error(msg) + end + end + end +end + + + +TIME_BETWEEN_SCORING_FRAMES = 0.03 -- 30 fps during scoring +-- we dont want overhead from updates making scoring much slower +-- originally 10 fps, I think 30 fps is a good way to balance it while making it look smooth, too +--wrap everything in calculating contexts so we can do more things with it +Talisman.calculating_joker = false +Talisman.calculating_score = false +Talisman.calculating_card = false +Talisman.dollar_update = false +local ccj = Card.calculate_joker +function Card:calculate_joker(context) + --scoring coroutine + G.CURRENT_SCORING_CARD = self + G.CARD_CALC_COUNTS = G.CARD_CALC_COUNTS or {} + if G.CARD_CALC_COUNTS[self] then + G.CARD_CALC_COUNTS[self][1] = G.CARD_CALC_COUNTS[self][1] + 1 + else + G.CARD_CALC_COUNTS[self] = {1, 1} + end + + + if G.LAST_SCORING_YIELD and ((love.timer.getTime() - G.LAST_SCORING_YIELD) > TIME_BETWEEN_SCORING_FRAMES) and coroutine.running() then + coroutine.yield() + end + Talisman.calculating_joker = true + local ret = ccj(self, context) + + if ret and type(ret) == "table" and ret.repetitions then + G.CARD_CALC_COUNTS[ret.card] = G.CARD_CALC_COUNTS[ret.card] or {1,1} + G.CARD_CALC_COUNTS[ret.card][2] = G.CARD_CALC_COUNTS[ret.card][2] + ret.repetitions + end + Talisman.calculating_joker = false + return ret +end +local cuc = Card.use_consumable +function Card:use_consumable(x,y) + Talisman.calculating_score = true + local ret = cuc(self, x,y) + Talisman.calculating_score = false + return ret +end +local gfep = G.FUNCS.evaluate_play +G.FUNCS.evaluate_play = function(e) + Talisman.calculating_score = true + local ret = gfep(e) + Talisman.calculating_score = false + return ret +end +--[[local ec = eval_card +function eval_card() + Talisman.calculating_card = true + local ret = ec() + Talisman.calculating_card = false + return ret +end--]] +local sm = Card.start_materialize +function Card:start_materialize(a,b,c) + if Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then return end + return sm(self,a,b,c) +end +local sd = Card.start_dissolve +function Card:start_dissolve(a,b,c,d) + if Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then self:remove() return end + return sd(self,a,b,c,d) +end +local ss = Card.set_seal +function Card:set_seal(a,b,immediate) + return ss(self,a,b,Talisman.config_file.disable_anims and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) or immediate) +end + +function Card:get_chip_x_bonus() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.x_chips or 0) <= 1 then return 0 end + return self.ability.x_chips +end + +function Card:get_chip_e_bonus() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.e_chips or 0) <= 1 then return 0 end + return self.ability.e_chips +end + +function Card:get_chip_ee_bonus() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.ee_chips or 0) <= 1 then return 0 end + return self.ability.ee_chips +end + +function Card:get_chip_eee_bonus() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.eee_chips or 0) <= 1 then return 0 end + return self.ability.eee_chips +end + +function Card:get_chip_hyper_bonus() + if self.debuff then return {0,0} end + if self.ability.set == 'Joker' then return {0,0} end + if type(self.ability.hyper_chips) ~= 'table' then return {0,0} end + if (self.ability.hyper_chips[1] <= 0 or self.ability.hyper_chips[2] <= 0) then return {0,0} end + return self.ability.hyper_chips +end + +function Card:get_chip_e_mult() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.e_mult or 0) <= 1 then return 0 end + return self.ability.e_mult +end + +function Card:get_chip_ee_mult() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.ee_mult or 0) <= 1 then return 0 end + return self.ability.ee_mult +end + +function Card:get_chip_eee_mult() + if self.debuff then return 0 end + if self.ability.set == 'Joker' then return 0 end + if (self.ability.eee_mult or 0) <= 1 then return 0 end + return self.ability.eee_mult +end + +function Card:get_chip_hyper_mult() + if self.debuff then return {0,0} end + if self.ability.set == 'Joker' then return {0,0} end + if type(self.ability.hyper_mult) ~= 'table' then return {0,0} end + if (self.ability.hyper_mult[1] <= 0 or self.ability.hyper_mult[2] <= 0) then return {0,0} end + return self.ability.hyper_mult +end + +--Easing fixes +--Changed this to always work; it's less pretty but fine for held in hand things +local edo = ease_dollars +function ease_dollars(mod, instant) + if Talisman.config_file.disable_anims then--and (Talisman.calculating_joker or Talisman.calculating_score or Talisman.calculating_card) then + mod = mod or 0 + if mod < 0 then inc_career_stat('c_dollars_earned', mod) end + G.GAME.dollars = G.GAME.dollars + mod + Talisman.dollar_update = true + else return edo(mod, instant) end +end + +local su = G.start_up +function safe_str_unpack(str) + local chunk, err = loadstring(str) + if chunk then + setfenv(chunk, {Big = Big, BigMeta = BigMeta, OmegaMeta = OmegaMeta, to_big = to_big, inf = 1.79769e308}) -- Use an empty environment to prevent access to potentially harmful functions + local success, result = pcall(chunk) + if success then + return result + else + print("Error unpacking string: " .. result) + return nil + end + else + print("Error loading string: " .. err) + return nil + end + end +function G:start_up() + STR_UNPACK = safe_str_unpack + su(self) + STR_UNPACK = safe_str_unpack +end + +--Skip round animation things +local gfer = G.FUNCS.evaluate_round +function G.FUNCS.evaluate_round() + if Talisman.config_file.disable_anims then + if to_big(G.GAME.chips) >= to_big(G.GAME.blind.chips) then + add_round_eval_row({dollars = G.GAME.blind.dollars, name='blind1', pitch = 0.95}) + else + add_round_eval_row({dollars = 0, name='blind1', pitch = 0.95, saved = true}) + end + local arer = add_round_eval_row + add_round_eval_row = function() return end + local dollars = gfer() + add_round_eval_row = arer + add_round_eval_row({name = 'bottom', dollars = Talisman.dollars}) + else + return gfer() + end +end + +--some debugging functions +--[[local callstep=0 +function printCallerInfo() + -- Get debug info for the caller of the function that called printCallerInfo + local info = debug.getinfo(3, "Sl") + callstep = callstep+1 + if info then + print("["..callstep.."] "..(info.short_src or "???")..":"..(info.currentline or "unknown")) + else + print("Caller information not available") + end +end +local emae = EventManager.add_event +function EventManager:add_event(x,y,z) + printCallerInfo() + return emae(self,x,y,z) +end--]] + +require 'cartomancer.init' + +Cartomancer.path = assert( + Cartomancer.find_self('cartomancer.lua'), + "Failed to find mod folder. Make sure that `Cartomancer` folder has `cartomancer.lua` file!" +) + +Cartomancer.load_mod_file('internal/config.lua', 'internal.config') +Cartomancer.load_mod_file('internal/atlas.lua', 'internal.atlas') +Cartomancer.load_mod_file('internal/ui.lua', 'internal.ui') +Cartomancer.load_mod_file('internal/keybinds.lua', 'internal.keybinds') + +Cartomancer.load_mod_file('core/view-deck.lua', 'core.view-deck') +Cartomancer.load_mod_file('core/flames.lua', 'core.flames') +Cartomancer.load_mod_file('core/optimizations.lua', 'core.optimizations') +Cartomancer.load_mod_file('core/jokers.lua', 'core.jokers') +Cartomancer.load_mod_file('core/hand.lua', 'core.hand') + +Cartomancer.load_config() + +Cartomancer.INTERNAL_jokers_menu = false + +-- TODO dedicated keybinds file? keybinds need to load after config +Cartomancer.register_keybind { + name = 'hide_joker', + func = function (controller) + Cartomancer.hide_hovered_joker(controller) + end +} + +Cartomancer.register_keybind { + name = 'toggle_tags', + func = function (controller) + Cartomancer.SETTINGS.hide_tags = not Cartomancer.SETTINGS.hide_tags + Cartomancer.update_tags_visibility() + end +} + +Cartomancer.register_keybind { + name = 'toggle_consumables', + func = function (controller) + Cartomancer.SETTINGS.hide_consumables = not Cartomancer.SETTINGS.hide_consumables + end +} + +Cartomancer.register_keybind { + name = 'toggle_deck', + func = function (controller) + Cartomancer.SETTINGS.hide_deck = not Cartomancer.SETTINGS.hide_deck + end +} + +Cartomancer.register_keybind { + name = 'toggle_jokers', + func = function (controller) + if not (G and G.jokers) then + return + end + G.jokers.cart_hide_all = not G.jokers.cart_hide_all + + if G.jokers.cart_hide_all then + Cartomancer.hide_all_jokers() + else + Cartomancer.show_all_jokers() + end + Cartomancer.align_G_jokers() + end +} + +Cartomancer.register_keybind { + name = 'toggle_jokers_buttons', + func = function (controller) + Cartomancer.SETTINGS.jokers_controls_buttons = not Cartomancer.SETTINGS.jokers_controls_buttons + end +} diff --git a/lovely/dump/tag.lua b/lovely/dump/tag.lua new file mode 100644 index 0000000..1ea5084 --- /dev/null +++ b/lovely/dump/tag.lua @@ -0,0 +1,781 @@ +LOVELY_INTEGRITY = 'd16a678af46c656e33c88a0349859985655ad1ef28db3eae950262607bda3372' + +--Class +Tag = Object:extend() + +--Class Methods +function Tag:init(_tag, for_collection, _blind_type) + self.key = _tag + local proto = G.P_TAGS[_tag] or G.tag_undiscovered + self.config = copy_table(proto.config) + self.pos = proto.pos + self.name = proto.name + self.tally = G.GAME.tag_tally or 0 + self.triggered = false + G.tagid = G.tagid or 0 + self.ID = G.tagid + G.tagid = G.tagid + 1 + self.ability = { + orbital_hand = '['..localize('k_poker_hand')..']', + blind_type = _blind_type + } + G.GAME.tag_tally = G.GAME.tag_tally and (G.GAME.tag_tally + 1) or 1 + if not for_collection then self:set_ability() end +end + +function Tag:nope() + G.E_MANAGER:add_event(Event({ + delay = 0.2, + trigger = 'after', + func = (function() + attention_text({ + text = 'NOPE', + colour = G.C.WHITE, + scale = 0.7, + hold = 0.3/G.SETTINGS.GAMESPEED, + cover = self.HUD_tag, + cover_colour = G.C.BLACK, + align = 'cm', + }) + play_sound('cancel', 1.4, 0.5) + return true + end) + })) + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.1, + func = (function() + self.HUD_tag.states.visible = false + play_sound('cancel', 1.26, 0.5) + return true + end) + })) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.5, + func = (function() + self:remove() + return true + end) + })) +end + +function Tag:yep(message, _colour, func) + stop_use() + + G.E_MANAGER:add_event(Event({ + delay = 0.4, + trigger = 'after', + func = (function() + attention_text({ + text = message, + colour = G.C.WHITE, + scale = 1, + hold = 0.3/G.SETTINGS.GAMESPEED, + cover = self.HUD_tag, + cover_colour = _colour or G.C.GREEN, + align = 'cm', + }) + play_sound('generic1', 0.9 + math.random()*0.1, 0.8) + play_sound('holo1', 1.2 + math.random()*0.1, 0.4) + return true + end) + })) + G.E_MANAGER:add_event(Event({ + func = (function() + self.HUD_tag.states.visible = false + return true + end) + })) + G.E_MANAGER:add_event(Event({ + func = func + })) + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.7, + func = (function() + self:remove() + return true + end) + })) +end + +function Tag:set_ability() + local obj = SMODS.Tags[self.key] + local res + if obj and obj.set_ability and type(obj.set_ability) == 'function' then + obj:set_ability(self) + end + if self.name == 'Orbital Tag' then + if G.orbital_hand then + self.ability.orbital_hand = G.orbital_hand + elseif self.ability.blind_type then + if G.GAME.orbital_choices and G.GAME.orbital_choices[G.GAME.round_resets.ante][self.ability.blind_type] then + self.ability.orbital_hand = G.GAME.orbital_choices[G.GAME.round_resets.ante][self.ability.blind_type] + end + end + end +end + +function Tag:apply_to_run(_context) + if self.triggered then return end + local obj = SMODS.Tags[self.key] + local res + if obj and obj.apply and type(obj.apply) == 'function' then + res = obj:apply(self, _context) + end + if res then return res end + if not self.triggered and self.config.type == _context.type then + if _context.type == 'eval' then + if self.name == 'Investment Tag' and + G.GAME.last_blind and G.GAME.last_blind.boss then + self:yep('+', G.C.GOLD,function() + return true + end) + self.triggered = true + return { + dollars = self.config.dollars, + condition = localize('ph_defeat_the_boss'), + pos = self.pos, + tag = self + } + end + elseif _context.type == 'immediate' then + local lock = self.ID + G.CONTROLLER.locks[lock] = true + if self.name == 'Top-up Tag' then + self:yep('+', G.C.PURPLE,function() + for i = 1, self.config.spawn_jokers do + if G.jokers and #G.jokers.cards < G.jokers.config.card_limit then + local card = create_card('Joker', G.jokers, nil, 0, nil, nil, nil, 'top') + card:add_to_deck() + G.jokers:emplace(card) + end + end + G.CONTROLLER.locks[lock] = nil + return true + end) + self.triggered = true + return true + end + if self.name == 'Skip Tag' then + self:yep('+', G.C.MONEY,function() + G.CONTROLLER.locks[lock] = nil + return true + end) + ease_dollars((G.GAME.skips or 0)*self.config.skip_bonus) + self.triggered = true + return true + end + if self.name == 'Garbage Tag' then + self:yep('+', G.C.MONEY,function() + G.CONTROLLER.locks[lock] = nil + return true + end) + ease_dollars((G.GAME.unused_discards or 0)*self.config.dollars_per_discard) + self.triggered = true + return true + end + if self.name == 'Handy Tag' then + self:yep('+', G.C.MONEY,function() + G.CONTROLLER.locks[lock] = nil + return true + end) + ease_dollars((G.GAME.hands_played or 0)*self.config.dollars_per_hand) + self.triggered = true + return true + end + if self.name == 'Economy Tag' then + self:yep('+', G.C.MONEY,function() + G.CONTROLLER.locks[lock] = nil + return true + end) + G.E_MANAGER:add_event(Event({ + trigger = 'immediate', + func = function() + ease_dollars(math.min(self.config.max, math.max(0,G.GAME.dollars)), true) + return true + end + })) + self.triggered = true + return true + end + if self.name == 'Orbital Tag' then + update_hand_text({sound = 'button', volume = 0.7, pitch = 0.8, delay = 0.3}, { + handname= self.ability.orbital_hand, + chips = G.GAME.hands[self.ability.orbital_hand].chips, + mult = G.GAME.hands[self.ability.orbital_hand].mult, + level= G.GAME.hands[self.ability.orbital_hand].level}) + level_up_hand(self, self.ability.orbital_hand, nil, self.config.levels) + update_hand_text({sound = 'button', volume = 0.7, pitch = 1.1, delay = 0}, {mult = 0, chips = 0, handname = '', level = ''}) + self:yep('+', G.C.MONEY,function() + G.CONTROLLER.locks[lock] = nil + return true + end) + self.triggered = true + return true + end + elseif _context.type == 'new_blind_choice' then + local lock = self.ID + G.CONTROLLER.locks[lock] = true + if self.name == 'Charm Tag' then + self:yep('+', G.C.PURPLE,function() + local key = 'p_arcana_mega_'..(math.random(1,2)) + local card = Card(G.play.T.x + G.play.T.w/2 - G.CARD_W*1.27/2, + G.play.T.y + G.play.T.h/2-G.CARD_H*1.27/2, G.CARD_W*1.27, G.CARD_H*1.27, G.P_CARDS.empty, G.P_CENTERS[key], {bypass_discovery_center = true, bypass_discovery_ui = true}) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({config = {ref_table = card}}) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + G.CONTROLLER.locks[lock] = nil + return true + end) + self.triggered = true + return true + end + if self.name == 'Meteor Tag' then + self:yep('+', G.C.SECONDARY_SET.Planet,function() + local key = 'p_celestial_mega_'..(math.random(1,2)) + local card = Card(G.play.T.x + G.play.T.w/2 - G.CARD_W*1.27/2, + G.play.T.y + G.play.T.h/2-G.CARD_H*1.27/2, G.CARD_W*1.27, G.CARD_H*1.27, G.P_CARDS.empty, G.P_CENTERS[key], {bypass_discovery_center = true, bypass_discovery_ui = true}) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({config = {ref_table = card}}) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + G.CONTROLLER.locks[lock] = nil + return true + end) + self.triggered = true + return true + end + if self.name == 'Ethereal Tag' then + self:yep('+', G.C.SECONDARY_SET.Spectral,function() + local key = 'p_spectral_normal_1' + local card = Card(G.play.T.x + G.play.T.w/2 - G.CARD_W*1.27/2, + G.play.T.y + G.play.T.h/2-G.CARD_H*1.27/2, G.CARD_W*1.27, G.CARD_H*1.27, G.P_CARDS.empty, G.P_CENTERS[key], {bypass_discovery_center = true, bypass_discovery_ui = true}) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({config = {ref_table = card}}) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + G.CONTROLLER.locks[lock] = nil + return true + end) + self.triggered = true + return true + end + if self.name == 'Standard Tag' then + self:yep('+', G.C.SECONDARY_SET.Spectral,function() + local key = 'p_standard_mega_1' + local card = Card(G.play.T.x + G.play.T.w/2 - G.CARD_W*1.27/2, + G.play.T.y + G.play.T.h/2-G.CARD_H*1.27/2, G.CARD_W*1.27, G.CARD_H*1.27, G.P_CARDS.empty, G.P_CENTERS[key], {bypass_discovery_center = true, bypass_discovery_ui = true}) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({config = {ref_table = card}}) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + G.CONTROLLER.locks[lock] = nil + return true + end) + self.triggered = true + return true + end + if self.name == 'Buffoon Tag' then + self:yep('+', G.C.SECONDARY_SET.Spectral,function() + local key = 'p_buffoon_mega_1' + local card = Card(G.play.T.x + G.play.T.w/2 - G.CARD_W*1.27/2, + G.play.T.y + G.play.T.h/2-G.CARD_H*1.27/2, G.CARD_W*1.27, G.CARD_H*1.27, G.P_CARDS.empty, G.P_CENTERS[key], {bypass_discovery_center = true, bypass_discovery_ui = true}) + card.cost = 0 + card.from_tag = true + G.FUNCS.use_card({config = {ref_table = card}}) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + G.CONTROLLER.locks[lock] = nil + return true + end) + self.triggered = true + return true + end + if self.name == 'Boss Tag' then + local lock = self.ID + G.CONTROLLER.locks[lock] = true + self:yep('+', G.C.GREEN,function() + G.from_boss_tag = true + G.FUNCS.reroll_boss() + + G.E_MANAGER:add_event(Event({func = function() + G.E_MANAGER:add_event(Event({func = function() + G.CONTROLLER.locks[lock] = nil + return true; end})) + return true; end})) + + return true + end) + self.triggered = true + return true + end + elseif _context.type == 'voucher_add' then + if self.name == 'Voucher Tag' then + self:yep('+', G.C.SECONDARY_SET.Voucher,function() + G.ARGS.voucher_tag = G.ARGS.voucher_tag or {} + local voucher_key = get_next_voucher_key(true) + G.ARGS.voucher_tag[voucher_key] = true + G.shop_vouchers.config.card_limit = G.shop_vouchers.config.card_limit + 1 + local card = Card(G.shop_vouchers.T.x + G.shop_vouchers.T.w/2, + G.shop_vouchers.T.y, G.CARD_W, G.CARD_H, G.P_CARDS.empty, G.P_CENTERS[voucher_key],{bypass_discovery_center = true, bypass_discovery_ui = true}) + cry_misprintize(card) + if G.GAME.events.ev_cry_choco2 then + card.misprint_cost_fac = (card.misprint_cost_fac or 1) * 2 + card:set_cost() + end + create_shop_card_ui(card, 'Voucher', G.shop_vouchers) + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true) + end + + if G.GAME.modifiers.cry_force_sticker == 'eternal' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_eternal(true) + card.ability.eternal = true + end + if G.GAME.modifiers.cry_force_sticker == 'perishable' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_perishable(true) + card.ability.perishable = true + end + if G.GAME.modifiers.cry_force_sticker == 'rental' or G.GAME.modifiers.cry_sticker_sheet_plus then + card:set_rental(true) + card.ability.rental = true + end + if G.GAME.modifiers.cry_force_sticker == 'pinned' or G.GAME.modifiers.cry_sticker_sheet_plus then + card.pinned = true + end + if G.GAME.modifiers.cry_force_sticker == 'banana' or G.GAME.modifiers.cry_sticker_sheet_plus then + card.ability.banana = true + end + if G.GAME.modifiers.cry_sticker_sheet_plus then + for k, v in pairs(SMODS.Stickers) do + if v.apply and not v.no_sticker_sheet then v:apply(card, true) end + end + end + G.shop_vouchers:emplace(card) + G.ARGS.voucher_tag = nil + return true + end) + self.triggered = true + end + elseif _context.type == 'tag_add' then + if self.name == 'Double Tag' and _context.tag.key ~= 'tag_double' then + local lock = self.ID + G.CONTROLLER.locks[lock] = true + self:yep('+', G.C.BLUE,function() + if _context.tag.ability and _context.tag.ability.orbital_hand then + G.orbital_hand = _context.tag.ability.orbital_hand + end + local tag = Tag(_context.tag.key) + if _context.tag.key == "tag_cry_rework" then + tag.ability.rework_edition = _context.tag.ability.rework_edition + tag.ability.rework_key = _context.tag.ability.rework_key + end + add_tag(tag) + G.orbital_hand = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + self.triggered = true + end + elseif _context.type == 'round_start_bonus' then + if self.name == 'Juggle Tag' then + self:yep('+', G.C.BLUE,function() + return true + end) + G.hand:change_size(self.config.h_size) + G.GAME.round_resets.temp_handsize = (G.GAME.round_resets.temp_handsize or 0) + self.config.h_size + self.triggered = true + return true + end + elseif _context.type == 'store_joker_create' then + local card = nil + if self.name == 'Rare Tag' then + local rares_in_posession = {0} + for k, v in ipairs(G.jokers.cards) do + if v.config.center.rarity == 3 and not rares_in_posession[v.config.center.key] then + rares_in_posession[1] = rares_in_posession[1] + 1 + rares_in_posession[v.config.center.key] = true + end + end + + if #G.P_JOKER_RARITY_POOLS[3] > rares_in_posession[1] then + card = create_card('Joker', _context.area, nil, 1, nil, nil, nil, 'rta') + create_shop_card_ui(card, 'Joker', _context.area) + card.states.visible = false + self:yep('+', G.C.RED,function() + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + card.ability.couponed = true + card:set_cost() + return true + end) + else + self:nope() + end + self.triggered = true + elseif self.name == 'Uncommon Tag' then + card = create_card('Joker', _context.area, nil, 0.9, nil, nil, nil, 'uta') + create_shop_card_ui(card, 'Joker', _context.area) + card.states.visible = false + self:yep('+', G.C.GREEN,function() + if G.GAME.modifiers.cry_force_edition and not G.GAME.modifiers.cry_force_random_edition then + card:set_edition(nil, true, true) + elseif G.GAME.modifiers.cry_force_random_edition then + local edition = cry_poll_random_edition() + card:set_edition(edition, true, true) + end + card:start_materialize() + card.ability.couponed = true + card:set_cost() + return true + end) + end + self.triggered = true + return card + elseif _context.type == 'shop_start' then + if self.name == 'D6 Tag' and not G.GAME.shop_d6ed then + G.GAME.shop_d6ed = true + self:yep('+', G.C.GREEN,function() + G.GAME.round_resets.temp_reroll_cost = 0 + calculate_reroll_cost(true) + return true + end) + self.triggered = true + return true + end + elseif _context.type == 'store_joker_modify' then + local _applied = nil + if not _context.card.edition and not _context.card.temp_edition and _context.card.ability.set == 'Joker' then + local lock = self.ID + G.CONTROLLER.locks[lock] = true + if self.name == 'Foil Tag' then + _context.card.temp_edition = true + self:yep('+', G.C.DARK_EDITION,function() + _context.card:set_edition({foil = true}, true) + _context.card.ability.couponed = true + _context.card:set_cost() + _context.card.temp_edition = nil + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + elseif self.name == 'Holographic Tag' then + _context.card.temp_edition = true + self:yep('+', G.C.DARK_EDITION,function() + _context.card.temp_edition = nil + _context.card:set_edition({holo = true}, true) + _context.card.ability.couponed = true + _context.card:set_cost() + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + elseif self.name == 'Polychrome Tag' then + _context.card.temp_edition = true + self:yep('+', G.C.DARK_EDITION,function() + _context.card.temp_edition = nil + _context.card:set_edition({polychrome = true}, true) + _context.card.ability.couponed = true + _context.card:set_cost() + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + elseif self.name == 'Negative Tag' then + _context.card.temp_edition = true + self:yep('+', G.C.DARK_EDITION,function() + _context.card.temp_edition = nil + _context.card:set_edition({negative = true}, true) + _context.card.ability.couponed = true + _context.card:set_cost() + G.CONTROLLER.locks[lock] = nil + return true + end) + _applied = true + end + self.triggered = true + end + + return _applied + elseif _context.type == 'shop_final_pass' then + if self.name == 'Coupon Tag' and (G.shop and not G.GAME.shop_free) then + G.GAME.shop_free = true + self:yep('+', G.C.GREEN,function() + if G.shop_jokers and G.shop_booster then + for k, v in pairs(G.shop_jokers.cards) do + v.ability.couponed = true + v:set_cost() + end + for k, v in pairs(G.shop_booster.cards) do + v.ability.couponed = true + v:set_cost() + end + end + return true + end) + self.triggered = true + return true + end + end + end +end + +function Tag:save() + return { + key = self.key, + tally = self.tally, + ability = self.ability + } +end + +function Tag:load(tag_savetable) + self.key = tag_savetable.key + local proto = G.P_TAGS[self.key] or G.tag_undiscovered + self.config = copy_table(proto.config) + self.pos = proto.pos + self.name = proto.name + self.tally = tag_savetable.tally + self.ability = tag_savetable.ability + G.GAME.tag_tally = math.max(self.tally, G.GAME.tag_tally) + 1 +end + +function Tag:juice_up(_scale, _rot) + if self.tag_sprite then self.tag_sprite:juice_up(_scale, _rot) end +end + +function Tag:generate_UI(_size) + _size = _size or 0.8 + + local tag_sprite_tab = nil + + local tag_sprite = Sprite(0,0,_size*1,_size*1,G.ASSET_ATLAS[(not self.hide_ability) and G.P_TAGS[self.key].atlas or "tags"], (self.hide_ability) and G.tag_undiscovered.pos or self.pos) + tag_sprite.T.scale = 1 + tag_sprite_tab = {n= G.UIT.C, config={align = "cm", ref_table = self, group = self.tally}, nodes={ + {n=G.UIT.O, config={w=_size*1,h=_size*1, colour = G.C.BLUE, object = tag_sprite, focus_with_object = true}}, + }} + tag_sprite:define_draw_steps({ + {shader = 'dissolve', shadow_height = 0.05}, + {shader = 'dissolve'}, + }) + tag_sprite.float = true + tag_sprite.states.hover.can = true + tag_sprite.states.drag.can = false + tag_sprite.states.collide.can = true + if self.key == 'tag_cry_cat' then tag_sprite.states.click.can = true; tag_sprite.states.drag.can = true end + tag_sprite.config = {tag = self, force_focus = true} + + tag_sprite.hover = function(_self) + if not G.CONTROLLER.dragging.target or G.CONTROLLER.using_touch then + if not _self.hovering and _self.states.visible then + _self.hovering = true + if _self == tag_sprite then + _self.hover_tilt = 3 + _self:juice_up(0.05, 0.02) + play_sound('paper1', math.random()*0.1 + 0.55, 0.42) + if self.key == 'tag_cry_cat' then + local rand = math.random(4) + play_sound('cry_meow'..rand, 1.26, 0.25) + end + play_sound('tarot2', math.random()*0.1 + 0.55, 0.09) + end + + self:get_uibox_table(tag_sprite) + _self.config.h_popup = G.UIDEF.card_h_popup(_self) + _self.config.h_popup_config = (_self.T.x > G.ROOM.T.w*0.4) and + {align = 'cl', offset = {x=-0.1,y=0},parent = _self} or + {align = 'cr', offset = {x=0.1,y=0},parent = _self} + Node.hover(_self) + if _self.children.alert then + _self.children.alert:remove() + _self.children.alert = nil + if self.key and G.P_TAGS[self.key] then G.P_TAGS[self.key].alerted = true end + G:save_progress() + end + end + end + end + tag_sprite.stop_hover = function(_self) _self.hovering = false; Node.stop_hover(_self); _self.hover_tilt = 0 end + tag_sprite.click = function(_self) + if self.key == 'tag_cry_cat' and self.HUD_tag then + for i = 1, #G.GAME.tags do + local other_cat = G.GAME.tags[i] + if other_cat.key == 'tag_cry_cat' then + if not self.ability.level then self.ability.level = 1 end + if not other_cat.ability.level then other_cat.ability.level = 1 end -- setting ability just doesn't seem to be working... so you get this + if (self.ability.level == other_cat.ability.level) and (other_cat ~= self) and not cry_too_fast_kitty then + cry_too_fast_kitty = true + local perc = (other_cat.ability.level + 1)/10 + if perc > 1 then perc = 1 end + G.E_MANAGER:add_event(Event({ + delay = 0.0, + trigger = 'immediate', + func = (function() + attention_text({ + text = ""..other_cat.ability.level, + colour = G.C.WHITE, + scale = 1, + hold = 0.3/G.SETTINGS.GAMESPEED, + cover = other_cat.HUD_tag, + cover_colour = G.C.DARK_EDITION, + align = 'cm', + }) + play_sound('generic1', 0.8 + perc/2, 0.6) + play_sound('multhit1', 0.9 + perc/2, 0.4) + return true + end) + })) + G.E_MANAGER:add_event(Event({ + delay = 0.0, + trigger = 'immediate', + func = (function() + attention_text({ + text = "-", + colour = G.C.WHITE, + scale = 1, + hold = 0.3/G.SETTINGS.GAMESPEED, + cover = self.HUD_tag, + cover_colour = G.C.RED, + align = 'cm', + }) + return true + end) + })) + G.E_MANAGER:add_event(Event({ + func = (function() + self.HUD_tag.states.visible = false + return true + end) + })) + G.E_MANAGER:add_event(Event({ -- i have no idea what this does but i'm not messing with it + func = func + })) + + other_cat.ability.level = other_cat.ability.level + 1 + + G.E_MANAGER:add_event(Event({ + trigger = 'after', + delay = 0.7, + func = (function() + self:remove() + cry_too_fast_kitty = nil + return true + end) + })) + break + end + end + end + end + end + + tag_sprite:juice_up() + self.tag_sprite = tag_sprite + + return tag_sprite_tab, tag_sprite +end + +function Tag:get_uibox_table(tag_sprite) + tag_sprite = tag_sprite or self.tag_sprite + local name_to_check, loc_vars = self.name, {} + if name_to_check == 'Uncommon Tag' then + elseif name_to_check == 'Investment Tag' then loc_vars = {self.config.dollars} + elseif name_to_check == 'Handy Tag' then loc_vars = {self.config.dollars_per_hand, self.config.dollars_per_hand*(G.GAME.hands_played or 0)} + elseif name_to_check == 'Garbage Tag' then loc_vars = {self.config.dollars_per_discard, self.config.dollars_per_discard*(G.GAME.unused_discards or 0)} + elseif name_to_check == 'Juggle Tag' then loc_vars = {self.config.h_size} + elseif name_to_check == 'Top-up Tag' then loc_vars = {self.config.spawn_jokers} + elseif name_to_check == 'Skip Tag' then loc_vars = {self.config.skip_bonus, self.config.skip_bonus*((G.GAME.skips or 0)+1)} + elseif name_to_check == 'Orbital Tag' then loc_vars = { + (self.ability.orbital_hand == '['..localize('k_poker_hand')..']') and self.ability.orbital_hand or localize(self.ability.orbital_hand, 'poker_hands'), self.config.levels} + elseif name_to_check == 'Economy Tag' then loc_vars = {self.config.max} + elseif name_to_check == "cry-Rework Tag" then loc_vars = { + self.ability and self.ability.rework_edition and localize{type = "name_text", set = "Edition", key = self.ability.rework_edition} or "["..string.lower(localize("k_edition")).."]", + self.ability and self.ability.rework_key and localize{type = "name_text", set = "Joker", key = self.ability.rework_key} or "["..string.lower(localize("k_joker")).."]", + } + elseif name_to_check == "cry-Cat Tag" then loc_vars = {self.ability.level or 1} + end + tag_sprite.ability_UIBox_table = generate_card_ui(G.P_TAGS[self.key], nil, loc_vars, (self.hide_ability) and 'Undiscovered' or 'Tag', nil, (self.hide_ability), nil, nil, self) + return tag_sprite +end + +function Tag:remove_from_game() + local tag_key = nil + for k, v in pairs(G.GAME.tags) do + if v == self then tag_key = k end + end + table.remove(G.GAME.tags, tag_key) +end + +function Tag:remove() + self:remove_from_game() + local HUD_tag_key = nil + for k, v in pairs(G.HUD_tags) do + if v == self.HUD_tag then HUD_tag_key = k end + end + + if HUD_tag_key then + if G.HUD_tags and G.HUD_tags[HUD_tag_key+1] then + if HUD_tag_key == 1 then + G.HUD_tags[HUD_tag_key+1]:set_alignment({type = 'bri', + offset = {x=0.7,y=0}, + xy_bond = 'Weak', + major = G.ROOM_ATTACH}) + else + G.HUD_tags[HUD_tag_key+1]:set_role({ + xy_bond = 'Weak', + major = G.HUD_tags[HUD_tag_key-1]}) + end + end + table.remove(G.HUD_tags, HUD_tag_key) + end + + self.HUD_tag:remove() +end diff --git a/lovely/log/lovely-2025.01.06-16.24.29.log b/lovely/log/lovely-2025.01.06-16.24.29.log new file mode 100644 index 0000000..9d660e6 --- /dev/null +++ b/lovely/log/lovely-2025.01.06-16.24.29.log @@ -0,0 +1,115 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Initialization complete in 127ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 10 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 72 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 121 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 47 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 203 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-06 16:24:30 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-06 16:24:31 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-06 16:24:31 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-06 16:24:31 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-06 16:24:31 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-06 16:24:31 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-06 16:24:31 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-06 16:24:31 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.747 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0030] Injected Atlas in 795.883 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0025] Injected Sound in 20.008 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0032] Injected Stake in 0.804 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0008] Injected Rarity in 0.031 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.073 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0391] Injected Center in 1.877 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.010 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0027] Injected Blind in 0.116 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0002] Injected Seal in 0.064 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0004] Injected Suit in 0.761 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0013] Injected Rank in 0.216 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.039 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.129 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0031] Injected Challenge in 0.033 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0028] Injected Tag in 0.116 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0009] Injected Sticker in 0.398 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0009] Injected Shader in 161.469 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0020] Injected Achievement in 0.157 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.633 ms +INFO - [G] 2025-01-06 16:24:32 :: INFO :: TIMER :: [0011] Injected Event in 0.105 ms +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.08058204 +INFO - [G] LONG DT @ 14: 0.064967672 +INFO - [G] LONG DT @ 14: 0.0525077976 diff --git a/lovely/log/lovely-2025.01.06-16.24.54.log b/lovely/log/lovely-2025.01.06-16.24.54.log new file mode 100644 index 0000000..23da833 --- /dev/null +++ b/lovely/log/lovely-2025.01.06-16.24.54.log @@ -0,0 +1,364 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 124ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 10 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 72 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 121 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 47 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 203 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-06 16:24:55 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-06 16:24:56 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-06 16:24:56 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-06 16:24:56 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-06 16:24:56 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-06 16:24:56 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-06 16:24:56 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-06 16:24:56 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.728 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0030] Injected Atlas in 776.599 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0025] Injected Sound in 21.525 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0032] Injected Stake in 2.105 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0008] Injected Rarity in 0.062 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.083 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0391] Injected Center in 2.357 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.009 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0027] Injected Blind in 0.096 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0002] Injected Seal in 0.119 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0004] Injected Suit in 0.295 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0013] Injected Rank in 0.214 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.037 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.182 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0031] Injected Challenge in 0.034 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0028] Injected Tag in 0.255 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0009] Injected Sticker in 0.737 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0009] Injected Shader in 53.803 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0020] Injected Achievement in 0.052 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.209 ms +INFO - [G] 2025-01-06 16:24:57 :: INFO :: TIMER :: [0011] Injected Event in 0.012 ms +INFO - [G] LONG DT @ 3: 0.1 +INFO - [G] LONG DT @ 3: 0.08331644 +INFO - [G] LONG DT @ 3: 0.069972552 +INFO - [G] LONG DT @ 3: 0.0592162216 +INFO - [G] LONG DT @ 3: 0.05065199728 +INFO - [G] LONG DT @ 50: 0.1 +INFO - [G] LONG DT @ 50: 0.1 +INFO - [G] LONG DT @ 50: 0.1 +INFO - [G] LONG DT @ 50: 0.1 +INFO - [G] LONG DT @ 50: 0.08123894 +INFO - [G] LONG DT @ 50: 0.065959192 +INFO - [G] LONG DT @ 50: 0.053649213600001 +INFO - [G] LONG DT @ 51: 0.1 +INFO - [G] LONG DT @ 51: 0.1 +INFO - [G] LONG DT @ 51: 0.1 +INFO - [G] LONG DT @ 51: 0.1 +INFO - [G] LONG DT @ 51: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 52: 0.1 +INFO - [G] LONG DT @ 53: 0.1 +INFO - [G] LONG DT @ 53: 0.1 +INFO - [G] LONG DT @ 53: 0.1 +INFO - [G] LONG DT @ 53: 0.081268919999998 +INFO - [G] LONG DT @ 53: 0.066091736 +INFO - [G] LONG DT @ 53: 0.0538612688 +INFO - [G] LONG DT @ 55: 0.1 +INFO - [G] LONG DT @ 55: 0.1 +INFO - [G] LONG DT @ 55: 0.1 +INFO - [G] LONG DT @ 55: 0.1 +INFO - [G] LONG DT @ 55: 0.1 +INFO - [G] LONG DT @ 55: 0.1 +INFO - [G] LONG DT @ 55: 0.1 +INFO - [G] LONG DT @ 55: 0.1 +INFO - [G] LONG DT @ 56: 0.1 +INFO - [G] LONG DT @ 56: 0.1 +INFO - [G] LONG DT @ 56: 0.081317280000001 +INFO - [G] LONG DT @ 56: 0.066314163999999 +INFO - [G] LONG DT @ 56: 0.054187291200002 +INFO - [G] LONG DT @ 210: 0.1 +INFO - [G] LONG DT @ 210: 0.1 +INFO - [G] LONG DT @ 210: 0.1 +INFO - [G] LONG DT @ 210: 0.1 +INFO - [G] LONG DT @ 210: 0.1 +INFO - [G] LONG DT @ 210: 0.1 +INFO - [G] LONG DT @ 210: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 211: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 212: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 213: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 214: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 215: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 216: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 217: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 218: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 219: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 220: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 221: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 222: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 223: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 224: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 225: 0.1 +INFO - [G] LONG DT @ 226: 0.1 +INFO - [G] LONG DT @ 226: 0.1 +INFO - [G] LONG DT @ 226: 0.1 +INFO - [G] LONG DT @ 226: 0.1 +INFO - [G] LONG DT @ 226: 0.1 +INFO - [G] LONG DT @ 226: 0.1 +INFO - [G] LONG DT @ 226: 0.081283380000007 +INFO - [G] LONG DT @ 226: 0.066030723999999 +INFO - [G] LONG DT @ 226: 0.053615339200006 +INFO - [G] LONG DT @ 326: 0.1 +INFO - [G] LONG DT @ 326: 0.1 +INFO - [G] LONG DT @ 326: 0.1 +INFO - [G] LONG DT @ 326: 0.1 +INFO - [G] LONG DT @ 326: 0.1 +INFO - [G] LONG DT @ 326: 0.1 +INFO - [G] LONG DT @ 326: 0.1 +INFO - [G] LONG DT @ 326: 0.1 +INFO - [G] LONG DT @ 327: 0.1 +INFO - [G] LONG DT @ 327: 0.1 +INFO - [G] LONG DT @ 327: 0.1 +INFO - [G] LONG DT @ 327: 0.1 +INFO - [G] LONG DT @ 327: 0.1 +INFO - [G] LONG DT @ 327: 0.1 +INFO - [G] LONG DT @ 327: 0.1 +INFO - [G] LONG DT @ 327: 0.081428220000003 +INFO - [G] LONG DT @ 327: 0.066303576000004 +INFO - [G] LONG DT @ 327: 0.054125840800004 +INFO - [G] LONG DT @ 360: 0.1 +INFO - [G] LONG DT @ 360: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 361: 0.1 +INFO - [G] LONG DT @ 362: 0.1 +INFO - [G] LONG DT @ 362: 0.1 +INFO - [G] LONG DT @ 362: 0.1 +INFO - [G] LONG DT @ 362: 0.1 +INFO - [G] LONG DT @ 362: 0.1 +INFO - [G] LONG DT @ 362: 0.1 +INFO - [G] LONG DT @ 362: 0.1 +INFO - [G] LONG DT @ 362: 0.081710219999995 +INFO - [G] LONG DT @ 362: 0.066695916000007 +INFO - [G] LONG DT @ 362: 0.054951152799998 diff --git a/lovely/log/lovely-2025.01.06-17.54.40.log b/lovely/log/lovely-2025.01.06-17.54.40.log new file mode 100644 index 0000000..ae1fa73 --- /dev/null +++ b/lovely/log/lovely-2025.01.06-17.54.40.log @@ -0,0 +1,370 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 142ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 10 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 72 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 121 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 47 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 203 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-06 17:54:41 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-06 17:54:42 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-06 17:54:42 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-06 17:54:42 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-06 17:54:42 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-06 17:54:42 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-06 17:54:42 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-06 17:54:42 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.853 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0030] Injected Atlas in 787.588 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0025] Injected Sound in 19.834 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0032] Injected Stake in 1.880 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0008] Injected Rarity in 0.067 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.112 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0391] Injected Center in 2.593 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.012 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0027] Injected Blind in 0.072 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0002] Injected Seal in 0.064 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0004] Injected Suit in 0.095 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0013] Injected Rank in 0.177 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.032 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.087 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0031] Injected Challenge in 0.035 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0028] Injected Tag in 0.095 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0009] Injected Sticker in 0.089 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0009] Injected Shader in 54.228 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0020] Injected Achievement in 0.053 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.124 ms +INFO - [G] 2025-01-06 17:54:43 :: INFO :: TIMER :: [0011] Injected Event in 0.018 ms +INFO - [G] LONG DT @ 1401: 0.1 +INFO - [G] LONG DT @ 1401: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1402: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1403: 0.1 +INFO - [G] LONG DT @ 1404: 0.1 +INFO - [G] LONG DT @ 1404: 0.081285999999982 +INFO - [G] LONG DT @ 1404: 0.066031859999979 +INFO - [G] LONG DT @ 1404: 0.053697567999983 +INFO - [G] LONG DT @ 1411: 0.1 +INFO - [G] LONG DT @ 1411: 0.081767680000012 +INFO - [G] LONG DT @ 1411: 0.066567164000002 +INFO - [G] LONG DT @ 1411: 0.1 +INFO - [G] LONG DT @ 1411: 0.1 +INFO - [G] LONG DT @ 1411: 0.081600499999995 +INFO - [G] LONG DT @ 1411: 0.066364679999982 +INFO - [G] LONG DT @ 1412: 0.1 +INFO - [G] LONG DT @ 1412: 0.081730199999975 +INFO - [G] LONG DT @ 1412: 0.066622379999984 +INFO - [G] LONG DT @ 1412: 0.1 +INFO - [G] LONG DT @ 1412: 0.1 +INFO - [G] LONG DT @ 1412: 0.1 +INFO - [G] LONG DT @ 1412: 0.1 +INFO - [G] LONG DT @ 1412: 0.081709560000018 +INFO - [G] LONG DT @ 1412: 0.1 +INFO - [G] LONG DT @ 1412: 0.081940880000029 +INFO - [G] LONG DT @ 1412: 0.066753623999997 +INFO - [G] LONG DT @ 1413: 0.1 +INFO - [G] LONG DT @ 1413: 0.081713160000008 +INFO - [G] LONG DT @ 1413: 0.066586568000015 +INFO - [G] LONG DT @ 1413: 0.1 +INFO - [G] LONG DT @ 1413: 0.081658380000026 +INFO - [G] LONG DT @ 1413: 0.066325323999994 +INFO - [G] LONG DT @ 1413: 0.053997039200024 +INFO - [G] LONG DT @ 1446: 0.1 +INFO - [G] LONG DT @ 1446: 0.1 +INFO - [G] LONG DT @ 1446: 0.081840320000019 +INFO - [G] LONG DT @ 1446: 0.1 +INFO - [G] LONG DT @ 1446: 0.1 +INFO - [G] LONG DT @ 1446: 0.1 +INFO - [G] LONG DT @ 1446: 0.1 +INFO - [G] LONG DT @ 1446: 0.1 +INFO - [G] LONG DT @ 1447: 0.1 +INFO - [G] LONG DT @ 1447: 0.1 +INFO - [G] LONG DT @ 1447: 0.1 +INFO - [G] LONG DT @ 1447: 0.082028060000021 +INFO - [G] LONG DT @ 1447: 0.067336968000012 +INFO - [G] LONG DT @ 1447: 0.055066294400017 +INFO - [G] LONG DT @ 1554: 0.1 +INFO - [G] LONG DT @ 1554: 0.1 +INFO - [G] LONG DT @ 1554: 0.081597339999989 +INFO - [G] LONG DT @ 1554: 0.066234331999989 +INFO - [G] LONG DT @ 1554: 0.053803365599989 +INFO - [G] LONG DT @ 1605: 0.1 +INFO - [G] LONG DT @ 1605: 0.1 +INFO - [G] LONG DT @ 1605: 0.1 +INFO - [G] LONG DT @ 1605: 0.1 +INFO - [G] LONG DT @ 1605: 0.1 +INFO - [G] LONG DT @ 1605: 0.1 +INFO - [G] LONG DT @ 1606: 0.1 +INFO - [G] LONG DT @ 1606: 0.081722380000001 +INFO - [G] LONG DT @ 1606: 0.066607564000036 +INFO - [G] LONG DT @ 1606: 0.054445931200006 +INFO - [G] LONG DT @ 1667: 0.1 +INFO - [G] LONG DT @ 1667: 0.1 +INFO - [G] LONG DT @ 1667: 0.1 +INFO - [G] LONG DT @ 1667: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.1 +INFO - [G] LONG DT @ 1668: 0.081780099999996 +INFO - [G] LONG DT @ 1669: 0.06657709999999 +INFO - [G] LONG DT @ 1669: 0.054286639999987 +INFO - [G] 2025-01-06 18:24:01 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-06 18:24:01 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.939 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0030] Injected Atlas in 772.983 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0025] Injected Sound in 18.565 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0032] Injected Stake in 0.111 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0008] Injected Rarity in 0.019 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.062 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0391] Injected Center in 1.430 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.010 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0027] Injected Blind in 0.034 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0002] Injected Seal in 0.042 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0004] Injected Suit in 0.049 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0013] Injected Rank in 0.023 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.021 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.055 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0031] Injected Challenge in 0.106 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0028] Injected Tag in 0.221 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0009] Injected Sticker in 0.068 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0009] Injected Shader in 21.968 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0020] Injected Achievement in 0.054 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.055 ms +INFO - [G] 2025-01-06 18:24:02 :: INFO :: TIMER :: [0011] Injected Event in 0.010 ms +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 12: 0.080777819999985 +INFO - [G] LONG DT @ 12: 0.064926176000021 +INFO - [G] LONG DT @ 12: 0.052253900799991 +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.767 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0030] Injected Atlas in 750.861 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0025] Injected Sound in 18.431 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0032] Injected Stake in 0.143 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0008] Injected Rarity in 0.017 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.032 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0391] Injected Center in 1.187 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.008 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0027] Injected Blind in 0.084 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0002] Injected Seal in 0.036 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0004] Injected Suit in 0.049 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0013] Injected Rank in 0.023 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.043 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.075 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0031] Injected Challenge in 0.055 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0028] Injected Tag in 0.079 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0009] Injected Sticker in 0.063 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0009] Injected Shader in 76.396 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0020] Injected Achievement in 0.071 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.298 ms +INFO - [G] 2025-01-06 18:24:12 :: INFO :: TIMER :: [0011] Injected Event in 0.013 ms +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 12: 0.080835899999993 +INFO - [G] LONG DT @ 12: 0.064991679999988 +INFO - [G] LONG DT @ 12: 0.052397784000002 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 18: 0.080871459999962 +INFO - [G] LONG DT @ 18: 0.065903347999978 +INFO - [G] LONG DT @ 18: 0.053924178400007 +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 137: 0.1 +INFO - [G] LONG DT @ 138: 0.1 +INFO - [G] LONG DT @ 138: 0.081146120000067 +INFO - [G] LONG DT @ 138: 0.065857816000047 +INFO - [G] LONG DT @ 138: 0.05390055280003 +INFO - [G] 2025-01-06 18:41:18 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-06 18:41:18 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.747 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0030] Injected Atlas in 762.820 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0025] Injected Sound in 18.082 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0032] Injected Stake in 0.378 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0008] Injected Rarity in 0.021 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.034 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0391] Injected Center in 0.985 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.008 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0027] Injected Blind in 0.027 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0002] Injected Seal in 0.038 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0004] Injected Suit in 0.049 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0013] Injected Rank in 0.022 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.022 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.039 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0031] Injected Challenge in 0.023 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0028] Injected Tag in 0.036 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0009] Injected Sticker in 0.082 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0009] Injected Shader in 54.513 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0020] Injected Achievement in 0.059 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.200 ms +INFO - [G] 2025-01-06 18:41:19 :: INFO :: TIMER :: [0011] Injected Event in 0.011 ms +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 12: 0.081048679999985 +INFO - [G] LONG DT @ 12: 0.065136364000049 +INFO - [G] LONG DT @ 12: 0.052441751200043 diff --git a/lovely/log/lovely-2025.01.06-23.32.17.log b/lovely/log/lovely-2025.01.06-23.32.17.log new file mode 100644 index 0000000..c5aa866 --- /dev/null +++ b/lovely/log/lovely-2025.01.06-23.32.17.log @@ -0,0 +1,408 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 136ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 10 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 72 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 121 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 47 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 203 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-06 23:32:19 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-06 23:32:19 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-06 23:32:19 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-06 23:32:19 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-06 23:32:19 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-06 23:32:19 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-06 23:32:19 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-06 23:32:19 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.890 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0030] Injected Atlas in 788.181 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0025] Injected Sound in 21.314 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0032] Injected Stake in 1.667 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0008] Injected Rarity in 0.074 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.126 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0391] Injected Center in 2.374 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.012 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0027] Injected Blind in 0.188 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0002] Injected Seal in 0.058 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0004] Injected Suit in 0.115 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0013] Injected Rank in 0.212 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.031 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.202 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0031] Injected Challenge in 0.078 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0028] Injected Tag in 0.091 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0009] Injected Sticker in 0.209 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0009] Injected Shader in 53.804 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0020] Injected Achievement in 0.092 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.056 ms +INFO - [G] 2025-01-06 23:32:20 :: INFO :: TIMER :: [0011] Injected Event in 0.014 ms +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 69: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 70: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 71: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 72: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 73: 0.1 +INFO - [G] LONG DT @ 74: 0.1 +INFO - [G] LONG DT @ 74: 0.1 +INFO - [G] LONG DT @ 74: 0.1 +INFO - [G] LONG DT @ 74: 0.1 +INFO - [G] LONG DT @ 74: 0.1 +INFO - [G] LONG DT @ 74: 0.1 +INFO - [G] LONG DT @ 74: 0.1 +INFO - [G] LONG DT @ 74: 0.081261959999999 +INFO - [G] LONG DT @ 74: 0.066264627999999 +INFO - [G] LONG DT @ 74: 0.054103482400001 +INFO - [G] LONG DT @ 80: 0.1 +INFO - [G] LONG DT @ 80: 0.1 +INFO - [G] LONG DT @ 80: 0.1 +INFO - [G] LONG DT @ 80: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 81: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 82: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.081094640000002 +INFO - [G] LONG DT @ 87: 0.065771371999999 +INFO - [G] LONG DT @ 87: 0.053828057600003 +INFO - [G] LONG DT @ 131: 0.1 +INFO - [G] LONG DT @ 131: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 132: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 133: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 134: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 135: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.1 +INFO - [G] LONG DT @ 136: 0.081191100000005 +INFO - [G] LONG DT @ 136: 0.066190239999998 +INFO - [G] LONG DT @ 136: 0.054027572000007 +INFO - [G] LONG DT @ 138: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 139: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 140: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 141: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 142: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 143: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 144: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 145: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 146: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 147: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 148: 0.1 +INFO - [G] LONG DT @ 149: 0.1 +INFO - [G] LONG DT @ 149: 0.1 +INFO - [G] LONG DT @ 149: 0.1 +INFO - [G] LONG DT @ 149: 0.0812112 +INFO - [G] LONG DT @ 149: 0.066017799999999 +INFO - [G] LONG DT @ 149: 0.053712919999999 diff --git a/lovely/log/lovely-2025.01.07-02.04.03.log b/lovely/log/lovely-2025.01.07-02.04.03.log new file mode 100644 index 0000000..1c47e77 --- /dev/null +++ b/lovely/log/lovely-2025.01.07-02.04.03.log @@ -0,0 +1,118 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 131ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 10 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 72 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 121 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 47 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 203 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-07 02:04:05 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-07 02:04:06 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-07 02:04:06 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-07 02:04:06 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-07 02:04:06 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-07 02:04:06 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-07 02:04:06 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-07 02:04:06 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.883 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0030] Injected Atlas in 785.442 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0025] Injected Sound in 21.318 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0032] Injected Stake in 1.772 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0008] Injected Rarity in 0.099 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.094 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0391] Injected Center in 2.605 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.010 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0027] Injected Blind in 0.434 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0002] Injected Seal in 0.055 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0004] Injected Suit in 0.066 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0013] Injected Rank in 0.081 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.028 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.083 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0031] Injected Challenge in 0.039 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0028] Injected Tag in 0.777 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0009] Injected Sticker in 0.498 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0009] Injected Shader in 57.738 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0020] Injected Achievement in 0.056 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.307 ms +INFO - [G] 2025-01-07 02:04:07 :: INFO :: TIMER :: [0011] Injected Event in 0.015 ms +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.08386144 +INFO - [G] LONG DT @ 22: 0.070841552 +INFO - [G] LONG DT @ 22: 0.0602701216 +INFO - [G] LONG DT @ 22: 0.05176605728 diff --git a/lovely/log/lovely-2025.01.08-01.06.04.log b/lovely/log/lovely-2025.01.08-01.06.04.log new file mode 100644 index 0000000..274f7c3 --- /dev/null +++ b/lovely/log/lovely-2025.01.08-01.06.04.log @@ -0,0 +1,120 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 140ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 10 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 72 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 121 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 47 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 203 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-08 01:06:06 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-08 01:06:07 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-08 01:06:07 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-08 01:06:07 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-08 01:06:07 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-08 01:06:07 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-08 01:06:07 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-08 01:06:07 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 1.066 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0030] Injected Atlas in 778.382 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0025] Injected Sound in 19.657 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0032] Injected Stake in 1.566 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0008] Injected Rarity in 0.043 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.077 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0391] Injected Center in 2.198 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.011 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0027] Injected Blind in 0.059 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0002] Injected Seal in 0.049 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0004] Injected Suit in 0.069 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0013] Injected Rank in 0.050 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.064 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.085 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0031] Injected Challenge in 0.036 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0028] Injected Tag in 0.091 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0009] Injected Sticker in 0.107 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0009] Injected Shader in 50.960 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0020] Injected Achievement in 0.067 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.045 ms +INFO - [G] 2025-01-08 01:06:08 :: INFO :: TIMER :: [0011] Injected Event in 0.017 ms +INFO - [G] LONG DT @ 6: 0.050069491898674 +INFO - [G] LONG DT @ 12: 0.1 +INFO - [G] LONG DT @ 12: 0.08280844 +INFO - [G] LONG DT @ 12: 0.068812992 +INFO - [G] LONG DT @ 12: 0.0575500936 +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load diff --git a/lovely/log/lovely-2025.01.08-01.08.26.log b/lovely/log/lovely-2025.01.08-01.08.26.log new file mode 100644 index 0000000..5270611 --- /dev/null +++ b/lovely/log/lovely-2025.01.08-01.08.26.log @@ -0,0 +1,220 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 136ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 10 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 72 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 121 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 47 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 203 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-08 01:08:28 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-08 01:08:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-08 01:08:28 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-08 01:08:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): MoreSpeeds.lua +INFO - [G] 2025-01-08 01:08:28 :: TRACE :: Loader :: Saving Mod Info: MoreSpeed +INFO - [G] 2025-01-08 01:08:28 :: ERROR :: Loader :: Found invalid metadata JSON file at C:\users\steamuser\AppData\Roaming\Balatro\Mods/Steamopollys-MoreSpeed-0.8.2/manifest.json, ignoring: [SMODS _ "src/loader.lua"]:212: id +INFO - [G] 2025-01-08 01:08:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-08 01:08:28 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-08 01:08:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-08 01:08:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-08 01:08:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-08 01:08:29 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 1.050 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0030] Injected Atlas in 767.423 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0025] Injected Sound in 20.446 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0032] Injected Stake in 1.084 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0008] Injected Rarity in 0.025 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.063 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0391] Injected Center in 2.572 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.009 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0027] Injected Blind in 0.092 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0002] Injected Seal in 0.071 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0004] Injected Suit in 0.071 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0013] Injected Rank in 0.110 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.032 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.113 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0031] Injected Challenge in 0.036 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0028] Injected Tag in 0.751 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0009] Injected Sticker in 0.531 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0009] Injected Shader in 56.041 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0020] Injected Achievement in 0.056 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.440 ms +INFO - [G] 2025-01-08 01:08:29 :: INFO :: TIMER :: [0011] Injected Event in 0.015 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 757: 0.1 +INFO - [G] LONG DT @ 757: 0.1 +INFO - [G] LONG DT @ 757: 0.1 +INFO - [G] LONG DT @ 757: 0.1 +INFO - [G] LONG DT @ 757: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 758: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 759: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.1 +INFO - [G] LONG DT @ 760: 0.081941880000004 +INFO - [G] LONG DT @ 760: 0.067397723999979 +INFO - [G] LONG DT @ 761: 0.055312819199972 +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 568: 0.1 +INFO - [G] LONG DT @ 569: 0.1 +INFO - [G] LONG DT @ 569: 0.1 +INFO - [G] LONG DT @ 569: 0.082049099999977 +INFO - [G] LONG DT @ 569: 0.067290059999981 +INFO - [G] LONG DT @ 569: 0.055326167999992 +INFO - [G] LONG DT @ 571: 0.066332079068852 +INFO - [G] LONG DT @ 571: 0.056924363255082 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 657: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 658: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 659: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.1 +INFO - [G] LONG DT @ 660: 0.08137937999998 +INFO - [G] LONG DT @ 660: 0.066217124000008 +INFO - [G] LONG DT @ 661: 0.05408547919998 diff --git a/lovely/log/lovely-2025.01.08-02.05.16.log b/lovely/log/lovely-2025.01.08-02.05.16.log new file mode 100644 index 0000000..2451455 --- /dev/null +++ b/lovely/log/lovely-2025.01.08-02.05.16.log @@ -0,0 +1,814 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 135ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 12 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 19 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 77 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 121 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 47 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 203 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-08 02:05:18 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] { + [1] = table: 0x046f3a10 { + [1] = 0.99607843137255, + [2] = 0.37254901960784, + [3] = 0.33333333333333, + [4] = 1, + }, + [2] = table: 0x046f3a60 { + [1] = 0, + [2] = 0.6156862745098, + [3] = 1, + [4] = 1, + }, +} +INFO - [G] 2025-01-08 02:05:19 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-08 02:05:19 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-08 02:05:19 :: TRACE :: Loader :: Processing Mod file (Legacy header): MoreSpeeds.lua +INFO - [G] 2025-01-08 02:05:19 :: TRACE :: Loader :: Saving Mod Info: MoreSpeed +INFO - [G] 2025-01-08 02:05:19 :: ERROR :: Loader :: Found invalid metadata JSON file at C:\users\steamuser\AppData\Roaming\Balatro\Mods/Steamopollys-MoreSpeed-0.8.2/manifest.json, ignoring: [SMODS _ "src/loader.lua"]:212: id +INFO - [G] 2025-01-08 02:05:19 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-08 02:05:19 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-08 02:05:19 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-08 02:05:19 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-08 02:05:19 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.697 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0030] Injected Atlas in 777.868 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0025] Injected Sound in 28.353 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0032] Injected Stake in 2.176 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0008] Injected Rarity in 0.069 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.107 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0391] Injected Center in 1.860 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.007 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0027] Injected Blind in 0.039 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0002] Injected Seal in 0.106 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0004] Injected Suit in 0.282 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0013] Injected Rank in 0.241 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.071 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0016] Injected PokerHand in 1.828 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0031] Injected Challenge in 0.093 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0028] Injected Tag in 0.047 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0009] Injected Sticker in 3.352 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0009] Injected Shader in 49.105 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0020] Injected Achievement in 0.081 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.582 ms +INFO - [G] 2025-01-08 02:05:20 :: INFO :: TIMER :: [0011] Injected Event in 0.013 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 1195: 0.065513680611866 +INFO - [G] LONG DT @ 1195: 0.059834684489461 +INFO - [G] LONG DT @ 1195: 0.055208227591607 +INFO - [G] LONG DT @ 2138: 0.067980991829622 +INFO - [G] LONG DT @ 2138: 0.061686373463664 +INFO - [G] LONG DT @ 2138: 0.056641658770973 +INFO - [G] LONG DT @ 2138: 0.052576427016779 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2614: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2615: 0.1 +INFO - [G] LONG DT @ 2616: 0.1 +INFO - [G] LONG DT @ 2616: 0.1 +INFO - [G] LONG DT @ 2616: 0.1 +INFO - [G] LONG DT @ 2616: 0.1 +INFO - [G] LONG DT @ 2616: 0.1 +INFO - [G] LONG DT @ 2616: 0.1 +INFO - [G] LONG DT @ 2616: 0.081539159999993 +INFO - [G] LONG DT @ 2616: 0.06645142800004 +INFO - [G] LONG DT @ 2616: 0.05440646240003 +INFO - [G] LONG DT @ 2617: 0.1 +INFO - [G] LONG DT @ 2617: 0.1 +INFO - [G] LONG DT @ 2618: 0.1 +INFO - [G] LONG DT @ 2618: 0.081775039999939 +INFO - [G] LONG DT @ 2618: 0.066723771999954 +INFO - [G] LONG DT @ 2618: 0.054492697599944 +INFO - [G] LONG DT @ 2619: 0.1 +INFO - [G] LONG DT @ 2619: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2620: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2621: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2622: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2623: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2624: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2625: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2626: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2627: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2628: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2629: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2630: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2631: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2632: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2633: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2634: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2635: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2636: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2637: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2638: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2639: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2640: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2641: 0.1 +INFO - [G] LONG DT @ 2642: 0.1 +INFO - [G] LONG DT @ 2642: 0.1 +INFO - [G] LONG DT @ 2642: 0.1 +INFO - [G] LONG DT @ 2642: 0.1 +INFO - [G] LONG DT @ 2642: 0.081699619999999 +INFO - [G] LONG DT @ 2642: 0.066955996000073 +INFO - [G] LONG DT @ 2642: 0.05506961680005 +INFO - [G] LONG DT @ 2819: 0.078233621775879 +INFO - [G] LONG DT @ 2819: 0.069938437420648 +INFO - [G] LONG DT @ 2819: 0.06331674993652 +INFO - [G] LONG DT @ 2819: 0.057957119949289 +INFO - [G] LONG DT @ 2819: 0.053641975959382 +INFO - [G] LONG DT @ 2819: 0.050245880767558 +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 194: 0.071740551129342 +INFO - [G] LONG DT @ 194: 0.065649660903379 +INFO - [G] LONG DT @ 194: 0.060814248722684 +INFO - [G] LONG DT @ 194: 0.056813078978159 +INFO - [G] LONG DT @ 194: 0.053617323182511 +INFO - [G] LONG DT @ 194: 0.051086578546088 +INFO - [G] LONG DT @ 196: 0.091272774803762 +INFO - [G] LONG DT @ 196: 0.083941239842983 +INFO - [G] LONG DT @ 196: 0.076216871874417 +INFO - [G] LONG DT @ 196: 0.070000177499522 +INFO - [G] LONG DT @ 197: 0.065033461999598 +INFO - [G] LONG DT @ 197: 0.061090349599607 +INFO - [G] LONG DT @ 197: 0.057885019679757 +INFO - [G] LONG DT @ 197: 0.055433015743755 +INFO - [G] LONG DT @ 197: 0.053460112594947 +INFO - [G] LONG DT @ 197: 0.051912350075951 +INFO - [G] LONG DT @ 197: 0.050586360060833 +INFO - [G] LONG DT @ 199: 0.097694309481017 +INFO - [G] LONG DT @ 199: 0.088001087584726 +INFO - [G] LONG DT @ 199: 0.080328370067829 +INFO - [G] LONG DT @ 199: 0.074060296054356 +INFO - [G] LONG DT @ 199: 0.0691370568435 +INFO - [G] LONG DT @ 199: 0.065180025474647 +INFO - [G] LONG DT @ 199: 0.062111960379742 +INFO - [G] LONG DT @ 199: 0.059300948303817 +INFO - [G] LONG DT @ 199: 0.057272998643159 +INFO - [G] LONG DT @ 199: 0.055614338914531 +INFO - [G] LONG DT @ 199: 0.054506611131621 +INFO - [G] LONG DT @ 199: 0.053565028905273 +INFO - [G] LONG DT @ 200: 0.052918123124077 +INFO - [G] LONG DT @ 200: 0.052277278499426 +INFO - [G] LONG DT @ 200: 0.051922182799429 +INFO - [G] LONG DT @ 200: 0.051481306239644 +INFO - [G] LONG DT @ 200: 0.051265364991622 +INFO - [G] LONG DT @ 200: 0.050990871993395 +INFO - [G] LONG DT @ 200: 0.050951137594657 +INFO - [G] LONG DT @ 200: 0.05074659007568 +INFO - [G] LONG DT @ 200: 0.050694912060638 +INFO - [G] LONG DT @ 200: 0.050578969648453 +INFO - [G] LONG DT @ 200: 0.050643875718672 +INFO - [G] LONG DT @ 200: 0.050578220575024 +INFO - [G] LONG DT @ 200: 0.050874116459969 +INFO - [G] LONG DT @ 200: 0.051354713167942 +INFO - [G] LONG DT @ 200: 0.051427710534519 +INFO - [G] LONG DT @ 200: 0.051501748427547 +INFO - [G] LONG DT @ 200: 0.051663718742116 +INFO - [G] LONG DT @ 200: 0.05174329499358 +INFO - [G] LONG DT @ 200: 0.051846075994861 +INFO - [G] LONG DT @ 200: 0.051824380795848 +INFO - [G] LONG DT @ 201: 0.051907244636696 +INFO - [G] LONG DT @ 201: 0.051908795709359 +INFO - [G] LONG DT @ 201: 0.052026296567447 +INFO - [G] LONG DT @ 201: 0.052128777254097 +INFO - [G] LONG DT @ 201: 0.052242481803205 +INFO - [G] LONG DT @ 201: 0.052358945442574 +INFO - [G] LONG DT @ 201: 0.052447236354019 +INFO - [G] LONG DT @ 201: 0.052506329083342 +INFO - [G] LONG DT @ 201: 0.052631863266628 +INFO - [G] LONG DT @ 201: 0.052666870613338 +INFO - [G] LONG DT @ 201: 0.052716056490637 +INFO - [G] LONG DT @ 201: 0.052785585192543 +INFO - [G] LONG DT @ 201: 0.1 +INFO - [G] LONG DT @ 201: 0.090691880000013 +INFO - [G] LONG DT @ 201: 0.083268543999933 +INFO - [G] LONG DT @ 201: 0.077285855199919 +INFO - [G] LONG DT @ 202: 0.072541424159989 +INFO - [G] LONG DT @ 202: 0.068734379327971 +INFO - [G] LONG DT @ 202: 0.065741463462343 +INFO - [G] LONG DT @ 202: 0.063424070769984 +INFO - [G] LONG DT @ 202: 0.061578436615844 +INFO - [G] LONG DT @ 202: 0.060105169292829 +INFO - [G] LONG DT @ 202: 0.058898875434105 +INFO - [G] LONG DT @ 202: 0.057740500347411 +INFO - [G] LONG DT @ 202: 0.056856160277918 +INFO - [G] LONG DT @ 202: 0.056334868222269 +INFO - [G] LONG DT @ 202: 0.056264014577837 +INFO - [G] LONG DT @ 202: 0.054580111662246 +INFO - [G] LONG DT @ 275: 0.1 +INFO - [G] LONG DT @ 275: 0.1 +INFO - [G] LONG DT @ 275: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.1 +INFO - [G] LONG DT @ 276: 0.085295939999996 +INFO - [G] LONG DT @ 276: 0.072963631999904 +INFO - [G] LONG DT @ 277: 0.062996905600022 +INFO - [G] LONG DT @ 277: 0.054947484479912 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 773: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 774: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 775: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 776: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 777: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 778: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 779: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 780: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 781: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 782: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 783: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 784: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 785: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 786: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 787: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 788: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 789: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 790: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 791: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 792: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 793: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 794: 0.1 +INFO - [G] LONG DT @ 795: 0.1 +INFO - [G] LONG DT @ 795: 0.1 +INFO - [G] LONG DT @ 795: 0.1 +INFO - [G] LONG DT @ 795: 0.1 +INFO - [G] LONG DT @ 795: 0.1 +INFO - [G] LONG DT @ 795: 0.1 +INFO - [G] LONG DT @ 795: 0.1 +INFO - [G] LONG DT @ 795: 0.084980339999929 +INFO - [G] LONG DT @ 795: 0.072510191999958 +INFO - [G] LONG DT @ 795: 0.062349313599934 +INFO - [G] LONG DT @ 795: 0.053827370880011 +INFO - [G] LONG DT @ 798: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 799: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 800: 0.1 +INFO - [G] LONG DT @ 801: 0.1 +INFO - [G] LONG DT @ 801: 0.1 +INFO - [G] LONG DT @ 801: 0.1 +INFO - [G] LONG DT @ 801: 0.1 +INFO - [G] LONG DT @ 801: 0.1 +INFO - [G] LONG DT @ 801: 0.1 +INFO - [G] LONG DT @ 801: 0.084517260000102 +INFO - [G] LONG DT @ 801: 0.071884148000121 +INFO - [G] LONG DT @ 801: 0.061747078400115 +INFO - [G] LONG DT @ 801: 0.053707842720085 +INFO - [G] LONG DT @ 2165: 0.1 +INFO - [G] LONG DT @ 2165: 0.1 +INFO - [G] LONG DT @ 2165: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2166: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2167: 0.1 +INFO - [G] LONG DT @ 2168: 0.1 +INFO - [G] LONG DT @ 2168: 0.1 +INFO - [G] LONG DT @ 2168: 0.1 +INFO - [G] LONG DT @ 2168: 0.1 +INFO - [G] LONG DT @ 2168: 0.1 +INFO - [G] LONG DT @ 2168: 0.082458560000014 +INFO - [G] LONG DT @ 2168: 0.068404507999861 +INFO - [G] LONG DT @ 2168: 0.056868526400026 +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load diff --git a/lovely/log/lovely-2025.01.08-17.38.33.log b/lovely/log/lovely-2025.01.08-17.38.33.log new file mode 100644 index 0000000..598c3d7 --- /dev/null +++ b/lovely/log/lovely-2025.01.08-17.38.33.log @@ -0,0 +1,301 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 127ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-08 17:38:36 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-08 17:38:36 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-08 17:38:36 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-08 17:38:37 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-08 17:38:37 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-08 17:38:37 :: TRACE :: Loader :: Processing Mod file (Legacy header): MoreSpeeds.lua +INFO - [G] 2025-01-08 17:38:37 :: TRACE :: Loader :: Saving Mod Info: MoreSpeed +INFO - [G] 2025-01-08 17:38:37 :: ERROR :: Loader :: Found invalid metadata JSON file at C:\users\steamuser\AppData\Roaming\Balatro\Mods/Steamopollys-MoreSpeed-0.8.2/manifest.json, ignoring: [SMODS _ "src/loader.lua"]:212: id +INFO - [G] 2025-01-08 17:38:37 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-08 17:38:37 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-08 17:38:37 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.657 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0031] Injected Atlas in 785.420 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0025] Injected Sound in 20.950 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0032] Injected Stake in 0.846 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0008] Injected Rarity in 0.022 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.046 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0391] Injected Center in 1.724 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.010 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0027] Injected Blind in 0.046 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0002] Injected Seal in 0.236 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0004] Injected Suit in 0.076 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0013] Injected Rank in 0.083 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.030 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.080 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0031] Injected Challenge in 0.052 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0028] Injected Tag in 0.166 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0009] Injected Sticker in 0.836 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0009] Injected Shader in 52.758 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0020] Injected Achievement in 0.060 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.535 ms +INFO - [G] 2025-01-08 17:38:37 :: INFO :: TIMER :: [0011] Injected Event in 0.014 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 39: 0.065712979523191 +INFO - [G] LONG DT @ 39: 0.060389583618553 +INFO - [G] LONG DT @ 39: 0.056165686894845 +INFO - [G] LONG DT @ 39: 0.052731249515871 +INFO - [G] LONG DT @ 40: 0.050146759612697 +INFO - [G] LONG DT @ 111: 0.095699457251497 +INFO - [G] LONG DT @ 111: 0.080804165801191 +INFO - [G] LONG DT @ 111: 0.068675732640958 +INFO - [G] LONG DT @ 111: 0.058927466112769 +INFO - [G] LONG DT @ 111: 0.051127652890209 +INFO - [G] LONG DT @ 112: 0.096535065488646 +INFO - [G] LONG DT @ 112: 0.081490312390913 +INFO - [G] LONG DT @ 113: 0.069177289912728 +INFO - [G] LONG DT @ 113: 0.059315731930188 +INFO - [G] LONG DT @ 113: 0.051432785544151 +INFO - [G] LONG DT @ 116: 0.094952365049208 +INFO - [G] LONG DT @ 116: 0.080242152039362 +INFO - [G] LONG DT @ 116: 0.068324881631487 +INFO - [G] LONG DT @ 116: 0.058683005305196 +INFO - [G] LONG DT @ 116: 0.050930144244158 +INFO - [G] LONG DT @ 117: 0.097258736826162 +INFO - [G] LONG DT @ 117: 0.082028629460933 +INFO - [G] LONG DT @ 117: 0.069546203568743 +INFO - [G] LONG DT @ 117: 0.059564662854991 +INFO - [G] LONG DT @ 117: 0.051638010283997 +INFO - [G] LONG DT @ 118: 0.098028523224237 +INFO - [G] LONG DT @ 118: 0.082668138579396 +INFO - [G] LONG DT @ 118: 0.07006381086351 +INFO - [G] LONG DT @ 118: 0.059985808690812 +INFO - [G] LONG DT @ 118: 0.052064346952648 +INFO - [G] LONG DT @ 122: 0.09540292718411 +INFO - [G] LONG DT @ 122: 0.080490081747291 +INFO - [G] LONG DT @ 122: 0.06832148539783 +INFO - [G] LONG DT @ 122: 0.058542668318261 +INFO - [G] LONG DT @ 122: 0.050775934654615 +INFO - [G] LONG DT @ 122: 0.096532873421755 +INFO - [G] LONG DT @ 123: 0.081521458737407 +INFO - [G] LONG DT @ 123: 0.06927398698992 +INFO - [G] LONG DT @ 123: 0.05948656959194 +INFO - [G] LONG DT @ 123: 0.051520375673556 +INFO - [G] LONG DT @ 123: 0.097209348797013 +INFO - [G] LONG DT @ 123: 0.081988359037613 +INFO - [G] LONG DT @ 124: 0.069696227230091 +INFO - [G] LONG DT @ 124: 0.059829721784076 +INFO - [G] LONG DT @ 124: 0.052009517427251 +INFO - [G] LONG DT @ 124: 0.097376561111689 +INFO - [G] LONG DT @ 124: 0.082168188889353 +INFO - [G] LONG DT @ 124: 0.069937571111482 +INFO - [G] LONG DT @ 124: 0.060007256889186 +INFO - [G] LONG DT @ 124: 0.052020225511347 +INFO - [G] LONG DT @ 125: 0.1 +INFO - [G] LONG DT @ 125: 0.08424352 +INFO - [G] LONG DT @ 125: 0.071398515999998 +INFO - [G] LONG DT @ 125: 0.061317012800006 +INFO - [G] LONG DT @ 125: 0.053182570239996 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 166: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 167: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.1 +INFO - [G] LONG DT @ 168: 0.082439040000002 +INFO - [G] LONG DT @ 169: 0.068138351999998 +INFO - [G] LONG DT @ 169: 0.056457401599999 +INFO - [G] LONG DT @ 178: 0.1 +INFO - [G] LONG DT @ 178: 0.1 +INFO - [G] LONG DT @ 178: 0.1 +INFO - [G] LONG DT @ 178: 0.1 +INFO - [G] LONG DT @ 178: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 179: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 180: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 181: 0.1 +INFO - [G] LONG DT @ 182: 0.1 +INFO - [G] LONG DT @ 182: 0.1 +INFO - [G] LONG DT @ 182: 0.082272540000001 +INFO - [G] LONG DT @ 182: 0.067807391999994 +INFO - [G] LONG DT @ 182: 0.056140173600001 +INFO - [G] LONG DT @ 12: 0.1 +INFO - [G] LONG DT @ 12: 0.1 +INFO - [G] LONG DT @ 13: 0.08107439999997 +INFO - [G] LONG DT @ 13: 0.066038119999979 +INFO - [G] LONG DT @ 13: 0.05402949599998 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 14: 0.08114552 +INFO - [G] LONG DT @ 14: 0.06609379599999 +INFO - [G] LONG DT @ 14: 0.054067856799998 +INFO - [G] LONG DT @ 14: 0.081042646327903 +INFO - [G] LONG DT @ 14: 0.067260957062321 +INFO - [G] LONG DT @ 15: 0.056172265649858 +INFO - [G] LONG DT @ 16: 0.087443327538586 +INFO - [G] LONG DT @ 16: 0.072506082030878 +INFO - [G] LONG DT @ 16: 0.060509325624702 +INFO - [G] LONG DT @ 16: 0.050798180499756 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.08268916 +INFO - [G] LONG DT @ 17: 0.068605627999999 +INFO - [G] LONG DT @ 17: 0.057244302400021 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 43: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 44: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 46: 0.1 +INFO - [G] LONG DT @ 46: 0.1 +INFO - [G] LONG DT @ 46: 0.1 +INFO - [G] LONG DT @ 46: 0.1 +INFO - [G] LONG DT @ 46: 0.1 diff --git a/lovely/log/lovely-2025.01.08-18.19.46.log b/lovely/log/lovely-2025.01.08-18.19.46.log new file mode 100644 index 0000000..dea18c7 --- /dev/null +++ b/lovely/log/lovely-2025.01.08-18.19.46.log @@ -0,0 +1,132 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 139ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-08 18:19:49 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-08 18:19:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-08 18:19:49 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-08 18:19:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-08 18:19:49 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-08 18:19:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): MoreSpeeds.lua +INFO - [G] 2025-01-08 18:19:49 :: TRACE :: Loader :: Saving Mod Info: MoreSpeed +INFO - [G] 2025-01-08 18:19:49 :: ERROR :: Loader :: Found invalid metadata JSON file at C:\users\steamuser\AppData\Roaming\Balatro\Mods/Steamopollys-MoreSpeed-0.8.2/manifest.json, ignoring: [SMODS _ "src/loader.lua"]:212: id +INFO - [G] 2025-01-08 18:19:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-08 18:19:49 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-08 18:19:49 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-08 18:19:49 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-08 18:19:49 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.667 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0031] Injected Atlas in 781.149 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0025] Injected Sound in 21.225 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0032] Injected Stake in 0.755 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0008] Injected Rarity in 0.022 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.057 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0391] Injected Center in 1.728 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.010 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0027] Injected Blind in 0.047 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0002] Injected Seal in 0.435 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0004] Injected Suit in 0.096 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0013] Injected Rank in 0.085 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.033 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.069 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0031] Injected Challenge in 0.041 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0028] Injected Tag in 0.058 ms +INFO - [G] 2025-01-08 18:19:50 :: INFO :: TIMER :: [0009] Injected Sticker in 0.087 ms +INFO - [G] 2025-01-08 18:19:51 :: INFO :: TIMER :: [0009] Injected Shader in 883.524 ms +INFO - [G] 2025-01-08 18:19:51 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-08 18:19:51 :: INFO :: TIMER :: [0020] Injected Achievement in 0.061 ms +INFO - [G] 2025-01-08 18:19:51 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.829 ms +INFO - [G] 2025-01-08 18:19:51 :: INFO :: TIMER :: [0011] Injected Event in 0.013 ms +INFO - [G] LONG DT @ 0: 0.1 +INFO - [G] LONG DT @ 0: 0.1 +INFO - [G] LONG DT @ 0: 0.08089688 +INFO - [G] LONG DT @ 0: 0.064980484 +INFO - [G] LONG DT @ 0: 0.0522670872 +INFO - [G] LONG DT @ 27: 0.088014122060336 +INFO - [G] LONG DT @ 27: 0.07460833764827 +INFO - [G] LONG DT @ 27: 0.063927730118615 +INFO - [G] LONG DT @ 28: 0.055742924094892 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.08403702 +INFO - [G] LONG DT @ 29: 0.071189556 +INFO - [G] LONG DT @ 29: 0.0608984248 +INFO - [G] LONG DT @ 29: 0.05286677984 diff --git a/lovely/log/lovely-2025.01.08-18.21.41.log b/lovely/log/lovely-2025.01.08-18.21.41.log new file mode 100644 index 0000000..da870e6 --- /dev/null +++ b/lovely/log/lovely-2025.01.08-18.21.41.log @@ -0,0 +1,275 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 134ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-08 18:21:44 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-08 18:21:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-08 18:21:45 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-08 18:21:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-08 18:21:45 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-08 18:21:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-08 18:21:45 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-08 18:21:45 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.689 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0031] Injected Atlas in 780.615 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0025] Injected Sound in 22.243 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0032] Injected Stake in 0.845 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0008] Injected Rarity in 0.026 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.076 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0391] Injected Center in 2.058 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.028 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0027] Injected Blind in 0.052 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0002] Injected Seal in 0.045 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0004] Injected Suit in 0.095 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0013] Injected Rank in 0.078 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.029 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.103 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0031] Injected Challenge in 0.057 ms +INFO - [G] 2025-01-08 18:21:45 :: INFO :: TIMER :: [0028] Injected Tag in 0.175 ms +INFO - [G] 2025-01-08 18:21:46 :: INFO :: TIMER :: [0009] Injected Sticker in 0.613 ms +INFO - [G] 2025-01-08 18:21:46 :: INFO :: TIMER :: [0009] Injected Shader in 52.614 ms +INFO - [G] 2025-01-08 18:21:46 :: INFO :: TIMER :: [0000] Injected Keybind in 0.019 ms +INFO - [G] 2025-01-08 18:21:46 :: INFO :: TIMER :: [0020] Injected Achievement in 0.053 ms +INFO - [G] 2025-01-08 18:21:47 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.421 ms +INFO - [G] 2025-01-08 18:21:47 :: INFO :: TIMER :: [0011] Injected Event in 0.031 ms +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.09256014 +INFO - [G] LONG DT @ 14: 0.074577492 +INFO - [G] LONG DT @ 14: 0.0607911936 +INFO - [G] LONG DT @ 60: 0.1 +INFO - [G] LONG DT @ 60: 0.1 +INFO - [G] LONG DT @ 60: 0.1 +INFO - [G] LONG DT @ 60: 0.1 +INFO - [G] LONG DT @ 60: 0.1 +INFO - [G] LONG DT @ 60: 0.1 +INFO - [G] LONG DT @ 60: 0.1 +INFO - [G] LONG DT @ 61: 0.1 +INFO - [G] LONG DT @ 61: 0.08106802 +INFO - [G] LONG DT @ 61: 0.065711856000001 +INFO - [G] LONG DT @ 61: 0.053207244800002 +INFO - [G] LONG DT @ 921: 0.1 +INFO - [G] LONG DT @ 921: 0.1 +INFO - [G] LONG DT @ 921: 0.1 +INFO - [G] LONG DT @ 921: 0.1 +INFO - [G] LONG DT @ 921: 0.1 +INFO - [G] LONG DT @ 921: 0.1 +INFO - [G] LONG DT @ 921: 0.086798800000001 +INFO - [G] LONG DT @ 921: 0.070583059999994 +INFO - [G] LONG DT @ 922: 0.05730684800001 +INFO - [G] LONG DT @ 1015: 0.1 +INFO - [G] LONG DT @ 1015: 0.1 +INFO - [G] LONG DT @ 1015: 0.1 +INFO - [G] LONG DT @ 1015: 0.1 +INFO - [G] LONG DT @ 1015: 0.1 +INFO - [G] LONG DT @ 1015: 0.1 +INFO - [G] LONG DT @ 1015: 0.1 +INFO - [G] LONG DT @ 1015: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1016: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1017: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.1 +INFO - [G] LONG DT @ 1018: 0.081382860000012 +INFO - [G] LONG DT @ 1019: 0.066360148000007 +INFO - [G] LONG DT @ 1019: 0.054098598400019 +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 458: 0.061078604946058 +INFO - [G] LONG DT @ 458: 0.054626863956966 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1165: 0.1 +INFO - [G] LONG DT @ 1166: 0.1 +INFO - [G] LONG DT @ 1166: 0.081653579999947 +INFO - [G] LONG DT @ 1166: 0.066631924000088 +INFO - [G] LONG DT @ 1166: 0.055204399199963 +INFO - [G] LONG DT @ 2309: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2310: 0.1 +INFO - [G] LONG DT @ 2311: 0.1 +INFO - [G] LONG DT @ 2311: 0.1 +INFO - [G] LONG DT @ 2311: 0.1 +INFO - [G] LONG DT @ 2311: 0.1 +INFO - [G] LONG DT @ 2311: 0.1 +INFO - [G] LONG DT @ 2311: 0.091925340000016 +INFO - [G] LONG DT @ 2311: 0.075753931999883 +INFO - [G] LONG DT @ 2311: 0.06252940559998 +INFO - [G] LONG DT @ 2311: 0.051754684480038 +INFO - [G] LONG DT @ 2313: 0.1 +INFO - [G] LONG DT @ 2313: 0.1 +INFO - [G] LONG DT @ 2313: 0.1 +INFO - [G] LONG DT @ 2313: 0.082357920000031 +INFO - [G] LONG DT @ 2313: 0.068063896000043 +INFO - [G] LONG DT @ 2313: 0.056773116799947 +INFO - [G] LONG DT @ 2325: 0.1 +INFO - [G] LONG DT @ 2325: 0.1 +INFO - [G] LONG DT @ 2325: 0.084979600000115 +INFO - [G] LONG DT @ 2326: 0.072676400000004 +INFO - [G] LONG DT @ 2326: 0.062823440000032 +INFO - [G] LONG DT @ 2326: 0.055019411999946 +INFO - [G] LONG DT @ 2443: 0.1 +INFO - [G] LONG DT @ 2443: 0.1 +INFO - [G] LONG DT @ 2443: 0.1 +INFO - [G] LONG DT @ 2443: 0.081888280000094 +INFO - [G] LONG DT @ 2443: 0.067131484000078 +INFO - [G] LONG DT @ 2443: 0.055165087200071 +INFO - [G] LONG DT @ 2538: 0.051000998776919 +INFO - [G] LONG DT @ 2716: 0.1 +INFO - [G] LONG DT @ 2716: 0.1 +INFO - [G] LONG DT @ 2716: 0.084713020000054 +INFO - [G] LONG DT @ 2716: 0.072307556 +INFO - [G] LONG DT @ 2716: 0.062347904799982 +INFO - [G] LONG DT @ 2716: 0.054740163839998 +INFO - [G] LONG DT @ 4107: 0.050062153510581 +INFO - [G] LONG DT @ 4206: 0.06230287349689 +INFO - [G] LONG DT @ 4206: 0.055591798797241 +INFO - [G] LONG DT @ 4208: 0.081529427191557 +INFO - [G] LONG DT @ 4208: 0.090117361753097 +INFO - [G] LONG DT @ 4208: 0.07642262940246 +INFO - [G] LONG DT @ 4208: 0.065446543521991 +INFO - [G] LONG DT @ 4208: 0.056319414817464 +INFO - [G] LONG DT @ 4443: 0.057108973628938 +INFO - [G] LONG DT @ 4560: 0.067182903383624 +INFO - [G] LONG DT @ 4560: 0.059166322706767 +INFO - [G] LONG DT @ 4560: 0.052543658165696 +INFO - [G] LONG DT @ 4592: 0.063660842852936 +INFO - [G] LONG DT @ 4592: 0.054208014282502 +INFO - [G] LONG DT @ 4633: 0.055609383440104 +INFO - [G] LONG DT @ 4633: 0.058235466751902 +INFO - [G] LONG DT @ 4678: 0.060809812721608 +INFO - [G] LONG DT @ 4678: 0.05218811017715 +INFO - [G] LONG DT @ 4678: 0.1 +INFO - [G] LONG DT @ 4678: 0.082854800000205 +INFO - [G] LONG DT @ 4678: 0.068664159999957 +INFO - [G] LONG DT @ 4678: 0.057290388000128 +INFO - [G] LONG DT @ 116: 0.1 +INFO - [G] LONG DT @ 116: 0.1 +INFO - [G] LONG DT @ 116: 0.1 +INFO - [G] LONG DT @ 116: 0.08920961999982 +INFO - [G] LONG DT @ 117: 0.07305883600004 +INFO - [G] LONG DT @ 117: 0.059714548800056 diff --git a/lovely/log/lovely-2025.01.08-21.02.39.log b/lovely/log/lovely-2025.01.08-21.02.39.log new file mode 100644 index 0000000..f46c3fb --- /dev/null +++ b/lovely/log/lovely-2025.01.08-21.02.39.log @@ -0,0 +1,276 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 129ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-08 21:02:42 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-08 21:02:43 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-08 21:02:43 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-08 21:02:43 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-08 21:02:43 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-08 21:02:43 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-08 21:02:43 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-08 21:02:43 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.665 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0031] Injected Atlas in 780.808 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0025] Injected Sound in 20.034 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0032] Injected Stake in 0.758 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0008] Injected Rarity in 0.021 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.045 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0391] Injected Center in 1.673 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.026 ms +INFO - [G] 2025-01-08 21:02:43 :: INFO :: TIMER :: [0027] Injected Blind in 0.065 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0002] Injected Seal in 0.042 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0004] Injected Suit in 0.086 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0013] Injected Rank in 0.087 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.057 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.071 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0031] Injected Challenge in 0.062 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0028] Injected Tag in 0.057 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0009] Injected Sticker in 0.084 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0009] Injected Shader in 166.273 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0020] Injected Achievement in 0.050 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.250 ms +INFO - [G] 2025-01-08 21:02:44 :: INFO :: TIMER :: [0011] Injected Event in 0.013 ms +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 428: 0.1 +INFO - [G] LONG DT @ 429: 0.1 +INFO - [G] LONG DT @ 429: 0.1 +INFO - [G] LONG DT @ 429: 0.1 +INFO - [G] LONG DT @ 429: 0.081563339999996 +INFO - [G] LONG DT @ 429: 0.066743972000001 +INFO - [G] LONG DT @ 429: 0.054993957599995 +INFO - [G] LONG DT @ 531: 0.1 +INFO - [G] LONG DT @ 531: 0.1 +INFO - [G] LONG DT @ 531: 0.1 +INFO - [G] LONG DT @ 531: 0.1 +INFO - [G] LONG DT @ 531: 0.1 +INFO - [G] LONG DT @ 531: 0.1 +INFO - [G] LONG DT @ 531: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 532: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 533: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 534: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 535: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 536: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 537: 0.1 +INFO - [G] LONG DT @ 538: 0.1 +INFO - [G] LONG DT @ 538: 0.1 +INFO - [G] LONG DT @ 538: 0.1 +INFO - [G] LONG DT @ 538: 0.1 +INFO - [G] LONG DT @ 538: 0.1 +INFO - [G] LONG DT @ 538: 0.1 +INFO - [G] LONG DT @ 538: 0.0816887 +INFO - [G] LONG DT @ 538: 0.066798879999987 +INFO - [G] LONG DT @ 538: 0.05471962399999 +INFO - [G] LONG DT @ 804: 0.1 +INFO - [G] LONG DT @ 804: 0.1 +INFO - [G] LONG DT @ 804: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 805: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 806: 0.1 +INFO - [G] LONG DT @ 807: 0.1 +INFO - [G] LONG DT @ 807: 0.1 +INFO - [G] LONG DT @ 807: 0.1 +INFO - [G] LONG DT @ 807: 0.1 +INFO - [G] LONG DT @ 807: 0.1 +INFO - [G] LONG DT @ 807: 0.1 +INFO - [G] LONG DT @ 807: 0.08180848 +INFO - [G] LONG DT @ 807: 0.066933044000001 +INFO - [G] LONG DT @ 807: 0.054882335200005 +INFO - [G] LONG DT @ 823: 0.1 +INFO - [G] LONG DT @ 823: 0.1 +INFO - [G] LONG DT @ 823: 0.1 +INFO - [G] LONG DT @ 823: 0.1 +INFO - [G] LONG DT @ 823: 0.1 +INFO - [G] LONG DT @ 823: 0.1 +INFO - [G] LONG DT @ 823: 0.1 +INFO - [G] LONG DT @ 823: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 824: 0.1 +INFO - [G] LONG DT @ 825: 0.1 +INFO - [G] LONG DT @ 825: 0.1 +INFO - [G] LONG DT @ 825: 0.1 +INFO - [G] LONG DT @ 825: 0.1 +INFO - [G] LONG DT @ 825: 0.1 +INFO - [G] LONG DT @ 825: 0.1 +INFO - [G] LONG DT @ 825: 0.1 +INFO - [G] LONG DT @ 825: 0.081634119999994 +INFO - [G] LONG DT @ 825: 0.066672395999994 +INFO - [G] LONG DT @ 825: 0.056100496800007 +INFO - [G] LONG DT @ 830: 0.1 +INFO - [G] LONG DT @ 830: 0.1 +INFO - [G] LONG DT @ 831: 0.1 +INFO - [G] LONG DT @ 831: 0.1 +INFO - [G] LONG DT @ 831: 0.1 +INFO - [G] LONG DT @ 831: 0.1 +INFO - [G] LONG DT @ 831: 0.1 +INFO - [G] LONG DT @ 831: 0.084873039999984 +INFO - [G] LONG DT @ 831: 0.069626072 +INFO - [G] LONG DT @ 831: 0.057503137599993 diff --git a/lovely/log/lovely-2025.01.09-00.41.50.log b/lovely/log/lovely-2025.01.09-00.41.50.log new file mode 100644 index 0000000..b8c5dff --- /dev/null +++ b/lovely/log/lovely-2025.01.09-00.41.50.log @@ -0,0 +1,279 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 149ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 00:41:53 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 00:41:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 00:41:54 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 00:41:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 00:41:54 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 00:41:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 00:41:54 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 00:41:54 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 00:41:54 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-09 00:41:54 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.647 ms +INFO - [G] 2025-01-09 00:41:55 :: INFO :: TIMER :: [0031] Injected Atlas in 815.097 ms +INFO - [G] 2025-01-09 00:41:55 :: INFO :: TIMER :: [0025] Injected Sound in 23.004 ms +INFO - [G] 2025-01-09 00:41:55 :: INFO :: TIMER :: [0032] Injected Stake in 1.041 ms +INFO - [G] 2025-01-09 00:41:55 :: INFO :: TIMER :: [0008] Injected Rarity in 0.022 ms +INFO - [G] 2025-01-09 00:41:56 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.121 ms +INFO - [G] 2025-01-09 00:41:57 :: INFO :: TIMER :: [0391] Injected Center in 2.134 ms +INFO - [G] 2025-01-09 00:41:58 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.044 ms +INFO - [G] 2025-01-09 00:41:59 :: INFO :: TIMER :: [0027] Injected Blind in 0.541 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0002] Injected Seal in 0.082 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0004] Injected Suit in 0.195 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0013] Injected Rank in 0.091 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.034 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.145 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0031] Injected Challenge in 0.076 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0028] Injected Tag in 0.672 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0009] Injected Sticker in 0.634 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0009] Injected Shader in 58.053 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0020] Injected Achievement in 0.147 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.512 ms +INFO - [G] 2025-01-09 00:42:00 :: INFO :: TIMER :: [0011] Injected Event in 0.128 ms +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 242: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 243: 0.1 +INFO - [G] LONG DT @ 244: 0.082632259999996 +INFO - [G] LONG DT @ 244: 0.068562608000001 +INFO - [G] LONG DT @ 244: 0.056856906399998 +INFO - [G] LONG DT @ 245: 0.1 +INFO - [G] LONG DT @ 245: 0.085794560000004 +INFO - [G] LONG DT @ 245: 0.074059648000008 +INFO - [G] LONG DT @ 245: 0.064583038400001 +INFO - [G] LONG DT @ 245: 0.056899570719997 +INFO - [G] LONG DT @ 245: 0.050576436576004 +INFO - [G] LONG DT @ 2620: 0.083872598038467 +INFO - [G] LONG DT @ 2620: 0.071462498430776 +INFO - [G] LONG DT @ 2620: 0.06149121874464 +INFO - [G] LONG DT @ 2621: 0.053595214995748 +INFO - [G] LONG DT @ 2623: 0.065053737025595 +INFO - [G] LONG DT @ 2623: 0.053883289620479 +INFO - [G] LONG DT @ 2690: 0.050212301610195 +INFO - [G] LONG DT @ 2693: 0.060296654117329 +INFO - [G] LONG DT @ 2834: 0.091615562591627 +INFO - [G] LONG DT @ 2834: 0.077839310073238 +INFO - [G] LONG DT @ 2835: 0.066596148058642 +INFO - [G] LONG DT @ 2835: 0.061569958446904 +INFO - [G] LONG DT @ 2835: 0.057908826757507 +INFO - [G] LONG DT @ 2835: 0.051225401406005 +INFO - [G] LONG DT @ 2980: 0.1 +INFO - [G] LONG DT @ 2980: 0.090045579999951 +INFO - [G] LONG DT @ 2980: 0.077122443999928 +INFO - [G] LONG DT @ 2981: 0.06597911519997 +INFO - [G] LONG DT @ 2981: 0.057087452159968 +INFO - [G] LONG DT @ 2995: 0.091635811342222 +INFO - [G] LONG DT @ 2996: 0.077788569073772 +INFO - [G] LONG DT @ 2996: 0.066564055259036 +INFO - [G] LONG DT @ 2996: 0.057793044207199 +INFO - [G] LONG DT @ 2996: 0.050740935365784 +INFO - [G] LONG DT @ 3094: 0.051310382816534 +INFO - [G] LONG DT @ 3183: 0.052398811267431 +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 01:35:42 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] LONG DT @ 3217: 0.075648980118892 +INFO - [G] LONG DT @ 3217: 0.065100084095132 +INFO - [G] LONG DT @ 3218: 0.056437267276064 +INFO - [G] LONG DT @ 3218: 0.087668246337747 +INFO - [G] LONG DT @ 3218: 0.072008577070179 +INFO - [G] LONG DT @ 3218: 0.059283001656161 +INFO - [G] LONG DT @ 3226: 0.1 +INFO - [G] LONG DT @ 3226: 0.081910039999984 +INFO - [G] LONG DT @ 3226: 0.067285091999987 +INFO - [G] LONG DT @ 3226: 0.055555193600022 +INFO - [G] LONG DT @ 3266: 0.056312389973779 +INFO - [G] LONG DT @ 3266: 0.082847785583251 +INFO - [G] LONG DT @ 3266: 0.069255608466593 +INFO - [G] LONG DT @ 3266: 0.058611506773266 +INFO - [G] LONG DT @ 3273: 0.056317270673948 +INFO - [G] LONG DT @ 3313: 0.050398878949625 +INFO - [G] LONG DT @ 3313: 0.1 +INFO - [G] LONG DT @ 3313: 0.084667760000029 +INFO - [G] LONG DT @ 3313: 0.072256508000031 +INFO - [G] LONG DT @ 3313: 0.062049906400006 +INFO - [G] LONG DT @ 3313: 0.053923685120003 +INFO - [G] LONG DT @ 3335: 0.1 +INFO - [G] LONG DT @ 3335: 0.085362460000015 +INFO - [G] LONG DT @ 3335: 0.073826367999995 +INFO - [G] LONG DT @ 3335: 0.064719494399989 +INFO - [G] LONG DT @ 3335: 0.05712447552001 +INFO - [G] LONG DT @ 3335: 0.051197000416037 +INFO - [G] LONG DT @ 3341: 0.065757603206926 +INFO - [G] LONG DT @ 3341: 0.05724356256556 +INFO - [G] LONG DT @ 3341: 0.050297950052414 diff --git a/lovely/log/lovely-2025.01.09-01.39.41.log b/lovely/log/lovely-2025.01.09-01.39.41.log new file mode 100644 index 0000000..80f36d0 --- /dev/null +++ b/lovely/log/lovely-2025.01.09-01.39.41.log @@ -0,0 +1,307 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 130ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 01:39:44 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 01:39:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 01:39:45 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 01:39:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 01:39:45 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 01:39:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 01:39:45 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 01:39:45 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 01:39:45 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-09 01:39:45 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.729 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0031] Injected Atlas in 800.171 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0025] Injected Sound in 21.776 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0032] Injected Stake in 0.729 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0008] Injected Rarity in 0.021 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.053 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0391] Injected Center in 2.165 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.025 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0027] Injected Blind in 0.470 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0002] Injected Seal in 0.042 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0004] Injected Suit in 0.103 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0013] Injected Rank in 0.086 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.032 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.110 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0031] Injected Challenge in 0.052 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0028] Injected Tag in 0.610 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0009] Injected Sticker in 0.559 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0009] Injected Shader in 54.651 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0020] Injected Achievement in 0.143 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.167 ms +INFO - [G] 2025-01-09 01:39:46 :: INFO :: TIMER :: [0011] Injected Event in 0.091 ms +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 13: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 14: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 15: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 16: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.080447499999999 +INFO - [G] LONG DT @ 19: 0.06554884 +INFO - [G] LONG DT @ 20: 0.053682532 +INFO - [G] LONG DT @ 1067: 0.1 +INFO - [G] LONG DT @ 1067: 0.1 +INFO - [G] LONG DT @ 1067: 0.1 +INFO - [G] LONG DT @ 1067: 0.1 +INFO - [G] LONG DT @ 1067: 0.1 +INFO - [G] LONG DT @ 1067: 0.1 +INFO - [G] LONG DT @ 1067: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1068: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1069: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1070: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1071: 0.1 +INFO - [G] LONG DT @ 1072: 0.1 +INFO - [G] LONG DT @ 1072: 0.1 +INFO - [G] LONG DT @ 1072: 0.1 +INFO - [G] LONG DT @ 1072: 0.1 +INFO - [G] LONG DT @ 1072: 0.1 +INFO - [G] LONG DT @ 1072: 0.1 +INFO - [G] LONG DT @ 1072: 0.1 +INFO - [G] LONG DT @ 1072: 0.1 +INFO - [G] LONG DT @ 1072: 0.08151194 +INFO - [G] LONG DT @ 1072: 0.066704631999995 +INFO - [G] LONG DT @ 1072: 0.054642965599997 +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] 2025-01-09 02:31:58 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-09 02:31:58 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 15.079 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0031] Injected Atlas in 811.704 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0025] Injected Sound in 20.201 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0032] Injected Stake in 0.317 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0008] Injected Rarity in 0.020 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.034 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0391] Injected Center in 0.822 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.021 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0027] Injected Blind in 0.026 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0002] Injected Seal in 0.041 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0004] Injected Suit in 0.046 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0013] Injected Rank in 0.025 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.020 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.060 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0031] Injected Challenge in 0.022 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0028] Injected Tag in 0.028 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0009] Injected Sticker in 0.121 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0009] Injected Shader in 36.829 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0020] Injected Achievement in 0.121 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 9.214 ms +INFO - [G] 2025-01-09 02:31:59 :: INFO :: TIMER :: [0011] Injected Event in 0.012 ms +INFO - [G] LONG DT @ 45: 0.1 +INFO - [G] LONG DT @ 12: 0.084008939999949 +INFO - [G] LONG DT @ 12: 0.070627251999998 +INFO - [G] LONG DT @ 12: 0.059946401599965 +INFO - [G] LONG DT @ 12: 0.051411801280022 +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] 2025-01-09 02:34:59 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-09 02:34:59 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 12.776 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0031] Injected Atlas in 791.418 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0025] Injected Sound in 19.979 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0032] Injected Stake in 0.147 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0008] Injected Rarity in 0.017 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.057 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0391] Injected Center in 0.836 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.009 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0027] Injected Blind in 0.026 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0002] Injected Seal in 0.039 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0004] Injected Suit in 0.051 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0013] Injected Rank in 0.028 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.021 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.037 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0031] Injected Challenge in 0.025 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0028] Injected Tag in 0.029 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0009] Injected Sticker in 0.084 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0009] Injected Shader in 67.462 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0020] Injected Achievement in 0.077 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 12.424 ms +INFO - [G] 2025-01-09 02:35:00 :: INFO :: TIMER :: [0011] Injected Event in 0.011 ms +INFO - [G] LONG DT @ 163: 0.1 +INFO - [G] LONG DT @ 12: 0.084497160000028 +INFO - [G] LONG DT @ 12: 0.071077967999976 +INFO - [G] LONG DT @ 12: 0.060296154399958 +INFO - [G] LONG DT @ 12: 0.051660703520015 diff --git a/lovely/log/lovely-2025.01.09-02.37.25.log b/lovely/log/lovely-2025.01.09-02.37.25.log new file mode 100644 index 0000000..1bd97d6 --- /dev/null +++ b/lovely/log/lovely-2025.01.09-02.37.25.log @@ -0,0 +1,115 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 128ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 02:37:29 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 02:37:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 02:37:29 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 02:37:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 02:37:29 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 02:37:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 02:37:29 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 02:37:30 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.752 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0031] Injected Atlas in 790.632 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0025] Injected Sound in 21.453 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0032] Injected Stake in 0.859 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0008] Injected Rarity in 0.022 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.048 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0391] Injected Center in 1.995 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.028 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0027] Injected Blind in 0.290 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0002] Injected Seal in 0.214 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0004] Injected Suit in 0.077 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0013] Injected Rank in 0.195 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.031 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.075 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0031] Injected Challenge in 0.055 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0028] Injected Tag in 0.052 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0009] Injected Sticker in 0.075 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0009] Injected Shader in 53.570 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0020] Injected Achievement in 0.060 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.675 ms +INFO - [G] 2025-01-09 02:37:30 :: INFO :: TIMER :: [0011] Injected Event in 0.014 ms diff --git a/lovely/log/lovely-2025.01.09-17.52.43.log b/lovely/log/lovely-2025.01.09-17.52.43.log new file mode 100644 index 0000000..66767cd --- /dev/null +++ b/lovely/log/lovely-2025.01.09-17.52.43.log @@ -0,0 +1,214 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 137ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 17:52:47 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 17:52:48 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 17:52:48 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 17:52:48 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 17:52:48 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 17:52:48 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 17:52:48 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 17:52:48 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.987 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0031] Injected Atlas in 799.057 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0025] Injected Sound in 23.766 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0032] Injected Stake in 0.613 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0008] Injected Rarity in 0.022 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.046 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0391] Injected Center in 2.202 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.044 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0027] Injected Blind in 0.417 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0002] Injected Seal in 0.276 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0004] Injected Suit in 0.120 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0013] Injected Rank in 0.105 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.033 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.104 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0031] Injected Challenge in 0.063 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0028] Injected Tag in 0.782 ms +INFO - [G] 2025-01-09 17:52:48 :: INFO :: TIMER :: [0009] Injected Sticker in 0.459 ms +INFO - [G] 2025-01-09 17:52:49 :: INFO :: TIMER :: [0009] Injected Shader in 61.350 ms +INFO - [G] 2025-01-09 17:52:49 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-09 17:52:49 :: INFO :: TIMER :: [0020] Injected Achievement in 0.242 ms +INFO - [G] 2025-01-09 17:52:49 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.695 ms +INFO - [G] 2025-01-09 17:52:49 :: INFO :: TIMER :: [0011] Injected Event in 0.165 ms +INFO - [G] LONG DT @ 67: 0.1 +INFO - [G] LONG DT @ 67: 0.1 +INFO - [G] LONG DT @ 67: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.1 +INFO - [G] LONG DT @ 68: 0.081165979999999 +INFO - [G] LONG DT @ 68: 0.065826343999999 +INFO - [G] LONG DT @ 68: 0.0538825152 +INFO - [G] LONG DT @ 74: 0.080170907077756 +INFO - [G] LONG DT @ 75: 0.067432685662204 +INFO - [G] LONG DT @ 75: 0.057214428529764 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 83: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 84: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 85: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 86: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 87: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 88: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 89: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 90: 0.1 +INFO - [G] LONG DT @ 91: 0.1 +INFO - [G] LONG DT @ 91: 0.1 +INFO - [G] LONG DT @ 91: 0.1 +INFO - [G] LONG DT @ 91: 0.1 +INFO - [G] LONG DT @ 91: 0.1 +INFO - [G] LONG DT @ 91: 0.081291100000003 +INFO - [G] LONG DT @ 91: 0.066132180000001 +INFO - [G] LONG DT @ 91: 0.053766764000003 diff --git a/lovely/log/lovely-2025.01.09-17.55.53.log b/lovely/log/lovely-2025.01.09-17.55.53.log new file mode 100644 index 0000000..60107dc --- /dev/null +++ b/lovely/log/lovely-2025.01.09-17.55.53.log @@ -0,0 +1,256 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 123ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 17:55:57 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 17:55:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 17:55:57 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 17:55:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 17:55:57 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 17:55:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-09 17:55:57 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-09 17:55:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 17:55:57 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-09 17:55:57 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 17:55:58 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.805 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0031] Injected Atlas in 803.834 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0025] Injected Sound in 20.557 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0032] Injected Stake in 0.720 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0008] Injected Rarity in 0.028 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.063 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0391] Injected Center in 1.838 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.010 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0027] Injected Blind in 0.057 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0002] Injected Seal in 0.049 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0004] Injected Suit in 0.084 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0013] Injected Rank in 0.065 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.049 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.109 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0031] Injected Challenge in 0.145 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0028] Injected Tag in 0.237 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0009] Injected Sticker in 0.686 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0009] Injected Shader in 55.551 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0020] Injected Achievement in 0.069 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 4.205 ms +INFO - [G] 2025-01-09 17:55:58 :: INFO :: TIMER :: [0011] Injected Event in 0.014 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 1894: 0.05097178820817 +INFO - [G] LONG DT @ 1952: 0.057003341348129 +INFO - [G] LONG DT @ 1952: 0.053710233078511 +INFO - [G] LONG DT @ 1952: 0.051085246462804 +INFO - [G] LONG DT @ 2109: 0.053958635977822 +INFO - [G] LONG DT @ 2109: 0.051689768782252 +INFO - [G] LONG DT @ 2109: 0.051517575025819 +INFO - [G] LONG DT @ 2109: 0.070034700020704 +INFO - [G] LONG DT @ 2109: 0.064395700016535 +INFO - [G] LONG DT @ 2109: 0.059747920013218 +INFO - [G] LONG DT @ 2109: 0.055842956010539 +INFO - [G] LONG DT @ 2109: 0.052902364808485 +INFO - [G] LONG DT @ 2109: 0.050436091846788 +INFO - [G] LONG DT @ 2187: 0.052896893123253 +INFO - [G] LONG DT @ 2187: 0.050348194498558 +INFO - [G] LONG DT @ 2268: 0.1 +INFO - [G] LONG DT @ 2268: 0.088545119999962 +INFO - [G] LONG DT @ 2269: 0.079070815999989 +INFO - [G] LONG DT @ 2269: 0.071441232800025 +INFO - [G] LONG DT @ 2269: 0.065316486239975 +INFO - [G] LONG DT @ 2269: 0.060285708992031 +INFO - [G] LONG DT @ 2269: 0.056594347193611 +INFO - [G] LONG DT @ 2269: 0.053259517754862 +INFO - [G] LONG DT @ 2269: 0.050781854203861 +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'cry_runarea' not instantiated before load +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-09 18:36:51 :: ERROR :: StackTrace :: Oops! The game crashed +[SMODS Cryptid "Items/CodeCards.lua"]:4064: attempt to index field 'cry_runarea' (a nil value) +Stack Traceback +=============== +(1) Lua local 'handler' at file 'main.lua:612' + Local variables: + msg = string: "[SMODS Cryptid \"Items/CodeCards.lua\"]:4064: attempt to index field 'cry_runarea' (a nil value)" + (*temporary) = Lua function '?' (defined at line 31 of chunk [SMODS _ "src/logging.lua"]) + (*temporary) = string: "Oops! The game crashed\ +" +(2) LÖVE metamethod at file 'boot.lua:352' + Local variables: + errhand = Lua function '?' (defined at line 598 of chunk main.lua) + handler = Lua function '?' (defined at line 598 of chunk main.lua) +(3) Lua function '?' at file 'Items/CodeCards.lua:4064' (from mod with id Cryptid) (best guess) + Local variables: + e = table: 0x06184158 {pixellated_rect:table: 0x09410d98, click_offset:table: 0x065037c8, children:table: 0x06184220 (more...)} + (*temporary) = nil + (*temporary) = table: 0x08fdd358 {queue_last_processed:2451.7833333369, queues:table: 0x04d01170, queue_dt:0.016666666666667 (more...)} + (*temporary) = table: 0x09132048 {start_timer:false, timer:TOTAL, blockable:true, trigger:after, func:function: 0x09132028 (more...)} + (*temporary) = string: "base" + (*temporary) = nil + (*temporary) = table: 0x04d20d90 {1:table: 0x06ec6f10, 2:table: 0x0901c6a0, 3:table: 0x09763468, 4:table: 0x044ac940 (more...)} + (*temporary) = number: 47 + (*temporary) = table: 0x0427d3a8 {handle:function: 0x0427c440, __index:table: 0x0427d3a8, init:function: 0x0427c9a8 (more...)} + (*temporary) = table: 0x0427d3a8 {handle:function: 0x0427c440, __index:table: 0x0427d3a8, init:function: 0x0427c9a8 (more...)} + (*temporary) = boolean: true + (*temporary) = table: 0x09131f98 {func:function: 0x09132028, trigger:after, delay:0.5} + (*temporary) = string: "attempt to index field 'cry_runarea' (a nil value)" +(4) Lua method 'click' at file 'engine/ui.lua:988' + Local variables: + self = table: 0x06184158 {pixellated_rect:table: 0x09410d98, click_offset:table: 0x065037c8, children:table: 0x06184220 (more...)} +(5) Lua method 'update' at file 'engine/controller.lua:375' + Local variables: + self = table: 0x049db078 {held_button_times:table: 0x049db948, focus_cursor_stack_level:1, snap_cursor_to:table: 0x11496848 (more...)} + dt = number: 0.006097 +(6) Lua upvalue 'gameUpdateRef' at file 'game.lua:2866' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0871ed68 (more...)} + dt = number: 0.006097 +(7) Lua upvalue 'upd' at Steamodded file 'src/ui.lua:81' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0871ed68 (more...)} + dt = number: 0.006097 +(8) Lua upvalue 'upd' at file 'main.lua:3934' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0871ed68 (more...)} + dt = number: 0.006097 +(9) Lua upvalue 'upd' at file 'Items/Blinds.lua:1310' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0871ed68 (more...)} + dt = number: 0.006097 +(10) Lua upvalue 'upd' at file 'Items/CodeCards.lua:4106' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0871ed68 (more...)} + dt = number: 0.006097 +(11) Lua upvalue 'upd' at file 'Items/Decks.lua:429' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0871ed68 (more...)} + dt = number: 0.006097 +(12) Lua upvalue 'upd' at file 'Items/MiscJokers.lua:6658' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0871ed68 (more...)} + dt = number: 0.006097 +(13) Lua method 'update' at file 'Cryptid.lua:2948' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0871ed68 (more...)} + dt = number: 0.006097 +(14) Lua upvalue 'love_update_ref' at file 'main.lua:993' + Local variables: + dt = number: 0.006097 +(15) Lua upvalue 'oldupd' at file 'main.lua:2796' + Local variables: + dt = number: 0.006097 +(16) Lua field 'update' at file 'main.lua:3961' + Local variables: + dt = number: 0.006097 +(17) Lua function '?' at file 'main.lua:931' (best guess) +(18) global C function 'xpcall' +(19) LÖVE function at file 'boot.lua:377' (best guess) + Local variables: + func = Lua function '?' (defined at line 902 of chunk main.lua) + inerror = boolean: true + deferErrhand = Lua function '(LÖVE Function)' (defined at line 348 of chunk [love "boot.lua"]) + earlyinit = Lua function '(LÖVE Function)' (defined at line 355 of chunk [love "boot.lua"]) + +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-09 18:36:51 :: INFO :: StackTrace :: Additional Context: +Balatro Version: 1.0.1n-FULL +Modded Version: 1.0.0~ALPHA-1220a-STEAMODDED +LÖVE Version: 11.5.0 +Lovely Version: 0.6.0 +Steamodded Mods: + 1: J Cursor by Jie65535, MarioMak967 [ID: JCursor] + 2: Talisman by MathIsFun_, Mathguy24, jenwalter666, cg-223 [ID: Talisman, Version: 2.0.2, Uses Lovely] + Break Infinity: omeganum + 3: Handy by SleepyG11 [ID: Handy, Version: 1.1.5, Uses Lovely] + 4: Cryptid by MathIsFun_, Cryptid and Balatro Discords [ID: Cryptid, Priority: 1e+299, Version: 0.5.3a, Uses Lovely] +Lovely Mods: diff --git a/lovely/log/lovely-2025.01.09-18.37.40.log b/lovely/log/lovely-2025.01.09-18.37.40.log new file mode 100644 index 0000000..2e60f88 --- /dev/null +++ b/lovely/log/lovely-2025.01.09-18.37.40.log @@ -0,0 +1,229 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 130ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 18:37:44 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 18:37:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 18:37:45 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 18:37:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 18:37:45 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 18:37:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-09 18:37:45 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-09 18:37:45 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 18:37:45 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-09 18:37:45 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 18:37:45 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 18:37:45 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-09 18:37:45 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.666 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0031] Injected Atlas in 778.134 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0025] Injected Sound in 20.385 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0032] Injected Stake in 0.849 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0008] Injected Rarity in 0.027 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.052 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0391] Injected Center in 1.617 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.014 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0027] Injected Blind in 0.044 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0002] Injected Seal in 0.049 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0004] Injected Suit in 0.068 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0013] Injected Rank in 0.067 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.066 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.107 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0031] Injected Challenge in 0.036 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0028] Injected Tag in 0.317 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0009] Injected Sticker in 0.071 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0009] Injected Shader in 109.290 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0020] Injected Achievement in 0.056 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.450 ms +INFO - [G] 2025-01-09 18:37:46 :: INFO :: TIMER :: [0011] Injected Event in 0.014 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'cry_runarea' not instantiated before load +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-09 18:37:57 :: ERROR :: StackTrace :: Oops! The game crashed +[SMODS Cryptid "Items/CodeCards.lua"]:4064: attempt to index field 'cry_runarea' (a nil value) +Stack Traceback +=============== +(1) Lua local 'handler' at file 'main.lua:612' + Local variables: + msg = string: "[SMODS Cryptid \"Items/CodeCards.lua\"]:4064: attempt to index field 'cry_runarea' (a nil value)" + (*temporary) = Lua function '?' (defined at line 31 of chunk [SMODS _ "src/logging.lua"]) + (*temporary) = string: "Oops! The game crashed\ +" +(2) LÖVE metamethod at file 'boot.lua:352' + Local variables: + errhand = Lua function '?' (defined at line 598 of chunk main.lua) + handler = Lua function '?' (defined at line 598 of chunk main.lua) +(3) Lua function '?' at file 'Items/CodeCards.lua:4064' (from mod with id Cryptid) (best guess) + Local variables: + e = table: 0x04c14ea8 {pixellated_rect:table: 0x08653338, click_offset:table: 0x084a64f0, children:table: 0x04c14ed0 (more...)} + (*temporary) = nil + (*temporary) = table: 0x04cdf9f8 {queue_last_processed:10.933333333333, queues:table: 0x04cdfa20, queue_dt:0.016666666666667 (more...)} + (*temporary) = table: 0x04bf79d0 {start_timer:false, timer:TOTAL, blockable:true, trigger:after, func:function: 0x08650280 (more...)} + (*temporary) = string: "base" + (*temporary) = nil + (*temporary) = table: 0x04d23008 {1:table: 0x1180b030, 2:table: 0x087ea7c0, 3:table: 0x044c9508, 4:table: 0x14224b90 (more...)} + (*temporary) = number: 47 + (*temporary) = table: 0x0427d3a8 {handle:function: 0x0427c440, __index:table: 0x0427d3a8, init:function: 0x0427c9a8 (more...)} + (*temporary) = table: 0x0427d3a8 {handle:function: 0x0427c440, __index:table: 0x0427d3a8, init:function: 0x0427c9a8 (more...)} + (*temporary) = boolean: true + (*temporary) = table: 0x04bf7940 {func:function: 0x08650280, trigger:after, delay:0.5} + (*temporary) = string: "attempt to index field 'cry_runarea' (a nil value)" +(4) Lua method 'click' at file 'engine/ui.lua:988' + Local variables: + self = table: 0x04c14ea8 {pixellated_rect:table: 0x08653338, click_offset:table: 0x084a64f0, children:table: 0x04c14ed0 (more...)} +(5) Lua method 'update' at file 'engine/controller.lua:375' + Local variables: + self = table: 0x049daff0 {held_button_times:table: 0x049db8c0, focus_cursor_stack_level:1, snap_cursor_to:table: 0x142e4858 (more...)} + dt = number: 0.00611024 +(6) Lua upvalue 'gameUpdateRef' at file 'game.lua:2866' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x06844500 (more...)} + dt = number: 0.00611024 +(7) Lua upvalue 'upd' at Steamodded file 'src/ui.lua:81' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x06844500 (more...)} + dt = number: 0.00611024 +(8) Lua upvalue 'upd' at file 'main.lua:3934' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x06844500 (more...)} + dt = number: 0.00611024 +(9) Lua upvalue 'upd' at file 'Items/Blinds.lua:1310' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x06844500 (more...)} + dt = number: 0.00611024 +(10) Lua upvalue 'upd' at file 'Items/CodeCards.lua:4106' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x06844500 (more...)} + dt = number: 0.00611024 +(11) Lua upvalue 'upd' at file 'Items/Decks.lua:429' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x06844500 (more...)} + dt = number: 0.00611024 +(12) Lua upvalue 'upd' at file 'Items/MiscJokers.lua:6658' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x06844500 (more...)} + dt = number: 0.00611024 +(13) Lua method 'update' at file 'Cryptid.lua:2948' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x06844500 (more...)} + dt = number: 0.00611024 +(14) Lua upvalue 'love_update_ref' at file 'main.lua:993' + Local variables: + dt = number: 0.00611024 +(15) Lua upvalue 'oldupd' at file 'main.lua:2796' + Local variables: + dt = number: 0.00611024 +(16) Lua field 'update' at file 'main.lua:3961' + Local variables: + dt = number: 0.00611024 +(17) Lua function '?' at file 'main.lua:931' (best guess) +(18) global C function 'xpcall' +(19) LÖVE function at file 'boot.lua:377' (best guess) + Local variables: + func = Lua function '?' (defined at line 902 of chunk main.lua) + inerror = boolean: true + deferErrhand = Lua function '(LÖVE Function)' (defined at line 348 of chunk [love "boot.lua"]) + earlyinit = Lua function '(LÖVE Function)' (defined at line 355 of chunk [love "boot.lua"]) + +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-09 18:37:57 :: INFO :: StackTrace :: Additional Context: +Balatro Version: 1.0.1n-FULL +Modded Version: 1.0.0~ALPHA-1220a-STEAMODDED +LÖVE Version: 11.5.0 +Lovely Version: 0.6.0 +Steamodded Mods: + 1: J Cursor by Jie65535, MarioMak967 [ID: JCursor] + 2: Talisman by MathIsFun_, Mathguy24, jenwalter666, cg-223 [ID: Talisman, Version: 2.0.2, Uses Lovely] + Break Infinity: omeganum + 3: Handy by SleepyG11 [ID: Handy, Version: 1.1.5, Uses Lovely] + 4: Cryptid by MathIsFun_, Cryptid and Balatro Discords [ID: Cryptid, Priority: 1e+299, Version: 0.5.3a, Uses Lovely] +Lovely Mods: diff --git a/lovely/log/lovely-2025.01.09-18.38.18.log b/lovely/log/lovely-2025.01.09-18.38.18.log new file mode 100644 index 0000000..a7454bc --- /dev/null +++ b/lovely/log/lovely-2025.01.09-18.38.18.log @@ -0,0 +1,229 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 129ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 18:38:28 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 18:38:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 18:38:29 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 18:38:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 18:38:29 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 18:38:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-09 18:38:29 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-09 18:38:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 18:38:29 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-09 18:38:29 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 18:38:29 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 18:38:29 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-09 18:38:29 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.646 ms +INFO - [G] 2025-01-09 18:38:29 :: INFO :: TIMER :: [0031] Injected Atlas in 784.712 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0025] Injected Sound in 20.795 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0032] Injected Stake in 0.679 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0008] Injected Rarity in 0.024 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.080 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0391] Injected Center in 1.784 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.012 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0027] Injected Blind in 0.044 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0002] Injected Seal in 0.066 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0004] Injected Suit in 0.065 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0013] Injected Rank in 0.067 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.035 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.104 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0031] Injected Challenge in 0.038 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0028] Injected Tag in 0.381 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0009] Injected Sticker in 0.541 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0009] Injected Shader in 55.214 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0020] Injected Achievement in 0.054 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.489 ms +INFO - [G] 2025-01-09 18:38:30 :: INFO :: TIMER :: [0011] Injected Event in 0.014 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'cry_runarea' not instantiated before load +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-09 18:38:40 :: ERROR :: StackTrace :: Oops! The game crashed +[SMODS Cryptid "Items/CodeCards.lua"]:4064: attempt to index field 'cry_runarea' (a nil value) +Stack Traceback +=============== +(1) Lua local 'handler' at file 'main.lua:612' + Local variables: + msg = string: "[SMODS Cryptid \"Items/CodeCards.lua\"]:4064: attempt to index field 'cry_runarea' (a nil value)" + (*temporary) = Lua function '?' (defined at line 31 of chunk [SMODS _ "src/logging.lua"]) + (*temporary) = string: "Oops! The game crashed\ +" +(2) LÖVE metamethod at file 'boot.lua:352' + Local variables: + errhand = Lua function '?' (defined at line 598 of chunk main.lua) + handler = Lua function '?' (defined at line 598 of chunk main.lua) +(3) Lua function '?' at file 'Items/CodeCards.lua:4064' (from mod with id Cryptid) (best guess) + Local variables: + e = table: 0x04bcea98 {pixellated_rect:table: 0x045efce8, click_offset:table: 0x0612d750, children:table: 0x04bceb60 (more...)} + (*temporary) = nil + (*temporary) = table: 0x049dc418 {queue_last_processed:10.666666666667, queues:table: 0x049dc440, queue_dt:0.016666666666667 (more...)} + (*temporary) = table: 0x04d35ca8 {start_timer:false, timer:TOTAL, blockable:true, trigger:after, func:function: 0x042731f8 (more...)} + (*temporary) = string: "base" + (*temporary) = nil + (*temporary) = table: 0x04ce3290 {1:table: 0x13e0fa40, 2:table: 0x0f566dd0, 3:table: 0x0c12fc30, 4:table: 0x00c01ba8 (more...)} + (*temporary) = number: 26 + (*temporary) = table: 0x0427d3a8 {handle:function: 0x0427c440, __index:table: 0x0427d3a8, init:function: 0x0427c9a8 (more...)} + (*temporary) = table: 0x0427d3a8 {handle:function: 0x0427c440, __index:table: 0x0427d3a8, init:function: 0x0427c9a8 (more...)} + (*temporary) = boolean: true + (*temporary) = table: 0x04d35c18 {func:function: 0x042731f8, trigger:after, delay:0.5} + (*temporary) = string: "attempt to index field 'cry_runarea' (a nil value)" +(4) Lua method 'click' at file 'engine/ui.lua:988' + Local variables: + self = table: 0x04bcea98 {pixellated_rect:table: 0x045efce8, click_offset:table: 0x0612d750, children:table: 0x04bceb60 (more...)} +(5) Lua method 'update' at file 'engine/controller.lua:375' + Local variables: + self = table: 0x049db088 {held_button_times:table: 0x049db958, focus_cursor_stack_level:1, snap_cursor_to:table: 0x064d6e08 (more...)} + dt = number: 0.0061158 +(6) Lua upvalue 'gameUpdateRef' at file 'game.lua:2866' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x060805e0 (more...)} + dt = number: 0.0061158 +(7) Lua upvalue 'upd' at Steamodded file 'src/ui.lua:81' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x060805e0 (more...)} + dt = number: 0.0061158 +(8) Lua upvalue 'upd' at file 'main.lua:3934' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x060805e0 (more...)} + dt = number: 0.0061158 +(9) Lua upvalue 'upd' at file 'Items/Blinds.lua:1310' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x060805e0 (more...)} + dt = number: 0.0061158 +(10) Lua upvalue 'upd' at file 'Items/CodeCards.lua:4106' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x060805e0 (more...)} + dt = number: 0.0061158 +(11) Lua upvalue 'upd' at file 'Items/Decks.lua:429' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x060805e0 (more...)} + dt = number: 0.0061158 +(12) Lua upvalue 'upd' at file 'Items/MiscJokers.lua:6658' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x060805e0 (more...)} + dt = number: 0.0061158 +(13) Lua method 'update' at file 'Cryptid.lua:2948' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x060805e0 (more...)} + dt = number: 0.0061158 +(14) Lua upvalue 'love_update_ref' at file 'main.lua:993' + Local variables: + dt = number: 0.0061158 +(15) Lua upvalue 'oldupd' at file 'main.lua:2796' + Local variables: + dt = number: 0.0061158 +(16) Lua field 'update' at file 'main.lua:3961' + Local variables: + dt = number: 0.0061158 +(17) Lua function '?' at file 'main.lua:931' (best guess) +(18) global C function 'xpcall' +(19) LÖVE function at file 'boot.lua:377' (best guess) + Local variables: + func = Lua function '?' (defined at line 902 of chunk main.lua) + inerror = boolean: true + deferErrhand = Lua function '(LÖVE Function)' (defined at line 348 of chunk [love "boot.lua"]) + earlyinit = Lua function '(LÖVE Function)' (defined at line 355 of chunk [love "boot.lua"]) + +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-09 18:38:40 :: INFO :: StackTrace :: Additional Context: +Balatro Version: 1.0.1n-FULL +Modded Version: 1.0.0~ALPHA-1220a-STEAMODDED +LÖVE Version: 11.5.0 +Lovely Version: 0.6.0 +Steamodded Mods: + 1: J Cursor by Jie65535, MarioMak967 [ID: JCursor] + 2: Talisman by MathIsFun_, Mathguy24, jenwalter666, cg-223 [ID: Talisman, Version: 2.0.2, Uses Lovely] + Break Infinity: omeganum + 3: Handy by SleepyG11 [ID: Handy, Version: 1.1.5, Uses Lovely] + 4: Cryptid by MathIsFun_, Cryptid and Balatro Discords [ID: Cryptid, Priority: 1e+299, Version: 0.5.3a, Uses Lovely] +Lovely Mods: diff --git a/lovely/log/lovely-2025.01.09-18.39.03.log b/lovely/log/lovely-2025.01.09-18.39.03.log new file mode 100644 index 0000000..585d698 --- /dev/null +++ b/lovely/log/lovely-2025.01.09-18.39.03.log @@ -0,0 +1,229 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 154ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 18:39:07 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 18:39:08 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 18:39:08 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 18:39:08 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 18:39:08 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 18:39:08 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-09 18:39:08 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-09 18:39:08 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 18:39:08 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-09 18:39:08 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 18:39:08 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 18:39:08 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-09 18:39:08 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.721 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0031] Injected Atlas in 775.306 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0025] Injected Sound in 19.414 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0032] Injected Stake in 0.587 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0008] Injected Rarity in 0.023 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.041 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0391] Injected Center in 1.826 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.011 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0027] Injected Blind in 0.042 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0002] Injected Seal in 0.054 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0004] Injected Suit in 0.076 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0013] Injected Rank in 0.064 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.059 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.089 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0031] Injected Challenge in 0.162 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0028] Injected Tag in 0.252 ms +INFO - [G] 2025-01-09 18:39:09 :: INFO :: TIMER :: [0009] Injected Sticker in 0.549 ms +INFO - [G] 2025-01-09 18:39:10 :: INFO :: TIMER :: [0009] Injected Shader in 826.031 ms +INFO - [G] 2025-01-09 18:39:10 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-09 18:39:10 :: INFO :: TIMER :: [0020] Injected Achievement in 0.057 ms +INFO - [G] 2025-01-09 18:39:10 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.632 ms +INFO - [G] 2025-01-09 18:39:10 :: INFO :: TIMER :: [0011] Injected Event in 0.021 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'cry_runarea' not instantiated before load +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-09 18:39:47 :: ERROR :: StackTrace :: Oops! The game crashed +[SMODS Cryptid "Items/CodeCards.lua"]:4064: attempt to index field 'cry_runarea' (a nil value) +Stack Traceback +=============== +(1) Lua local 'handler' at file 'main.lua:612' + Local variables: + msg = string: "[SMODS Cryptid \"Items/CodeCards.lua\"]:4064: attempt to index field 'cry_runarea' (a nil value)" + (*temporary) = Lua function '?' (defined at line 31 of chunk [SMODS _ "src/logging.lua"]) + (*temporary) = string: "Oops! The game crashed\ +" +(2) LÖVE metamethod at file 'boot.lua:352' + Local variables: + errhand = Lua function '?' (defined at line 598 of chunk main.lua) + handler = Lua function '?' (defined at line 598 of chunk main.lua) +(3) Lua function '?' at file 'Items/CodeCards.lua:4064' (from mod with id Cryptid) (best guess) + Local variables: + e = table: 0x0bb5a048 {pixellated_rect:table: 0x05ff8d90, click_offset:table: 0x086fee90, children:table: 0x0bb5a070 (more...)} + (*temporary) = nil + (*temporary) = table: 0x046d5af0 {queue_last_processed:37.833333333332, queues:table: 0x046d5b18, queue_dt:0.016666666666667 (more...)} + (*temporary) = table: 0x086092e8 {start_timer:false, timer:TOTAL, blockable:true, trigger:after, func:function: 0x086092c8 (more...)} + (*temporary) = string: "base" + (*temporary) = nil + (*temporary) = table: 0x046d5b68 {1:table: 0x0f8e4190, 2:table: 0x1428ec78, 3:table: 0x08608d60, 4:table: 0x08609088 (more...)} + (*temporary) = number: 5 + (*temporary) = table: 0x0427d3a8 {handle:function: 0x0427c440, __index:table: 0x0427d3a8, init:function: 0x0427c9a8 (more...)} + (*temporary) = table: 0x0427d3a8 {handle:function: 0x0427c440, __index:table: 0x0427d3a8, init:function: 0x0427c9a8 (more...)} + (*temporary) = boolean: true + (*temporary) = table: 0x08609238 {func:function: 0x086092c8, trigger:after, delay:0.5} + (*temporary) = string: "attempt to index field 'cry_runarea' (a nil value)" +(4) Lua method 'click' at file 'engine/ui.lua:988' + Local variables: + self = table: 0x0bb5a048 {pixellated_rect:table: 0x05ff8d90, click_offset:table: 0x086fee90, children:table: 0x0bb5a070 (more...)} +(5) Lua method 'update' at file 'engine/controller.lua:375' + Local variables: + self = table: 0x049cf9e0 {held_button_times:table: 0x049cf5b8, focus_cursor_stack_level:1, snap_cursor_to:table: 0x08a999e0 (more...)} + dt = number: 0.00612002 +(6) Lua upvalue 'gameUpdateRef' at file 'game.lua:2866' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0bf67978 (more...)} + dt = number: 0.00612002 +(7) Lua upvalue 'upd' at Steamodded file 'src/ui.lua:81' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0bf67978 (more...)} + dt = number: 0.00612002 +(8) Lua upvalue 'upd' at file 'main.lua:3934' + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0bf67978 (more...)} + dt = number: 0.00612002 +(9) Lua upvalue 'upd' at file 'Items/Blinds.lua:1310' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0bf67978 (more...)} + dt = number: 0.00612002 +(10) Lua upvalue 'upd' at file 'Items/CodeCards.lua:4106' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0bf67978 (more...)} + dt = number: 0.00612002 +(11) Lua upvalue 'upd' at file 'Items/Decks.lua:429' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0bf67978 (more...)} + dt = number: 0.00612002 +(12) Lua upvalue 'upd' at file 'Items/MiscJokers.lua:6658' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0bf67978 (more...)} + dt = number: 0.00612002 +(13) Lua method 'update' at file 'Cryptid.lua:2948' (from mod with id Cryptid) + Local variables: + self = table: 0x042f89c0 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x0bf67978 (more...)} + dt = number: 0.00612002 +(14) Lua upvalue 'love_update_ref' at file 'main.lua:993' + Local variables: + dt = number: 0.00612002 +(15) Lua upvalue 'oldupd' at file 'main.lua:2796' + Local variables: + dt = number: 0.00612002 +(16) Lua field 'update' at file 'main.lua:3961' + Local variables: + dt = number: 0.00612002 +(17) Lua function '?' at file 'main.lua:931' (best guess) +(18) global C function 'xpcall' +(19) LÖVE function at file 'boot.lua:377' (best guess) + Local variables: + func = Lua function '?' (defined at line 902 of chunk main.lua) + inerror = boolean: true + deferErrhand = Lua function '(LÖVE Function)' (defined at line 348 of chunk [love "boot.lua"]) + earlyinit = Lua function '(LÖVE Function)' (defined at line 355 of chunk [love "boot.lua"]) + +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-09 18:39:47 :: INFO :: StackTrace :: Additional Context: +Balatro Version: 1.0.1n-FULL +Modded Version: 1.0.0~ALPHA-1220a-STEAMODDED +LÖVE Version: 11.5.0 +Lovely Version: 0.6.0 +Steamodded Mods: + 1: J Cursor by Jie65535, MarioMak967 [ID: JCursor] + 2: Talisman by MathIsFun_, Mathguy24, jenwalter666, cg-223 [ID: Talisman, Version: 2.0.2, Uses Lovely] + Break Infinity: omeganum + 3: Handy by SleepyG11 [ID: Handy, Version: 1.1.5, Uses Lovely] + 4: Cryptid by MathIsFun_, Cryptid and Balatro Discords [ID: Cryptid, Priority: 1e+299, Version: 0.5.3a, Uses Lovely] +Lovely Mods: diff --git a/lovely/log/lovely-2025.01.09-18.40.11.log b/lovely/log/lovely-2025.01.09-18.40.11.log new file mode 100644 index 0000000..ff2f793 --- /dev/null +++ b/lovely/log/lovely-2025.01.09-18.40.11.log @@ -0,0 +1,121 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 131ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 18:40:15 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 18:40:16 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 18:40:16 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 18:40:16 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 18:40:16 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 18:40:16 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-09 18:40:16 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-09 18:40:16 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 18:40:16 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-09 18:40:16 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 18:40:16 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 18:40:16 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-09 18:40:16 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.653 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0031] Injected Atlas in 777.138 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0025] Injected Sound in 20.617 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0032] Injected Stake in 1.267 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0008] Injected Rarity in 0.028 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.081 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0391] Injected Center in 2.667 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.012 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0027] Injected Blind in 0.059 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0002] Injected Seal in 0.054 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0004] Injected Suit in 0.079 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0013] Injected Rank in 0.059 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.030 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.104 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0031] Injected Challenge in 0.044 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0028] Injected Tag in 0.395 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0009] Injected Sticker in 0.066 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0009] Injected Shader in 53.614 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0020] Injected Achievement in 0.069 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.516 ms +INFO - [G] 2025-01-09 18:40:17 :: INFO :: TIMER :: [0011] Injected Event in 0.082 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load diff --git a/lovely/log/lovely-2025.01.09-21.35.12.log b/lovely/log/lovely-2025.01.09-21.35.12.log new file mode 100644 index 0000000..8b2cb08 --- /dev/null +++ b/lovely/log/lovely-2025.01.09-21.35.12.log @@ -0,0 +1,461 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 130ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-09 21:35:17 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-09 21:35:18 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-09 21:35:18 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-09 21:35:18 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-09 21:35:18 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-09 21:35:18 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-09 21:35:18 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-09 21:35:18 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-09 21:35:18 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-09 21:35:18 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-09 21:35:18 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.652 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0031] Injected Atlas in 787.294 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0025] Injected Sound in 22.890 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0032] Injected Stake in 0.749 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0008] Injected Rarity in 0.027 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.125 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0391] Injected Center in 2.057 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.010 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0027] Injected Blind in 0.054 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0002] Injected Seal in 0.050 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0004] Injected Suit in 0.081 ms +INFO - [G] 2025-01-09 21:35:18 :: INFO :: TIMER :: [0013] Injected Rank in 0.128 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.045 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.107 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0031] Injected Challenge in 0.191 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0028] Injected Tag in 0.373 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0009] Injected Sticker in 0.717 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0009] Injected Shader in 61.765 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0020] Injected Achievement in 0.070 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 3.712 ms +INFO - [G] 2025-01-09 21:35:19 :: INFO :: TIMER :: [0011] Injected Event in 0.015 ms +INFO - [G] LONG DT @ 420: 0.1 +INFO - [G] LONG DT @ 420: 0.1 +INFO - [G] LONG DT @ 420: 0.1 +INFO - [G] LONG DT @ 420: 0.1 +INFO - [G] LONG DT @ 420: 0.1 +INFO - [G] LONG DT @ 420: 0.1 +INFO - [G] LONG DT @ 421: 0.1 +INFO - [G] LONG DT @ 421: 0.087805280000002 +INFO - [G] LONG DT @ 421: 0.072251723999992 +INFO - [G] LONG DT @ 421: 0.059350199199999 +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:11 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:51 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] 2025-01-09 21:58:53 :: WARN :: Blind :: Blind object bl_cry_obsidian_orb has debuff_card function, recalc_debuff is preferred +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 17: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 18: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 19: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 20: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 21: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 24: 0.081568279999992 +INFO - [G] LONG DT @ 24: 0.066641264000004 +INFO - [G] LONG DT @ 24: 0.054512091199972 diff --git a/lovely/log/lovely-2025.01.10-02.26.25.log b/lovely/log/lovely-2025.01.10-02.26.25.log new file mode 100644 index 0000000..ba5dd26 --- /dev/null +++ b/lovely/log/lovely-2025.01.10-02.26.25.log @@ -0,0 +1,257 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 170ms +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 16 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 4 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 45 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 75 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +INFO - [♥] Applied 6 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 97 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 123 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 51 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 205 patches to 'card.lua' +INFO - [♥] Applied 15 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-10 02:26:26 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-10 02:26:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-10 02:26:28 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-10 02:26:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-10 02:26:28 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-10 02:26:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-10 02:26:28 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-10 02:26:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-10 02:26:28 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-10 02:26:28 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-10 02:26:28 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-10 02:26:28 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-10 02:26:28 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 1.030 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0031] Injected Atlas in 784.296 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0025] Injected Sound in 262.659 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0032] Injected Stake in 0.639 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0008] Injected Rarity in 0.021 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.035 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0391] Injected Center in 1.662 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.008 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0027] Injected Blind in 0.043 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0002] Injected Seal in 0.033 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0004] Injected Suit in 0.075 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0013] Injected Rank in 0.039 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.033 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.091 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0031] Injected Challenge in 0.036 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0028] Injected Tag in 0.408 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0009] Injected Sticker in 0.273 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0009] Injected Shader in 61.561 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0020] Injected Achievement in 0.054 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 4.248 ms +INFO - [G] 2025-01-10 02:26:29 :: INFO :: TIMER :: [0011] Injected Event in 0.012 ms +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 22: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 23: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 24: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 25: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 26: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 27: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 28: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 29: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 30: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 31: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 32: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 33: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 34: 0.1 +INFO - [G] LONG DT @ 35: 0.1 +INFO - [G] LONG DT @ 35: 0.1 +INFO - [G] LONG DT @ 35: 0.1 +INFO - [G] LONG DT @ 35: 0.081521959999997 +INFO - [G] LONG DT @ 35: 0.066589188000001 +INFO - [G] LONG DT @ 35: 0.054426990399997 diff --git a/lovely/log/lovely-2025.01.10-14.30.56.log b/lovely/log/lovely-2025.01.10-14.30.56.log new file mode 100644 index 0000000..1c65396 --- /dev/null +++ b/lovely/log/lovely-2025.01.10-14.30.56.log @@ -0,0 +1,227 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 212ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +WARN - [♥] Pattern 'if not _RELEASE_MODE then' on target 'engine/controller.lua' resulted in no matches +INFO - [♥] Applied 10 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-10 14:30:58 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-10 14:30:59 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-10 14:30:59 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-10 14:30:59 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-10 14:30:59 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-10 14:30:59 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-10 14:30:59 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 1.066 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0032] Injected Atlas in 856.949 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0025] Injected Sound in 283.071 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0032] Injected Stake in 0.753 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0008] Injected Rarity in 0.026 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.060 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0392] Injected Center in 1.683 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.009 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0027] Injected Blind in 0.041 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0002] Injected Seal in 0.040 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0004] Injected Suit in 0.070 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0013] Injected Rank in 0.036 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.027 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.051 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0031] Injected Challenge in 0.124 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0028] Injected Tag in 0.193 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0009] Injected Sticker in 0.577 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0009] Injected Shader in 53.226 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0020] Injected Achievement in 0.058 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 6.991 ms +INFO - [G] 2025-01-10 14:31:00 :: INFO :: TIMER :: [0011] Injected Event in 0.013 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews +INFO - [G] > help +INFO - [G] < Help: +Below is a list of commands. +echo: Repeat's what you say +help: Get command info +eval: Evaluate lua code +money: Set or add money +round: Set or add to your round +ante: Set or add to your ante +discards: Set or add to your hand +hands: Set or add to your hand +watch: Watch and execute a file when it changes. +tutorial: Modify the tutorial state. +resetshop: Reset the shop. +value: Get and modify highlighted card values + +For more information about a specific command, run 'help ' +INFO - [G] > help +INFO - [G] < Help: +Below is a list of commands. +echo: Repeat's what you say +help: Get command info +eval: Evaluate lua code +money: Set or add money +round: Set or add to your round +ante: Set or add to your ante +discards: Set or add to your hand +hands: Set or add to your hand +watch: Watch and execute a file when it changes. +tutorial: Modify the tutorial state. +resetshop: Reset the shop. +value: Get and modify highlighted card values + +For more information about a specific command, run 'help ' +INFO - [G] > echo your mother i +INFO - [G] < your mother i +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 11.872 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0032] Injected Atlas in 753.511 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0025] Injected Sound in 18.633 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0032] Injected Stake in 0.188 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0008] Injected Rarity in 0.022 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.036 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0392] Injected Center in 1.311 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.007 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0027] Injected Blind in 0.031 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0002] Injected Seal in 0.038 ms +INFO - [G] 2025-01-10 14:35:55 :: INFO :: TIMER :: [0004] Injected Suit in 0.046 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0013] Injected Rank in 0.123 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.019 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.037 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0031] Injected Challenge in 0.066 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0028] Injected Tag in 0.032 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0009] Injected Sticker in 0.061 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0009] Injected Shader in 84.833 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0020] Injected Achievement in 0.067 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 28.154 ms +INFO - [G] 2025-01-10 14:35:56 :: INFO :: TIMER :: [0011] Injected Event in 0.011 ms +INFO - [G] > help +INFO - [G] < Help: +Below is a list of commands. +echo: Repeat's what you say +help: Get command info +eval: Evaluate lua code +money: Set or add money +round: Set or add to your round +ante: Set or add to your ante +discards: Set or add to your hand +hands: Set or add to your hand +watch: Watch and execute a file when it changes. +tutorial: Modify the tutorial state. +resetshop: Reset the shop. +value: Get and modify highlighted card values + +For more information about a specific command, run 'help ' +INFO - [G] > value +INFO - [G] < This command only works while hovering over a card. Rerun it while hovering over a card. +INFO - [G] > value +INFO - [G] < Invalid argument. Use 'get' or 'set' or 'set_center'. +INFO - [G] > value get +INFO - [G] < Values: +extra money 3 +hands_played_at_create 27 +INFO - [G] > value set +INFO - [G] < Please provide a key to set +INFO - [G] > value set extra money 100 +INFO - [G] < Value set successfully. +INFO - [G] > value get +INFO - [G] < This command only works while hovering over a card. Rerun it while hovering over a card. +INFO - [G] > value get +INFO - [G] < Values: +extra 25 +hands_played_at_create 27 diff --git a/lovely/log/lovely-2025.01.10-14.42.50.log b/lovely/log/lovely-2025.01.10-14.42.50.log new file mode 100644 index 0000000..dd9b05e --- /dev/null +++ b/lovely/log/lovely-2025.01.10-14.42.50.log @@ -0,0 +1,154 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 139ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-10 14:42:52 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-10 14:42:53 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-10 14:42:53 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-10 14:42:53 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-10 14:42:53 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.691 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0032] Injected Atlas in 752.222 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0025] Injected Sound in 19.123 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0032] Injected Stake in 0.529 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0008] Injected Rarity in 0.022 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.052 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0392] Injected Center in 1.952 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.011 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0027] Injected Blind in 0.051 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0002] Injected Seal in 0.107 ms +INFO - [G] 2025-01-10 14:42:53 :: INFO :: TIMER :: [0004] Injected Suit in 0.056 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0013] Injected Rank in 0.055 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.038 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.077 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0031] Injected Challenge in 0.152 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0028] Injected Tag in 0.308 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0009] Injected Sticker in 0.805 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0009] Injected Shader in 57.027 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0020] Injected Achievement in 0.126 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 7.196 ms +INFO - [G] 2025-01-10 14:42:54 :: INFO :: TIMER :: [0011] Injected Event in 0.033 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 9.458 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0032] Injected Atlas in 742.288 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0025] Injected Sound in 16.892 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0032] Injected Stake in 0.281 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0008] Injected Rarity in 0.016 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.034 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0392] Injected Center in 1.403 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.007 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0027] Injected Blind in 0.034 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0002] Injected Seal in 0.046 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0004] Injected Suit in 0.063 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0013] Injected Rank in 0.059 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.171 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.040 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0031] Injected Challenge in 0.053 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0028] Injected Tag in 0.108 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0009] Injected Sticker in 1.012 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0009] Injected Shader in 109.197 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0020] Injected Achievement in 0.104 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 37.331 ms +INFO - [G] 2025-01-10 15:00:39 :: INFO :: TIMER :: [0011] Injected Event in 0.011 ms diff --git a/lovely/log/lovely-2025.01.10-20.43.00.log b/lovely/log/lovely-2025.01.10-20.43.00.log new file mode 100644 index 0000000..e2e0c07 --- /dev/null +++ b/lovely/log/lovely-2025.01.10-20.43.00.log @@ -0,0 +1,128 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 138ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-10 20:43:02 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-10 20:43:03 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-10 20:43:03 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-10 20:43:03 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-10 20:43:03 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-10 20:43:03 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-10 20:43:03 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.782 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0032] Injected Atlas in 809.700 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0025] Injected Sound in 25.339 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0032] Injected Stake in 0.825 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0008] Injected Rarity in 0.026 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.060 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0392] Injected Center in 2.641 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.009 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0027] Injected Blind in 0.447 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0002] Injected Seal in 0.057 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0004] Injected Suit in 0.082 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0013] Injected Rank in 0.103 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.033 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.204 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0031] Injected Challenge in 0.139 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0028] Injected Tag in 0.614 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0009] Injected Sticker in 0.495 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0009] Injected Shader in 139.276 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0020] Injected Achievement in 0.078 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 6.753 ms +INFO - [G] 2025-01-10 20:43:04 :: INFO :: TIMER :: [0011] Injected Event in 0.015 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews diff --git a/lovely/log/lovely-2025.01.10-21.28.54.log b/lovely/log/lovely-2025.01.10-21.28.54.log new file mode 100644 index 0000000..6c6e603 --- /dev/null +++ b/lovely/log/lovely-2025.01.10-21.28.54.log @@ -0,0 +1,241 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 149ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-10 21:28:56 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-10 21:28:57 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-10 21:28:57 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-10 21:28:57 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-10 21:28:57 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-10 21:28:57 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-10 21:28:57 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.662 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0032] Injected Atlas in 774.183 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0025] Injected Sound in 19.506 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0032] Injected Stake in 0.551 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0008] Injected Rarity in 0.024 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.052 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0392] Injected Center in 1.775 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.010 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0027] Injected Blind in 0.061 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0002] Injected Seal in 0.048 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0004] Injected Suit in 0.057 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0013] Injected Rank in 0.063 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.035 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.088 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0031] Injected Challenge in 0.155 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0028] Injected Tag in 0.293 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0009] Injected Sticker in 0.608 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0009] Injected Shader in 54.808 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0020] Injected Achievement in 0.099 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 6.453 ms +INFO - [G] 2025-01-10 21:28:58 :: INFO :: TIMER :: [0011] Injected Event in 0.013 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews +INFO - [G] > help +INFO - [G] < Help: +Below is a list of commands. +echo: Repeat's what you say +help: Get command info +eval: Evaluate lua code +money: Set or add money +round: Set or add to your round +ante: Set or add to your ante +discards: Set or add to your hand +hands: Set or add to your hand +watch: Watch and execute a file when it changes. +tutorial: Modify the tutorial state. +resetshop: Reset the shop. +value: Get and modify highlighted card values + +For more information about a specific command, run 'help ' +INFO - [G] > value get +INFO - [G] < Values: +extra Emult 1 +extra Emult_mod 0.03 +INFO - [G] > value set 9999999 +INFO - [G] < Please provide a value to set +INFO - [G] > value set Emult 9 +INFO - [G] < Value set successfully. +INFO - [G] > value get +INFO - [G] < Values: +extra Emult 1 +extra Emult_mod 0.03 +Emult 9 +INFO - [G] > value set extra Emult 999999999999999 +INFO - [G] < Value set successfully. +INFO - [G] > value set emult 9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999 +INFO - [G] < Value set successfully. +INFO - [G] > value get +INFO - [G] < Values: +emult 1e+301 +extra Emult 1e+15 +extra Emult_mod 0.03 +Emult 9 +INFO - [G] > value set extra Emult 1e+999 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+308 +INFO - [G] < This command only works while hovering over a card. Rerun it while hovering over a card. +INFO - [G] > value set extra Emult 1e+300 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+301 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+302 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+307 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+308 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+3010 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+300 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+400 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+308 +INFO - [G] < This command only works while hovering over a card. Rerun it while hovering over a card. +INFO - [G] > value set extra Emult 1e+308 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+309 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra Emult 1e+308 +INFO - [G] < Value set successfully. +INFO - [G] > value get +INFO - [G] < Values: +emult 1e+301 +extra Emult 1e+308 +extra Emult_mod 0.03 +Emult 9 +INFO - [G] > value Emult_mod 1e+308 +INFO - [G] < Invalid argument. Use 'get' or 'set' or 'set_center'. +INFO - [G] > value get Emult_mod 1e+308 +INFO - [G] < Values: +emult 1e+301 +extra Emult 1e+308 +extra Emult_mod 0.03 +Emult 9 +INFO - [G] > value set extra Emult_mod 1e+308 +INFO - [G] < This command only works while hovering over a card. Rerun it while hovering over a card. +INFO - [G] > value set extra Emult_mod 1e+308 +INFO - [G] < Value set successfully. +INFO - [G] 2025-01-10 21:43:27 :: INFO :: TIMER :: [0032] Injected Atlas in 762.058 ms +INFO - [G] [DebugPlus] Reloaded Atlases +INFO - [G] 2025-01-10 21:43:29 :: INFO :: TIMER :: [0032] Injected Atlas in 759.027 ms +INFO - [G] [DebugPlus] Reloaded Atlases +INFO - [G] 2025-01-10 21:48:55 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-10 21:48:55 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 6.112 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0032] Injected Atlas in 750.030 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0025] Injected Sound in 17.579 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0032] Injected Stake in 0.100 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0008] Injected Rarity in 0.019 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.032 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0392] Injected Center in 1.404 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.008 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0027] Injected Blind in 0.031 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0002] Injected Seal in 0.039 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0004] Injected Suit in 0.043 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0013] Injected Rank in 0.052 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.126 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.058 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0031] Injected Challenge in 0.022 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0028] Injected Tag in 0.033 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0009] Injected Sticker in 0.064 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0009] Injected Shader in 28.218 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0020] Injected Achievement in 0.109 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 13.057 ms +INFO - [G] 2025-01-10 21:48:56 :: INFO :: TIMER :: [0011] Injected Event in 0.012 ms diff --git a/lovely/log/lovely-2025.01.11-14.30.44.log b/lovely/log/lovely-2025.01.11-14.30.44.log new file mode 100644 index 0000000..a1c134e --- /dev/null +++ b/lovely/log/lovely-2025.01.11-14.30.44.log @@ -0,0 +1,151 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 160ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-11 14:30:45 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-11 14:30:46 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-11 14:30:46 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-11 14:30:46 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-11 14:30:47 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0000] Injected Language in 0.003 ms +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 2.096 ms +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0032] Injected Atlas in 832.546 ms +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0025] Injected Sound in 111.684 ms +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0032] Injected Stake in 0.774 ms +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0008] Injected Rarity in 0.021 ms +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.070 ms +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0392] Injected Center in 2.300 ms +INFO - [G] 2025-01-11 14:30:47 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.013 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0027] Injected Blind in 0.074 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0002] Injected Seal in 0.137 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0004] Injected Suit in 0.067 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0013] Injected Rank in 0.083 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.029 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.086 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0031] Injected Challenge in 0.162 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0028] Injected Tag in 0.306 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0009] Injected Sticker in 0.602 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0009] Injected Shader in 116.075 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0020] Injected Achievement in 0.121 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 8.327 ms +INFO - [G] 2025-01-11 14:30:48 :: INFO :: TIMER :: [0011] Injected Event in 0.015 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews +INFO - [G] 2025-01-11 14:30:57 :: INFO :: TIMER :: [0000] Injected Language in 0.002 ms +INFO - [G] 2025-01-11 14:30:57 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 4.288 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0032] Injected Atlas in 806.544 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0025] Injected Sound in 19.484 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0032] Injected Stake in 0.231 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0008] Injected Rarity in 0.019 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.054 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0392] Injected Center in 1.293 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.011 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0027] Injected Blind in 0.061 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0002] Injected Seal in 0.072 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0004] Injected Suit in 0.060 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0013] Injected Rank in 0.102 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.022 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.038 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0031] Injected Challenge in 0.078 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0028] Injected Tag in 0.047 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0009] Injected Sticker in 0.076 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0009] Injected Shader in 22.948 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0020] Injected Achievement in 0.153 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 21.668 ms +INFO - [G] 2025-01-11 14:30:58 :: INFO :: TIMER :: [0011] Injected Event in 0.017 ms diff --git a/lovely/log/lovely-2025.01.11-16.33.51.log b/lovely/log/lovely-2025.01.11-16.33.51.log new file mode 100644 index 0000000..f5be140 --- /dev/null +++ b/lovely/log/lovely-2025.01.11-16.33.51.log @@ -0,0 +1,164 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 136ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-11 16:33:52 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-11 16:33:53 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-11 16:33:53 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-11 16:33:53 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-11 16:33:53 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-11 16:33:53 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-11 16:33:53 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.644 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0032] Injected Atlas in 767.253 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0025] Injected Sound in 18.101 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0032] Injected Stake in 0.474 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0008] Injected Rarity in 0.020 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.053 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0392] Injected Center in 1.746 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.012 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0027] Injected Blind in 0.057 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0002] Injected Seal in 0.061 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0004] Injected Suit in 0.065 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0013] Injected Rank in 0.068 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.032 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.083 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0031] Injected Challenge in 0.163 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0028] Injected Tag in 0.315 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0009] Injected Sticker in 0.912 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0009] Injected Shader in 81.992 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0000] Injected Keybind in 0.002 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0020] Injected Achievement in 0.100 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 5.940 ms +INFO - [G] 2025-01-11 16:33:54 :: INFO :: TIMER :: [0011] Injected Event in 0.014 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] > value get +INFO - [G] < Values: +extra scale_mod 3.2e82 +extra scale 8.8e84 +extra shadow_scale 8.8e84 +extra shadow_scale_mod 3.2e82 +hands_played_at_create 3.6e+84 +INFO - [G] > value set extra scale_mod 1e300 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra scale 1e300 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra shadow_scale 1e300 +INFO - [G] < Value set successfully. +INFO - [G] > value set extra shadow_scale_mod 1e300 +INFO - [G] < Value set successfully. +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load +INFO - [G] > value get +INFO - [G] < Values: +extra scale_mod 5.392e367 +extra scale 1.219e370 +extra shadow_scale 1.219e370 +extra shadow_scale_mod 5.392e367 +hands_played_at_create 1.1e+152 +INFO - [G] > value set extra scale_mod 1e500 +INFO - [G] < Value set successfully. diff --git a/lovely/log/lovely-2025.01.13-03.39.25.log b/lovely/log/lovely-2025.01.13-03.39.25.log new file mode 100644 index 0000000..51461c5 --- /dev/null +++ b/lovely/log/lovely-2025.01.13-03.39.25.log @@ -0,0 +1,264 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 174ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-13 03:39:27 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-13 03:39:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-13 03:39:28 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-13 03:39:28 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-13 03:39:28 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-13 03:39:29 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-13 03:39:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-13 03:39:29 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-13 03:39:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-13 03:39:29 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-13 03:39:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-13 03:39:29 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-13 03:39:29 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-13 03:39:29 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-13 03:39:29 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-13 03:39:29 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-13 03:39:29 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-13 03:39:29 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 1.225 ms +INFO - [G] 2025-01-13 03:39:29 :: INFO :: TIMER :: [0032] Injected Atlas in 850.444 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0025] Injected Sound in 115.481 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0032] Injected Stake in 0.946 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0008] Injected Rarity in 0.028 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.186 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0392] Injected Center in 1.805 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.013 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0027] Injected Blind in 0.045 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0002] Injected Seal in 0.049 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0004] Injected Suit in 0.052 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0013] Injected Rank in 0.053 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.054 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.070 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0031] Injected Challenge in 0.148 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0028] Injected Tag in 0.283 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0009] Injected Sticker in 0.610 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0009] Injected Shader in 153.250 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0020] Injected Achievement in 0.105 ms +INFO - [G] 2025-01-13 03:39:30 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 7.810 ms +INFO - [G] 2025-01-13 03:39:31 :: INFO :: TIMER :: [0011] Injected Event in 0.025 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews +INFO - [G] line not found +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-13 03:39:48 :: ERROR :: StackTrace :: Oops! The game crashed +[SMODS Cryptid "Items/Exotic.lua"]:801: attempt to call method 'start_dissolve' (a nil value) +Stack Traceback +=============== +(1) Lua upvalue 'orig' at file 'main.lua:612' + Local variables: + msg = string: "[SMODS Cryptid \"Items/Exotic.lua\"]:801: attempt to call method 'start_dissolve' (a nil value)" + (*temporary) = Lua function '?' (defined at line 31 of chunk [SMODS _ "src/logging.lua"]) + (*temporary) = string: "Oops! The game crashed\ +" +(2) Lua local 'handler' at file 'console.lua:551' (from lovely module debugplus.console) + Local variables: + msg = string: "[SMODS Cryptid \"Items/Exotic.lua\"]:801: attempt to call method 'start_dissolve' (a nil value)" +(3) LÖVE method at file 'boot.lua:352' + Local variables: + errhand = Lua function '(LÖVE Function)' (defined at line 550 of chunk [lovely debugplus.console "console.lua"]) + handler = Lua function '(LÖVE Function)' (defined at line 550 of chunk [lovely debugplus.console "console.lua"]) +(4) Lua method 'remove_from_deck' at file 'Items/Exotic.lua:801' (from mod with id Cryptid) + Local variables: + self = table: 0x0468eb50 {alerted:true, loc_vars:function: 0x0468ed88, object_type:Joker, _saved_d_u:true (more...)} + card = table: 0x062cbde8 {shadow_parrallax:table: 0x062cc908, click_offset:table: 0x062cc0b8, children:table: 0x062d2c98 (more...)} + from_debuff = nil + (*temporary) = nil + (*temporary) = string: "\"MANUAL_REPLACE\"" + (*temporary) = string: "attempt to call method 'start_dissolve' (a nil value)" +(5) Lua method 'remove_from_deck' at file 'card.lua:820' + Local variables: + self = table: 0x062cbde8 {shadow_parrallax:table: 0x062cc908, click_offset:table: 0x062cc0b8, children:table: 0x062d2c98 (more...)} + from_debuff = nil + obj = table: 0x0468eb50 {alerted:true, loc_vars:function: 0x0468ed88, object_type:Joker, _saved_d_u:true (more...)} +(6) Lua method 'remove' at file 'card.lua:5391' + Local variables: + self = table: 0x062cbde8 {shadow_parrallax:table: 0x062cc908, click_offset:table: 0x062cc0b8, children:table: 0x062d2c98 (more...)} +(7) Lua upvalue 'rma' at file 'functions/misc_functions.lua:153' + Local variables: + t = table: 0x04336fd0 {1:table: 0x04de7ef8, 2:table: 0x04de1680, 3:table: 0x04e70608, 4:table: 0x047634f0 (more...)} + (for index) = number: 461 + (for limit) = number: 1 + (for step) = number: -1 + i = number: 461 + v = table: 0x062cbde8 {shadow_parrallax:table: 0x062cc908, click_offset:table: 0x062cc0b8, children:table: 0x062d2c98 (more...)} +(8) Lua global 'remove_all' at file 'Items/Misc.lua:1904' (from mod with id Cryptid) + Local variables: + t = table: 0x04336fd0 {1:table: 0x04de7ef8, 2:table: 0x04de1680, 3:table: 0x04e70608, 4:table: 0x047634f0 (more...)} +(9) Lua method 'delete_run' at file 'game.lua:1258' + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} +(10) Lua field 'func' at file 'functions/button_callbacks.lua:3022' +(11) Lua method 'handle' at file 'engine/event.lua:99' + Local variables: + self = table: 0x06417550 {start_timer:true, timer:REAL, func:function: 0x069f77a8, blockable:true (more...)} + _results = table: 0x13cea148 {blocking:true, pause_skip:false, time_done:false, completed:false} +(12) Lua method 'update' at file 'engine/event.lua:182' + Local variables: + self = table: 0x04f271d8 {queue_last_processed:9.7500000000001, queues:table: 0x04f27200, queue_dt:0.016666666666667 (more...)} + dt = number: 0.0135071 + forced = nil + (for generator) = C function: next + (for state) = table: 0x04f27200 {unlock:table: 0x04f27228, other:table: 0x04f276a0, tutorial:table: 0x04f27278 (more...)} + (for control) = number: nan + k = string: "base" + v = table: 0x04f27250 {1:table: 0x06417550, 2:table: 0x06417578, 3:table: 0x06423430, 4:table: 0x06424918 (more...)} + blocked = boolean: false + i = number: 1 + results = table: 0x13cea148 {blocking:true, pause_skip:false, time_done:false, completed:false} +(13) Lua upvalue 'gameUpdateRef' at file 'game.lua:2734' + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} + dt = number: 0 + http_resp = nil +(14) Lua upvalue 'upd' at Steamodded file 'src/ui.lua:81' + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} + dt = number: 0.0135071 +(15) Lua upvalue 'upd' at file 'main.lua:4093' + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} + dt = number: 0.0135071 +(16) Lua upvalue 'upd' at file 'Items/Blinds.lua:1310' (from mod with id Cryptid) + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} + dt = number: 0.0135071 +(17) Lua upvalue 'upd' at file 'Items/CodeCards.lua:4106' (from mod with id Cryptid) + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} + dt = number: 0.0135071 +(18) Lua upvalue 'upd' at file 'Items/Decks.lua:429' (from mod with id Cryptid) + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} + dt = number: 0.0135071 +(19) Lua upvalue 'upd' at file 'Items/MiscJokers.lua:6658' (from mod with id Cryptid) + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} + dt = number: 0.0135071 +(20) Lua method 'update' at file 'Cryptid.lua:2948' (from mod with id Cryptid) + Local variables: + self = table: 0x043329d8 {F_GUIDE:false, F_CRASH_REPORTS:false, F_QUIT_BUTTON:true, HUD_tags:table: 0x077952d8 (more...)} + dt = number: 0.0135071 +(21) Lua upvalue 'love_update_ref' at file 'main.lua:1141' + Local variables: + dt = number: 0.0135071 +(22) Lua upvalue 'oldupd' at file 'main.lua:2951' + Local variables: + dt = number: 0.0135071 +(23) Lua field 'update' at file 'main.lua:4120' + Local variables: + dt = number: 0.0135071 +(24) Lua function '?' at file 'main.lua:1079' (best guess) +(25) global C function 'xpcall' +(26) LÖVE function at file 'boot.lua:377' (best guess) + Local variables: + func = Lua function '?' (defined at line 1050 of chunk main.lua) + inerror = boolean: true + deferErrhand = Lua function '(LÖVE Function)' (defined at line 348 of chunk [love "boot.lua"]) + earlyinit = Lua function '(LÖVE Function)' (defined at line 355 of chunk [love "boot.lua"]) + +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] file not found: main.lua: No such file or directory +INFO - [G] 2025-01-13 03:39:48 :: INFO :: StackTrace :: Additional Context: +Balatro Version: 1.0.1n-FULL +Modded Version: 1.0.0~ALPHA-1220a-STEAMODDED +LÖVE Version: 11.5.0 +Lovely Version: 0.6.0 +Steamodded Mods: + 1: J Cursor by Jie65535, MarioMak967 [ID: JCursor] + 2: Cryptid by MathIsFun_, Cryptid and Balatro Discords [ID: Cryptid, Priority: 1e+299, Version: 0.5.3a, Uses Lovely] + 3: Talisman by MathIsFun_, Mathguy24, jenwalter666, cg-223 [ID: Talisman, Version: 2.0.2, Uses Lovely] + Break Infinity: omeganum + 4: Cartomancer by stupxd aka stupid [ID: cartomancer, Priority: 69, Version: 4.10, Uses Lovely] + 5: Handy by SleepyG11 [ID: Handy, Version: 1.1.5, Uses Lovely] + 6: Incantation by jenwalter666, MathIsFun_ [ID: incantation, Priority: 9e+301, Version: 0.5.10, Uses Lovely] + 7: DebugPlus by WilsontheWolf [ID: DebugPlus, Version: 1.3.0, Uses Lovely] +Lovely Mods: diff --git a/lovely/log/lovely-2025.01.14-00.56.45.log b/lovely/log/lovely-2025.01.14-00.56.45.log new file mode 100644 index 0000000..a02d182 --- /dev/null +++ b/lovely/log/lovely-2025.01.14-00.56.45.log @@ -0,0 +1,128 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 159ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-14 00:56:48 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-14 00:56:49 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-14 00:56:49 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-14 00:56:49 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-14 00:56:49 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-14 00:56:49 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-14 00:56:49 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 1.039 ms +INFO - [G] 2025-01-14 00:56:50 :: INFO :: TIMER :: [0032] Injected Atlas in 872.290 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0025] Injected Sound in 262.811 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0032] Injected Stake in 0.477 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0008] Injected Rarity in 0.020 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.133 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0392] Injected Center in 1.843 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.011 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0027] Injected Blind in 0.053 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0002] Injected Seal in 0.089 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0004] Injected Suit in 0.052 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0013] Injected Rank in 0.055 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.027 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.081 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0031] Injected Challenge in 0.162 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0028] Injected Tag in 0.297 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0009] Injected Sticker in 0.775 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0009] Injected Shader in 56.400 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0020] Injected Achievement in 0.096 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 7.712 ms +INFO - [G] 2025-01-14 00:56:51 :: INFO :: TIMER :: [0011] Injected Event in 0.012 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews diff --git a/lovely/log/lovely-2025.01.14-22.28.58.log b/lovely/log/lovely-2025.01.14-22.28.58.log new file mode 100644 index 0000000..a21f9fd --- /dev/null +++ b/lovely/log/lovely-2025.01.14-22.28.58.log @@ -0,0 +1,128 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 212ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-14 22:29:00 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-14 22:29:01 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-14 22:29:01 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-14 22:29:01 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-14 22:29:01 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-14 22:29:02 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-14 22:29:02 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-14 22:29:02 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-14 22:29:02 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-14 22:29:02 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-14 22:29:02 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-14 22:29:02 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-14 22:29:02 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-14 22:29:02 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-14 22:29:02 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-14 22:29:02 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-14 22:29:02 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-14 22:29:02 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 1.478 ms +INFO - [G] 2025-01-14 22:29:02 :: INFO :: TIMER :: [0032] Injected Atlas in 858.054 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0025] Injected Sound in 198.016 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0032] Injected Stake in 0.478 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0008] Injected Rarity in 0.021 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.134 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0392] Injected Center in 1.534 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.009 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0027] Injected Blind in 0.040 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0002] Injected Seal in 0.036 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0004] Injected Suit in 0.054 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0013] Injected Rank in 0.039 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.026 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.074 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0031] Injected Challenge in 0.153 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0028] Injected Tag in 0.273 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0009] Injected Sticker in 0.662 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0009] Injected Shader in 181.900 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0020] Injected Achievement in 0.094 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 7.321 ms +INFO - [G] 2025-01-14 22:29:03 :: INFO :: TIMER :: [0011] Injected Event in 0.012 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews diff --git a/lovely/log/lovely-2025.01.18-16.12.50.log b/lovely/log/lovely-2025.01.18-16.12.50.log new file mode 100644 index 0000000..e23a4b6 --- /dev/null +++ b/lovely/log/lovely-2025.01.18-16.12.50.log @@ -0,0 +1,131 @@ +INFO - [♥] Lovely 0.6.0 +INFO - [♥] Game directory is at "Z:\\run\\media\\vomitblood\\DataDisk1\\SteamLibrary\\steamapps\\common\\Balatro" +INFO - [♥] Writing logs to "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\log" +INFO - [♥] Using mod directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods" +INFO - [♥] Cleaning up dumps directory at "C:\\users\\steamuser\\AppData\\Roaming\\Balatro\\Mods\\lovely\\dump" +INFO - [♥] Initialization complete in 141ms +INFO - [♥] Applied 1 patch to 'conf.lua' +WARN - [♥] Pattern 'G.ARGS.score_intensity.organ = G.video_organ or G.ARGS.score_intensity.required_score > 0 and math.max(math.min(0.4, 0.1*math.log(G.ARGS.score_intensity.earned_score/(G.ARGS.score_intensity.required_score+1), 5)),0.) or 0' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'if type(G.GAME.current_round.current_hand.chips) ~= \'number\' or type(G.GAME.current_round.current_hand.mult) ~= \'number\' then' on target 'main.lua' resulted in no matches +WARN - [♥] Pattern 'modded and {' on target 'main.lua' resulted in no matches +INFO - [♥] Applied 30 patches to 'main.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [♥] Applied 12 patches to 'engine/controller.lua' +INFO - [♥] Applied 13 patches to 'back.lua' +INFO - [♥] Applied 14 patches to 'tag.lua' +INFO - [♥] Applied 2 patches to 'engine/moveable.lua' +INFO - [♥] Applied 2 patches to 'engine/sprite.lua' +INFO - [♥] Applied 2 patches to 'engine/animatedsprite.lua' +WARN - [♥] Pattern 'assembled_string = assembled_string..(type(subpart) == \'string\' and subpart or args.vars[tonumber(subpart[1])] or \'ERROR\')' on target 'functions/misc_functions.lua' resulted in no matches +INFO - [♥] Applied 46 patches to 'functions/misc_functions.lua' +INFO - [♥] Applied 82 patches to 'game.lua' +INFO - [♥] Applied 1 patch to 'globals.lua' +WARN - [♥] Pattern 'self.config.chosen = true' on target 'engine/ui.lua' resulted in no matches +INFO - [♥] Applied 7 patches to 'engine/ui.lua' +WARN - [♥] Pattern '{card_limit = _size, type = \'consumeable\', highlight_limit = 1}' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].chips, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}},' on target 'functions/UI_definitions.lua' resulted in no matches +WARN - [♥] Pattern '{n=G.UIT.T, config={text = G.GAME.hands[handname].mult, scale = 0.45, colour = G.C.UI.TEXT_LIGHT}}' on target 'functions/UI_definitions.lua' resulted in no matches +INFO - [♥] Applied 118 patches to 'functions/UI_definitions.lua' +WARN - [♥] Pattern 'ease_to = G.GAME.chips + math.floor(hand_chips * mult) * (e and e.antiscore and -1 or 1),' on target 'functions/state_events.lua' resulted in no matches +INFO - [♥] Applied 98 patches to 'functions/state_events.lua' +WARN - [♥] Pattern 'local cfg = (card and card.ability) or _c[\'config\']' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'elseif v.boss.showdown and (G.GAME.round_resets.ante)%G.GAME.win_ante == 0 and G.GAME.round_resets.ante >= 2 then' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Regex '(?[\\t ]*)if G\\.F_NO_ACHIEVEMENTS then return end\\n[\\s\\S][\\s\\S]{16}--\\|LOCAL SETTINGS FILE' on target 'functions/common_events.lua' resulted in no matches +WARN - [♥] Pattern 'func = (function() if eval_func(card) then if not first or first then card:juice_up(0.1, 0.1) end;juice_card_until(card, eval_func, nil, 0.8) end return true end)' on target 'functions/common_events.lua' resulted in no matches +INFO - [♥] Applied 124 patches to 'functions/common_events.lua' +WARN - [♥] Pattern 'G.pack_cards:emplace(v)' on target 'functions/button_callbacks.lua' resulted in no matches +INFO - [♥] Applied 53 patches to 'functions/button_callbacks.lua' +WARN - [♥] Pattern 'if self.ability.name == \'Campfire\' and G.GAME.blind.boss and not (G.GAME.blind.config and G.GAME.blind.config.bonus) and self.ability.x_mult > 1 then' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'Xmult_mod = G.P_CENTERS.v_observatory.config.extra' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if k ~= \'focused_ui\' and k ~= \"front\" and k ~= \"back\" and k ~= \"soul_parts\" and k ~= \"center\" and k ~= \'floating_sprite\' and k~= \"shadow\" and k~= \"use_button\" and k ~= \'buy_button\' and k ~= \'buy_and_use_button\' and k~= \"debuff\" and k ~= \'price\' and k~= \'particles\' and k ~= \'h_popup\' then v:draw() end' on target 'card.lua' resulted in no matches +WARN - [♥] Pattern 'if G.GAME.blind then G.E_MANAGER:add_event(Event({ func = function() G.GAME.blind:set_blind(nil, true, nil); return true end })) end' on target 'card.lua' resulted in no matches +INFO - [♥] Applied 206 patches to 'card.lua' +INFO - [♥] Applied 20 patches to 'cardarea.lua' +INFO - [♥] Applied 32 patches to 'blind.lua' +INFO - [♥] Applied 5 patches to 'engine/text.lua' +INFO - [G] Totally applied 4 replacements to overrides.lua +INFO - [G] Failed to connect to the debug server +INFO - [G] 2025-01-18 16:12:53 :: DEBUG :: DebugConsole :: Steamodded Debug Socket started ! +INFO - [♥] Applied 9 patches to 'engine/sound_manager.lua' +INFO - [♥] Applied 2 patches to 'engine/string_packer.lua' +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): mod.lua +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Saving Mod Info: cartomancer +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): Cryptid.lua +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Saving Mod Info: Cryptid +INFO - [G] 2025-01-18 16:12:54 :: INFO :: DefaultLogger :: Valid JSON file found +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded.lua +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Saving Mod Info: Handy +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): Incantation.lua +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Saving Mod Info: incantation +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): JCursor.lua +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Saving Mod Info: JCursor +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Processing Mod file (Legacy header): steamodded_metadata.lua +INFO - [G] 2025-01-18 16:12:54 :: TRACE :: Loader :: Saving Mod Info: Talisman +INFO - [G] 2025-01-18 16:12:54 :: DEBUG :: DefaultLogger :: JCursor loaded! +INFO - [G] Loading file Achievements.lua +INFO - [G] Loading file Antimatter.lua +INFO - [G] Loading file Blinds.lua +INFO - [G] Loading file Challenges.lua +INFO - [G] Loading file CodeCards.lua +INFO - [G] Loading file CryptidJokerDisplay.lua +INFO - [G] Warning: CryptidJokerDisplay.lua has no items +INFO - [G] Loading file Decks.lua +INFO - [G] Loading file Enhanced.lua +INFO - [G] Loading file EpicJokers.lua +INFO - [G] Loading file Exotic.lua +INFO - [G] Loading file M.lua +INFO - [G] Loading file Misc.lua +INFO - [G] Loading file MiscJokers.lua +INFO - [G] Loading file Planets.lua +INFO - [G] Loading file Sleeves.lua +INFO - [G] Loading file Spectrals.lua +INFO - [G] Loading file Spooky.lua +INFO - [G] Loading file Stakes.lua +INFO - [G] Loading file Tags.lua +INFO - [G] Loading file Vouchers.lua +INFO - [G] Loading file dummy_https.lua +INFO - [G] Warning: dummy_https.lua has no items +INFO - [G] Loading file dummy_timerblinds.lua +INFO - [G] Warning: dummy_timerblinds.lua has no items +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_modicon +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_placeholders +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasepic +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasone +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlastwo +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasthree +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasspooky +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasexotic +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_atlasnotjokers +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_tag_cry +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Atlas :: Detected duplicate register call on object cry_misc +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Sticker :: Detected duplicate register call on object perishable +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Sticker :: Detected duplicate register call on object pinned +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Sticker :: Detected duplicate register call on object eternal +INFO - [G] 2025-01-18 16:12:54 :: WARN :: Sticker :: Detected duplicate register call on object rental +INFO - [G] 2025-01-18 16:12:54 :: INFO :: TIMER :: [0000] Injected Language in 0.001 ms +INFO - [G] 2025-01-18 16:12:54 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 0.683 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0032] Injected Atlas in 780.652 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0025] Injected Sound in 20.941 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0032] Injected Stake in 0.489 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0008] Injected Rarity in 0.026 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0007] Injected ObjectType in 0.059 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0392] Injected Center in 2.139 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0011] Injected Undiscovered Sprite in 0.008 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0027] Injected Blind in 0.043 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0002] Injected Seal in 0.127 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0004] Injected Suit in 0.052 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0013] Injected Rank in 0.063 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0016] Injected DeckSkin in 0.040 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0016] Injected PokerHand in 0.082 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0031] Injected Challenge in 0.038 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0028] Injected Tag in 0.871 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0009] Injected Sticker in 0.290 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0009] Injected Shader in 58.620 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0000] Injected Keybind in 0.001 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0020] Injected Achievement in 0.123 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0000] Injected [INTERNAL] in 6.579 ms +INFO - [G] 2025-01-18 16:12:55 :: INFO :: TIMER :: [0011] Injected Event in 0.014 ms +INFO - [G] [DebugPlus] Press [/] to toggle console and press [shift] + [/] to toggle new log previews +INFO - [G] ERROR LOADING GAME: Card area 'shop_vouchers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_jokers' not instantiated before load +INFO - [G] ERROR LOADING GAME: Card area 'shop_booster' not instantiated before load