diff --git a/LICENSE b/LICENSE index a1b6b8208..e72bfddab 100644 --- a/LICENSE +++ b/LICENSE @@ -1,21 +1,674 @@ -MIT License + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 -Copyright (c) 2026 Aleksander Medvedev + 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. -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: + Preamble -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. + The GNU General Public License is a free, copyleft license for +software and other kinds of works. -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. + 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/README.md b/README.md index ec2395501..710aa2e01 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ ![CI](https://github.com/Pumpkin-MC/Pumpkin/actions/workflows/rust.yml/badge.svg) [![Discord](https://img.shields.io/discord/1268592337445978193.svg?label=&logo=discord&logoColor=ffffff&color=7389D8&labelColor=6A7EC2)](https://discord.gg/wT8XjrjKkf) -[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) +[![License: GPL](https://img.shields.io/badge/License-GPLv3-yellow.svg)](https://opensource.org/licenses/gpl-3-0) diff --git a/pumpkin-util/src/math/block_box.rs b/pumpkin-util/src/math/block_box.rs index 5cf7efdbb..1dc61899b 100644 --- a/pumpkin-util/src/math/block_box.rs +++ b/pumpkin-util/src/math/block_box.rs @@ -145,10 +145,12 @@ impl BlockBox { #[must_use] pub const fn intersects(&self, other: &Self) -> bool { - self.min.x < other.max.x - && self.max.x > other.min.x - && self.min.y < other.max.y - && self.max.y > other.min.y + self.max.x >= other.min.x + && self.min.x <= other.max.x + && self.max.z >= other.min.z + && self.min.z <= other.max.z + && self.max.y >= other.min.y + && self.min.y <= other.max.y } #[must_use] diff --git a/pumpkin-world/src/chunk_system.rs b/pumpkin-world/src/chunk_system.rs index ecbeab030..ae110fa6a 100644 --- a/pumpkin-world/src/chunk_system.rs +++ b/pumpkin-world/src/chunk_system.rs @@ -935,6 +935,20 @@ impl GenerationCache for Cache { .then(|| self.chunks[(dx * self.size + dz) as usize].get_proto_chunk()) } + fn try_get_proto_chunk(&self, chunk_x: i32, chunk_z: i32) -> Option<&ProtoChunk> { + let dx = chunk_x - self.x; + let dz = chunk_z - self.z; + + if dx < 0 || dx >= self.size || dz < 0 || dz >= self.size { + return None; + } + + match &self.chunks[(dx * self.size + dz) as usize] { + Chunk::Proto(chunk) => Some(chunk), + Chunk::Level(_) => None, + } + } + fn get_center_chunk(&self) -> &ProtoChunk { let mid = ((self.size * self.size) >> 1) as usize; self.chunks[mid].get_proto_chunk() diff --git a/pumpkin-world/src/generation/noise/router/chunk_noise_router.rs b/pumpkin-world/src/generation/noise/router/chunk_noise_router.rs index 0e19c72fa..8f5d7d7cc 100644 --- a/pumpkin-world/src/generation/noise/router/chunk_noise_router.rs +++ b/pumpkin-world/src/generation/noise/router/chunk_noise_router.rs @@ -350,11 +350,6 @@ impl<'a> ChunkNoiseRouter<'a> { ChunkNoiseFunctionComponent::PassThrough(pass_through.clone()) } ProtoNoiseFunctionComponent::Wrapper(wrapper) => { - //NOTE: Due to our previous invariant with the proto-function, it is guaranteed - // that the wrapped function is already on the stack - - // NOTE: Current wrapped functions do not give different values than what they - // wrap. If they do, maxs and mins need to be changed here let min_value = component_stack[wrapper.input_index].min(); let max_value = component_stack[wrapper.input_index].max(); diff --git a/pumpkin-world/src/generation/proto_chunk.rs b/pumpkin-world/src/generation/proto_chunk.rs index 0d3ebb9f9..fbded45a5 100644 --- a/pumpkin-world/src/generation/proto_chunk.rs +++ b/pumpkin-world/src/generation/proto_chunk.rs @@ -1,5 +1,6 @@ use std::collections::HashMap; use std::pin::Pin; +use std::sync::Arc; use pumpkin_data::block_properties::is_air; use pumpkin_data::dimension::Dimension; @@ -53,6 +54,8 @@ pub trait GenerationCache: HeightLimitView + BlockAccessor { fn get_chunk_mut(&mut self, chunk_x: i32, chunk_z: i32) -> Option<&mut ProtoChunk>; fn get_chunk(&self, chunk_x: i32, chunk_z: i32) -> Option<&ProtoChunk>; + fn try_get_proto_chunk(&self, chunk_x: i32, chunk_z: i32) -> Option<&ProtoChunk>; + fn get_block_state(&self, pos: &Vector3) -> RawBlockState; fn get_fluid_and_fluid_state(&self, position: &Vector3) -> (Fluid, FluidState); fn set_block_state(&mut self, pos: &Vector3, block_state: &BlockState); @@ -987,6 +990,9 @@ impl ProtoChunk { let mut tasks = Vec::new(); { let center_chunk = cache.get_center_chunk(); + let center_x = center_chunk.x; + let center_z = center_chunk.z; + for (id, instance) in ¢er_chunk.structure_starts { if let Some(s) = STRUCTURES.get(id) && s.step.ordinal() != step @@ -1008,6 +1014,72 @@ impl ProtoChunk { } } } + + let radius = 8; + for dx in -radius..=radius { + for dz in -radius..=radius { + if dx == 0 && dz == 0 { + continue; + } + + let neighbor_x = center_x + dx; + let neighbor_z = center_z + dz; + + if let Some(neighbor) = cache.try_get_proto_chunk(neighbor_x, neighbor_z) { + for (id, instance) in &neighbor.structure_starts { + if let Some(s) = STRUCTURES.get(id) + && s.step.ordinal() != step + { + continue; + } + + match instance { + StructureInstance::Start(pos) => { + let start_x = chunk_pos::start_block_x(center_x); + let start_z = chunk_pos::start_block_z(center_z); + let end_x = start_x + 15; + let end_z = start_z + 15; + + if pos + .get_bounding_box() + .intersects_raw_xz(start_x, start_z, end_x, end_z) + { + let collector_arc = pos.collector.clone(); + if !tasks.iter().any(|t| Arc::ptr_eq(t, &collector_arc)) { + tasks.push(collector_arc); + } + } + } + StructureInstance::Reference(origin_block_pos) => { + let origin_chunk_x = origin_block_pos.0.x >> 4; + let origin_chunk_z = origin_block_pos.0.z >> 4; + if let Some(origin_neighbor) = + cache.try_get_proto_chunk(origin_chunk_x, origin_chunk_z) + && let Some(StructureInstance::Start(pos)) = + origin_neighbor.structure_starts.get(id) + { + let start_x = chunk_pos::start_block_x(center_x); + let start_z = chunk_pos::start_block_z(center_z); + let end_x = start_x + 15; + let end_z = start_z + 15; + + if pos + .get_bounding_box() + .intersects_raw_xz(start_x, start_z, end_x, end_z) + { + let collector_arc = pos.collector.clone(); + if !tasks.iter().any(|t| Arc::ptr_eq(t, &collector_arc)) + { + tasks.push(collector_arc); + } + } + } + } + } + } + } + } + } } let decorator_seed = get_decorator_seed(population_seed, 0, step as u64); diff --git a/pumpkin-world/src/generation/structure/placement.rs b/pumpkin-world/src/generation/structure/placement.rs index 5f0e37f4a..793da5d3b 100644 --- a/pumpkin-world/src/generation/structure/placement.rs +++ b/pumpkin-world/src/generation/structure/placement.rs @@ -170,7 +170,7 @@ impl ConcentricRingsStructurePlacement { // ^ (salt as i64); // (seed.abs() % 400) == 0 - rand::random_bool(1.0 / 1000.0) + rand::random_bool(1.0 / 100.0) } } diff --git a/pumpkin-world/src/generation/structure/structures/mod.rs b/pumpkin-world/src/generation/structure/structures/mod.rs index ac8625997..d87406f49 100644 --- a/pumpkin-world/src/generation/structure/structures/mod.rs +++ b/pumpkin-world/src/generation/structure/structures/mod.rs @@ -9,6 +9,8 @@ use pumpkin_util::{ random::{RandomGenerator, RandomImpl, get_carver_seed, xoroshiro128::Xoroshiro}, }; +use crate::generation::structure::structures::stronghold::PieceWeight; +use crate::generation::structure::structures::stronghold::StrongholdPieceType; use crate::{ ProtoChunk, generation::{ @@ -46,10 +48,16 @@ pub trait StructurePieceBase: Send + Sync { /// Places the blocks for this piece into the chunk. fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, seed: i64); + #[expect(clippy::too_many_arguments)] fn fill_openings( &self, _start: &StructurePiece, _random: &mut RandomGenerator, + // TODO: this is only for Stronghold and should not be here + _weights: &mut Vec, + _last_piece_type: &mut Option, + _has_portal_room: &mut bool, + _collector: &mut StructurePiecesCollector, _pieces_to_process: &mut Vec>, ) { @@ -361,9 +369,9 @@ impl StructurePiece { pub fn get_random_horizontal_direction(random: &mut impl RandomImpl) -> BlockDirection { match random.next_bounded_i32(4) { 0 => BlockDirection::North, - 1 => BlockDirection::South, - 2 => BlockDirection::West, - _ => BlockDirection::East, + 1 => BlockDirection::East, + 2 => BlockDirection::South, + _ => BlockDirection::West, } } } @@ -409,21 +417,17 @@ impl StructurePiecesCollector { self.cached_box = None; } - #[expect(clippy::borrowed_box)] #[must_use] - pub fn get_intersecting( - &self, - box_to_check: &BlockBox, - ) -> Option<&Box> { + pub fn get_intersecting(&self, box_to_check: &BlockBox) -> Option<&dyn StructurePieceBase> { self.pieces .iter() - .find(|&piece| { + .find(|piece| { piece .get_structure_piece() .bounding_box .intersects(box_to_check) }) - .map(|v| v as _) + .map(|v| v.as_ref() as &dyn StructurePieceBase) } /// Iterates over all pieces and generates them if they intersect the current chunk. diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/chest_corridor.rs b/pumpkin-world/src/generation/structure/structures/stronghold/chest_corridor.rs index 8cd98c58a..3be76510f 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/chest_corridor.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/chest_corridor.rs @@ -11,7 +11,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -68,11 +71,24 @@ impl StructurePieceBase for ChestCorridorPiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { - self.piece - .fill_forward_opening(start, collector, random, 1, 1, pieces_to_process, None); + self.piece.fill_forward_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 1, + pieces_to_process, + None, + ); } fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { @@ -133,7 +149,6 @@ impl StructurePieceBase for ChestCorridorPiece { // // Note: In a full implementation, you would use a helper to set the LootTable NBT here // inner.add_block(chunk, &chest_state, 3, 2, 3, &box_limit); - // // If Pumpkin has a loot provider, you'd hook into it here: // // chunk.set_loot_table(chest_pos, "minecraft:chests/stronghold_corridor"); // } // } diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/corridor.rs b/pumpkin-world/src/generation/structure/structures/stronghold/corridor.rs index 241f072b4..4af8a4d80 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/corridor.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/corridor.rs @@ -11,7 +11,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -70,20 +73,49 @@ impl StructurePieceBase for CorridorPiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { - self.piece - .fill_forward_opening(start, collector, random, 1, 1, pieces_to_process, None); + self.piece.fill_forward_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 1, + pieces_to_process, + None, + ); if self.left_exit { - self.piece - .fill_nw_opening(start, collector, random, 1, 2, pieces_to_process); + self.piece.fill_nw_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 2, + pieces_to_process, + ); } if self.right_exit { - self.piece - .fill_se_opening(start, collector, random, 1, 2, pieces_to_process); + self.piece.fill_se_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 2, + pieces_to_process, + ); } } @@ -174,7 +206,6 @@ impl SmallCorridorPiece { // so it perfectly abuts the intersecting piece. if intersecting.bounding_box().min.y == block_box.min.y { for j in (1..=2).rev() { - // Corresponds to for (int j = 2; j >= 1; --j) block_box = BlockBox::rotated(x, y, z, -1, -1, 0, 5, 5, j, orientation); if !intersecting.bounding_box().intersects(&block_box) { diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/five_way_crossing.rs b/pumpkin-world/src/generation/structure/structures/stronghold/five_way_crossing.rs index 136cfd5e5..7ab7b1bd9 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/five_way_crossing.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/five_way_crossing.rs @@ -1,4 +1,7 @@ -use pumpkin_data::Block; +use pumpkin_data::{ + Block, BlockState, + block_properties::{BlockProperties, ResinBrickSlabLikeProperties, SlabType}, +}; use pumpkin_util::{ BlockDirection, math::block_box::BlockBox, @@ -11,7 +14,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -74,6 +80,10 @@ impl StructurePieceBase for FiveWayCrossingPiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { @@ -86,25 +96,66 @@ impl StructurePieceBase for FiveWayCrossingPiece { j = 8 - j; } - self.piece - .fill_forward_opening(start, collector, random, 5, 1, pieces_to_process, None); + self.piece.fill_forward_opening( + start, + collector, + random, + weights, + last_piece_type, + 5, + 1, + pieces_to_process, + None, + ); if self.lower_left_exists { - self.piece - .fill_nw_opening(start, collector, random, i, 1, pieces_to_process); + self.piece.fill_nw_opening( + start, + collector, + random, + weights, + last_piece_type, + i, + 1, + pieces_to_process, + ); } if self.upper_left_exists { - self.piece - .fill_nw_opening(start, collector, random, j, 7, pieces_to_process); + self.piece.fill_nw_opening( + start, + collector, + random, + weights, + last_piece_type, + j, + 7, + pieces_to_process, + ); } if self.lower_right_exists { - self.piece - .fill_se_opening(start, collector, random, i, 1, pieces_to_process); + self.piece.fill_se_opening( + start, + collector, + random, + weights, + last_piece_type, + i, + 1, + pieces_to_process, + ); } if self.upper_right_exists { - self.piece - .fill_se_opening(start, collector, random, j, 7, pieces_to_process); + self.piece.fill_se_opening( + start, + collector, + random, + weights, + last_piece_type, + j, + 7, + pieces_to_process, + ); } } @@ -236,8 +287,22 @@ impl StructurePieceBase for FiveWayCrossingPiece { inner.fill_with_outline(chunk, &box_limit, false, 4, 5, 7, 4, 5, 9, slab, slab); inner.fill_with_outline(chunk, &box_limit, false, 8, 5, 7, 8, 5, 9, slab, slab); - // let double_slab = Block::SMOOTH_STONE_SLAB.default_state.with("type", "double"); - // inner.fill_with_outline(chunk, &box_limit, false, 5, 5, 7, 7, 5, 9, &double_slab, &double_slab); + let mut props = ResinBrickSlabLikeProperties::default(&Block::SMOOTH_STONE_SLAB); + props.r#type = SlabType::Double; + let double_slab = BlockState::from_id(props.to_state_id(&Block::SMOOTH_STONE_SLAB)); + inner.fill_with_outline( + chunk, + &box_limit, + false, + 5, + 5, + 7, + 7, + 5, + 9, + double_slab, + double_slab, + ); // let torch = Block::WALL_TORCH.default_state.with("facing", "south"); // inner.add_block(chunk, &torch, 6, 5, 6, &box_limit); diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/left_turn.rs b/pumpkin-world/src/generation/structure/structures/stronghold/left_turn.rs index 2d6abc009..a4ba3db44 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/left_turn.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/left_turn.rs @@ -11,7 +11,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -64,17 +67,37 @@ impl StructurePieceBase for LeftTurnPiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { let facing = self.piece.piece.facing.unwrap_or(BlockDirection::North); if facing == BlockDirection::North || facing == BlockDirection::East { - self.piece - .fill_nw_opening(start, collector, random, 1, 1, pieces_to_process); + self.piece.fill_nw_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 1, + pieces_to_process, + ); } else { - self.piece - .fill_se_opening(start, collector, random, 1, 1, pieces_to_process); + self.piece.fill_se_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 1, + pieces_to_process, + ); } } diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/library.rs b/pumpkin-world/src/generation/structure/structures/stronghold/library.rs index fbfd63ce3..b7c61dc26 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/library.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/library.rs @@ -1,4 +1,7 @@ -use pumpkin_data::Block; +use pumpkin_data::{ + Block, BlockState, + block_properties::{BlockProperties, HorizontalFacing, LadderLikeProperties}, +}; use pumpkin_util::{ BlockDirection, math::block_box::BlockBox, @@ -194,10 +197,12 @@ impl StructurePieceBase for LibraryPiece { // } // // Ladder - // let ladder = Block::LADDER.default_state.with("facing", "south"); - // for y in 1..=7 { - // inner.add_block(chunk, &ladder, 10, y, 13, &box_limit); - // } + let mut props = LadderLikeProperties::default(&Block::LADDER); + props.facing = HorizontalFacing::South; + let ladder = BlockState::from_id(props.to_state_id(&Block::LADDER)); + for y in 1..=7 { + inner.add_block(chunk, ladder, 10, y, 13, &box_limit); + } // // Chandelier (The central structure) // let f_e = Block::OAK_FENCE.default_state.with("east", "true"); diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/mod.rs b/pumpkin-world/src/generation/structure/structures/stronghold/mod.rs index f8d87976f..f3332bf93 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/mod.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/mod.rs @@ -1,12 +1,17 @@ use std::sync::{Arc, Mutex}; -use pumpkin_data::{Block, BlockState}; +use pumpkin_data::{ + Block, BlockState, + block_properties::{ + BlockProperties, DoubleBlockHalf, HorizontalFacing, LeverLikeProperties, + OakDoorLikeProperties, OakFenceLikeProperties, + }, +}; use pumpkin_util::{ BlockDirection, math::{block_box::BlockBox, position::BlockPos}, random::{RandomGenerator, RandomImpl}, }; -// Removed unused rand imports to avoid confusion with Pumpkin's random use serde::Deserialize; use crate::{ @@ -36,7 +41,6 @@ use crate::{ }, }; -// Assuming these modules exist as per your import pub mod chest_corridor; pub mod corridor; pub mod five_way_crossing; @@ -49,6 +53,7 @@ pub mod spiral_staircase; pub mod square_room; pub mod stairs; +/// Great reference: https://minecraft.wiki/w/Stronghold #[derive(Deserialize)] pub struct StrongholdGenerator; @@ -57,12 +62,14 @@ impl StructureGenerator for StrongholdGenerator { &self, context: StructureGeneratorContext, ) -> Option { - let mut retry_count: i32 = 0; let mut collector = StructurePiecesCollector::default(); let mut random = context.random; loop { collector.clear(); + let mut weights = get_initial_weights(); + let mut last_piece_type: Option = None; + let mut has_portal_room = false; // let attempt_seed = get_carver_seed( // &mut context.random, @@ -86,6 +93,9 @@ impl StructureGenerator for StrongholdGenerator { start_piece.fill_openings( &start_piece.piece.piece, &mut random, + &mut weights, + &mut last_piece_type, + &mut has_portal_room, &mut collector, &mut pieces_to_process, ); @@ -97,6 +107,9 @@ impl StructureGenerator for StrongholdGenerator { piece.fill_openings( &start_piece.piece.piece, &mut random, + &mut weights, + &mut last_piece_type, + &mut has_portal_room, &mut collector, &mut pieces_to_process, ); @@ -105,18 +118,9 @@ impl StructureGenerator for StrongholdGenerator { // Shift height collector.shift_into(context.sea_level, context.min_y, &mut random, 10); - let has_portal_room = collector.pieces.iter().any(|p| { - p.get_structure_piece().r#type == StructurePieceType::StrongholdPortalRoom - }); - if !collector.is_empty() && has_portal_room { break; } - - retry_count += 1; - if retry_count > 1000 { - return None; - } } Some(StructurePosition { @@ -205,91 +209,48 @@ impl StrongholdPieceType { pub struct PieceWeight { pub piece_type: StrongholdPieceType, pub weight: i32, - pub limit: i32, + pub limit: u32, + pub generated_count: u32, // Added to track per-generation counts } impl PieceWeight { - const fn new(piece_type: StrongholdPieceType, weight: i32, limit: i32) -> Self { + const fn new(piece_type: StrongholdPieceType, weight: i32, limit: u32) -> Self { Self { piece_type, weight, limit, + generated_count: 0, } } - const fn can_generate(&self, _chain_length: u32) -> bool { - self.limit == 0 //|| self.generated_count < self.limit + fn can_generate_chained(&self, chain_length: u32) -> bool { + match self.piece_type { + StrongholdPieceType::Library => self.can_generate() && chain_length > 4, + StrongholdPieceType::PortalRoom => self.can_generate() && chain_length > 5, + _ => self.can_generate(), + } + } + + fn can_generate(&self) -> bool { + self.limit == 0 || self.generated_count < self.limit } } -const POSSIBLE_PIECES: &[PieceWeight] = &[ - PieceWeight { - piece_type: StrongholdPieceType::Corridor, - weight: 40, - limit: 0, - }, - PieceWeight { - piece_type: StrongholdPieceType::PrisonHall, - weight: 5, - limit: 5, - }, - PieceWeight { - piece_type: StrongholdPieceType::LeftTurn, - weight: 20, - limit: 0, - }, - PieceWeight { - piece_type: StrongholdPieceType::RightTurn, - weight: 20, - limit: 0, - }, - PieceWeight { - piece_type: StrongholdPieceType::SquareRoom, - weight: 10, - limit: 6, - }, - PieceWeight { - piece_type: StrongholdPieceType::Stairs, - weight: 5, - limit: 5, - }, - PieceWeight { - piece_type: StrongholdPieceType::SpiralStaircase, - weight: 5, - limit: 5, - }, - PieceWeight { - piece_type: StrongholdPieceType::FiveWayCrossing, - weight: 5, - limit: 4, - }, - PieceWeight { - piece_type: StrongholdPieceType::ChestCorridor, - weight: 5, - limit: 4, - }, - PieceWeight { - piece_type: StrongholdPieceType::Library, - weight: 10, - limit: 2, - }, - PieceWeight { - piece_type: StrongholdPieceType::PortalRoom, - weight: 20, - limit: 1, - }, -]; - -// Pre-calculate total weight (const loop because iter().sum() isn't always const yet) -const TOTAL_WEIGHT: i32 = { - let mut sum = 0; - let mut i = 0; - while i < POSSIBLE_PIECES.len() { - sum += POSSIBLE_PIECES[i].weight; - i += 1; - } - sum -}; +fn get_initial_weights() -> Vec { + vec![ + PieceWeight::new(StrongholdPieceType::Corridor, 40, 0), + PieceWeight::new(StrongholdPieceType::PrisonHall, 5, 5), + PieceWeight::new(StrongholdPieceType::LeftTurn, 20, 0), + PieceWeight::new(StrongholdPieceType::RightTurn, 20, 0), + PieceWeight::new(StrongholdPieceType::SquareRoom, 10, 6), + PieceWeight::new(StrongholdPieceType::Stairs, 5, 5), + PieceWeight::new(StrongholdPieceType::SpiralStaircase, 5, 5), + PieceWeight::new(StrongholdPieceType::FiveWayCrossing, 5, 4), + PieceWeight::new(StrongholdPieceType::ChestCorridor, 5, 4), + PieceWeight::new(StrongholdPieceType::Library, 10, 2), + PieceWeight::new(StrongholdPieceType::PortalRoom, 20, 1), + ] +} #[derive(Clone)] pub struct StrongholdPiece { @@ -337,8 +298,10 @@ impl StrongholdPiece { } EntranceType::WoodDoor => { let stone = Block::STONE_BRICKS.default_state; - let _door_lower = Block::OAK_DOOR.default_state; - //let door_upper = Block::OAK_DOOR.default_state.with("half", "upper"); + let door_lower = Block::OAK_DOOR.default_state; + let mut door_upper = OakDoorLikeProperties::default(&Block::OAK_DOOR); + door_upper.half = DoubleBlockHalf::Upper; + let door_upper = BlockState::from_id(door_upper.to_state_id(&Block::OAK_DOOR)); // Frame self.piece.add_block(chunk, stone, x, y, z, box_limit); @@ -353,37 +316,54 @@ impl StrongholdPiece { self.piece.add_block(chunk, stone, x + 2, y, z, box_limit); // Door - // self.piece.add_block(chunk, &door_lower, x + 1, y, z, box_limit); - // self.piece.add_block(chunk, &door_upper, x + 1, y + 1, z, box_limit); + self.piece + .add_block(chunk, door_lower, x + 1, y, z, box_limit); + self.piece + .add_block(chunk, door_upper, x + 1, y + 1, z, box_limit); } EntranceType::Grates => { let air = Block::CAVE_AIR.default_state; - // let bar_w = Block::IRON_BARS.default_state.with("west", "true"); - // let bar_e = Block::IRON_BARS.default_state.with("east", "true"); - // let bar_ew = Block::IRON_BARS.default_state.with("east", "true").with("west", "true"); + let mut props = OakFenceLikeProperties::default(&Block::IRON_BARS); + + // North-South facing bars + props.west = true; + let bar_w = BlockState::from_id(props.to_state_id(&Block::IRON_BARS)); + props.west = false; + props.east = true; + let bar_e = BlockState::from_id(props.to_state_id(&Block::IRON_BARS)); + props.west = true; + let bar_ew = BlockState::from_id(props.to_state_id(&Block::IRON_BARS)); self.piece.add_block(chunk, air, x + 1, y, z, box_limit); self.piece.add_block(chunk, air, x + 1, y + 1, z, box_limit); // Left side (West connection) - // self.piece.add_block(chunk, &bar_w, x, y, z, box_limit); - // self.piece.add_block(chunk, &bar_w, x, y + 1, z, box_limit); + self.piece.add_block(chunk, bar_w, x, y, z, box_limit); + self.piece.add_block(chunk, bar_w, x, y + 1, z, box_limit); // // Top beam (Connected East-West) - // self.piece.add_block(chunk, &bar_ew, x, y + 2, z, box_limit); - // self.piece.add_block(chunk, &bar_ew, x + 1, y + 2, z, box_limit); - // self.piece.add_block(chunk, &bar_ew, x + 2, y + 2, z, box_limit); + self.piece.add_block(chunk, bar_ew, x, y + 2, z, box_limit); + self.piece + .add_block(chunk, bar_ew, x + 1, y + 2, z, box_limit); + self.piece + .add_block(chunk, bar_ew, x + 2, y + 2, z, box_limit); // // Right side (East connection) - // self.piece.add_block(chunk, &bar_e, x + 2, y + 1, z, box_limit); - // self.piece.add_block(chunk, &bar_e, x + 2, y, z, box_limit); + self.piece + .add_block(chunk, bar_e, x + 2, y + 1, z, box_limit); + self.piece.add_block(chunk, bar_e, x + 2, y, z, box_limit); } EntranceType::IronDoor => { let stone = Block::STONE_BRICKS.default_state; - // let door_lower = Block::IRON_DOOR.default_state; - // let door_upper = Block::IRON_DOOR.default_state.with("half", "upper"); - // let button_n = Block::STONE_BUTTON.default_state.with("facing", "north"); - // let button_s = Block::STONE_BUTTON.default_state.with("facing", "south"); + let door_lower = Block::IRON_DOOR.default_state; + let mut door_upper = OakDoorLikeProperties::default(&Block::IRON_DOOR); + door_upper.half = DoubleBlockHalf::Upper; + let door_upper = BlockState::from_id(door_upper.to_state_id(&Block::IRON_DOOR)); + let mut props = LeverLikeProperties::default(&Block::STONE_BUTTON); + props.facing = HorizontalFacing::North; + let button_n = BlockState::from_id(props.to_state_id(&Block::STONE_BUTTON)); + props.facing = HorizontalFacing::South; + let button_s = BlockState::from_id(props.to_state_id(&Block::STONE_BUTTON)); // Frame self.piece.fill_with_outline( @@ -416,12 +396,16 @@ impl StrongholdPiece { ); // Door - // self.piece.add_block(chunk, &door_lower, x + 1, y, z, box_limit); - // self.piece.add_block(chunk, &door_upper, x + 1, y + 1, z, box_limit); + self.piece + .add_block(chunk, door_lower, x + 1, y, z, box_limit); + self.piece + .add_block(chunk, door_upper, x + 1, y + 1, z, box_limit); - // // Buttons - // self.piece.add_block(chunk, &button_n, x + 2, y + 1, z + 1, box_limit); - // self.piece.add_block(chunk, &button_s, x + 2, y + 1, z - 1, box_limit); + // Buttons + self.piece + .add_block(chunk, button_n, x + 2, y + 1, z + 1, box_limit); + self.piece + .add_block(chunk, button_s, x + 2, y + 1, z - 1, box_limit); } } } @@ -432,6 +416,8 @@ impl StrongholdPiece { start: &StructurePiece, collector: &mut StructurePiecesCollector, random: &mut impl RandomImpl, + weights: &mut Vec, + last_piece_type: &mut Option, left_right_offset: i32, height_offset: i32, pieces_to_process: &mut Vec>, @@ -467,6 +453,8 @@ impl StrongholdPiece { start, collector, random, + weights, + last_piece_type, nx, ny, nz, @@ -474,17 +462,19 @@ impl StrongholdPiece { self.piece.chain_length, piece, ) { - // IMPORTANT: The generator already adds it to the collector if successful. - // We just need to add it to the processing queue. pieces_to_process.push(next); } } } + + #[expect(clippy::too_many_arguments)] pub fn fill_nw_opening( &self, start: &StructurePiece, collector: &mut StructurePiecesCollector, random: &mut impl RandomImpl, + weights: &mut Vec, + last_piece_type: &mut Option, height_offset: i32, left_right_offset: i32, pieces_to_process: &mut Vec>, @@ -513,23 +503,28 @@ impl StrongholdPiece { start, collector, random, + weights, + last_piece_type, nx, ny, nz, next_facing, self.piece.chain_length, - None, // Dynamic piece selection + None, ) { pieces_to_process.push(next); } } } + #[expect(clippy::too_many_arguments)] pub fn fill_se_opening( &self, start: &StructurePiece, collector: &mut StructurePiecesCollector, random: &mut impl RandomImpl, + weights: &mut Vec, + last_piece_type: &mut Option, height_offset: i32, left_right_offset: i32, pieces_to_process: &mut Vec>, @@ -557,6 +552,8 @@ impl StrongholdPiece { start, collector, random, + weights, + last_piece_type, nx, ny, nz, @@ -574,6 +571,8 @@ impl StrongholdPiece { start: &StructurePiece, collector: &mut StructurePiecesCollector, random: &mut impl RandomImpl, + weights: &mut Vec, + last_piece_type: &mut Option, x: i32, y: i32, z: i32, @@ -601,10 +600,20 @@ impl StrongholdPiece { y, z, orientation, - chain_length, + chain_length + 1, ) } else { - Self::pick_piece(collector, random, x, y, z, orientation, chain_length) + Self::pick_piece( + collector, + random, + weights, + last_piece_type, + x, + y, + z, + orientation, + chain_length + 1, + ) }; if let Some(p) = next_piece { @@ -615,59 +624,81 @@ impl StrongholdPiece { None } + fn check_remaining_pieces(weights: &Vec, total_weight: &mut i32) -> bool { + let mut can_generate = false; + *total_weight = 0; + + for piece_data in weights { + // If at least one piece with a limit hasn't reached it, we can keep going + if piece_data.limit > 0 && piece_data.generated_count < piece_data.limit { + can_generate = true; + } + *total_weight += piece_data.weight; + } + + can_generate + } + + #[expect(clippy::too_many_arguments)] fn pick_piece( collector: &mut StructurePiecesCollector, random: &mut impl RandomImpl, + weights: &mut Vec, + last_piece_type: &mut Option, x: i32, y: i32, z: i32, orientation: BlockDirection, chain_length: u32, ) -> Option> { + let mut total_weight = 0; + + if !Self::check_remaining_pieces(weights, &mut total_weight) { + return None; + } + let mut attempt = 0; while attempt < 5 { attempt += 1; - let mut j = random.next_bounded_i32(TOTAL_WEIGHT); + let mut j = random.next_bounded_i32(total_weight); - for piece_data in POSSIBLE_PIECES { - j -= piece_data.weight; - if j >= 0 { - continue; - } - - if piece_data.limit > 0 { - let type_to_check = piece_data.piece_type.as_structure_type(); - - let current_count = collector - .pieces - .iter() - .filter(|p| p.get_structure_piece().r#type == type_to_check) - .count(); - - if current_count >= piece_data.limit as usize { - continue; + for i in 0..weights.len() { + j -= weights[i].weight; + if j < 0 { + // Check if this piece can generate at this chain_length + if !weights[i].can_generate_chained(chain_length) + || Some(weights[i].piece_type) == *last_piece_type + { + break; } - } - let piece = Self::create_piece( - piece_data.piece_type, - collector, - random, - x, - y, - z, - orientation, - chain_length, - ); + if let Some(p) = Self::create_piece( + weights[i].piece_type, + collector, + random, + x, + y, + z, + orientation, + chain_length, + ) { + weights[i].generated_count += 1; + *last_piece_type = Some(weights[i].piece_type); - if let Some(p) = piece { - return Some(p); + if !weights[i].can_generate() { + weights.remove(i); + } + + return Some(p); + } } } } // Fallback: Small Corridor - if let Some(bbox) = SmallCorridorPiece::create_box(collector, x, y, z, &orientation) { + if let Some(bbox) = SmallCorridorPiece::create_box(collector, x, y, z, &orientation) + && bbox.min.y > 1 + { return Some(Box::new(SmallCorridorPiece::new( chain_length, bbox, diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/portal_room.rs b/pumpkin-world/src/generation/structure/structures/stronghold/portal_room.rs index da46a34ee..c978740ef 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/portal_room.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/portal_room.rs @@ -1,4 +1,10 @@ -use pumpkin_data::Block; +use pumpkin_data::{ + Block, BlockState, + block_properties::{ + BlockProperties, EndPortalFrameLikeProperties, HorizontalFacing, OakFenceLikeProperties, + OakStairsLikeProperties, + }, +}; use pumpkin_util::{ BlockDirection, math::block_box::BlockBox, @@ -11,7 +17,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -63,13 +72,28 @@ impl StructurePieceBase for PortalRoomPiece { &mut self.piece.piece } + fn fill_openings( + &self, + _start: &StructurePiece, + _random: &mut RandomGenerator, + // TODO: this is only for Stronghold and should not be here + _weights: &mut Vec, + _last_piece_type: &mut Option, + has_portal_room: &mut bool, + + _collector: &mut StructurePiecesCollector, + _pieces_to_process: &mut Vec>, + ) { + *has_portal_room = true + } + fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { let randomizer = StoneBrickRandomizer; let box_limit = self.piece.piece.bounding_box; let p = &self.piece; let inner = &p.piece; - // 1. Main Shell (11x8x16) + // 1. Main Shell inner.fill_outline_random( 0, 0, @@ -84,7 +108,7 @@ impl StructurePieceBase for PortalRoomPiece { &box_limit, ); - // 2. Entrance (Grates) + // 2. Entrance p.generate_entrance(chunk, &box_limit, EntranceType::Grates, 4, 1, 0); // 3. Upper Walkways @@ -141,8 +165,8 @@ impl StructurePieceBase for PortalRoomPiece { &box_limit, ); - // 4. Lava Pools (Side pools and central pool) - let lava = Block::LAVA.default_state; + // 4. Lava Pools + // Side Stone Brick borders inner.fill_outline_random( 1, 1, @@ -169,9 +193,13 @@ impl StructurePieceBase for PortalRoomPiece { random, &box_limit, ); + + // Side Lava + let lava = Block::LAVA.default_state; inner.fill_with_outline(chunk, &box_limit, false, 1, 1, 1, 1, 1, 3, lava, lava); inner.fill_with_outline(chunk, &box_limit, false, 9, 1, 1, 9, 1, 3, lava, lava); + // Center Stone Brick platform inner.fill_outline_random( 3, 1, @@ -185,60 +213,130 @@ impl StructurePieceBase for PortalRoomPiece { random, &box_limit, ); + // Center Lava inner.fill_with_outline(chunk, &box_limit, false, 4, 1, 9, 6, 1, 11, lava, lava); - // 5. Iron Bars (Directional panes) - // let bar_ns = Block::IRON_BARS.default_state.with("north", "true").with("south", "true"); - // let bar_we = Block::IRON_BARS.default_state.with("west", "true").with("east", "true"); + // 5. Iron Bars + let mut props = OakFenceLikeProperties::default(&Block::IRON_BARS); - // for j in (3..14).step_by(2) { - // inner.fill_with_outline(chunk, &box_limit, false, 0, 3, j, 0, 4, j, &bar_ns, &bar_ns); - // inner.fill_with_outline(chunk, &box_limit, false, 10, 3, j, 10, 4, j, &bar_ns, &bar_ns); - // } - // for j in (2..9).step_by(2) { - // inner.fill_with_outline(chunk, &box_limit, false, j, 3, 15, j, 4, 15, &bar_we, &bar_we); - // } + // North-South facing bars + props.north = true; + props.south = true; + let bar_ns = BlockState::from_id(props.to_state_id(&Block::IRON_BARS)); - // // 6. Stairs leading to Portal - // let stairs_n = Block::STONE_BRICK_STAIRS.default_state.with("facing", "north"); - // inner.fill_outline_random(4, 1, 5, 6, 1, 7, &randomizer, chunk, false, random, &box_limit); - // inner.fill_outline_random(4, 2, 6, 6, 2, 7, &randomizer, chunk, false, random, &box_limit); - // inner.fill_outline_random(4, 3, 7, 6, 3, 7, &randomizer, chunk, false, random, &box_limit); + // West-East facing bars + props.north = false; + props.south = false; + props.west = true; + props.east = true; + let bar_we = BlockState::from_id(props.to_state_id(&Block::IRON_BARS)); - // for k in 4..=6 { - // inner.add_block(chunk, &stairs_n, k, 1, 4, &box_limit); - // inner.add_block(chunk, &stairs_n, k, 2, 5, &box_limit); - // inner.add_block(chunk, &stairs_n, k, 3, 6, &box_limit); - // } + // Bars along Z axis (North/South) at X=0 and X=10 + for j in (3..14).step_by(2) { + inner.fill_with_outline(chunk, &box_limit, false, 0, 3, j, 0, 4, j, bar_ns, bar_ns); + inner.fill_with_outline(chunk, &box_limit, false, 10, 3, j, 10, 4, j, bar_ns, bar_ns); + } - // 7. End Portal Frames and Eyes + // Bars along X axis (West/East) at Z=15 + for j in (2..9).step_by(2) { + inner.fill_with_outline(chunk, &box_limit, false, j, 3, 15, j, 4, 15, bar_we, bar_we); + } + + // 6. Stairs Platform (Stone Brick backing) + inner.fill_outline_random( + 4, + 1, + 5, + 6, + 1, + 7, + &randomizer, + chunk, + false, + random, + &box_limit, + ); + inner.fill_outline_random( + 4, + 2, + 6, + 6, + 2, + 7, + &randomizer, + chunk, + false, + random, + &box_limit, + ); + inner.fill_outline_random( + 4, + 3, + 7, + 6, + 3, + 7, + &randomizer, + chunk, + false, + random, + &box_limit, + ); + + // Stairs Blocks + let mut props = OakStairsLikeProperties::default(&Block::STONE_BRICK_STAIRS); + props.facing = HorizontalFacing::North; + let stairs_n = BlockState::from_id(props.to_state_id(&Block::STONE_BRICK_STAIRS)); + + for k in 4..=6 { + inner.add_block(chunk, stairs_n, k, 1, 4, &box_limit); + inner.add_block(chunk, stairs_n, k, 2, 5, &box_limit); + inner.add_block(chunk, stairs_n, k, 3, 6, &box_limit); + } + + // 7. End Portal Frames + // Pre-calculate properties for base directions + let mut props = EndPortalFrameLikeProperties::default(&Block::END_PORTAL_FRAME); + props.facing = HorizontalFacing::North; + let f_north = props; + props.facing = HorizontalFacing::South; + let f_south = props; + props.facing = HorizontalFacing::East; + let f_east = props; + props.facing = HorizontalFacing::West; + let f_west = props; + + // Randomize eyes let eyes: [bool; 12] = std::array::from_fn(|_| random.next_f32() > 0.9); - let all_eyes = eyes.iter().all(|&eye| eye); + let all_eyes = eyes.iter().all(|&e| e); - // let f_north = Block::END_PORTAL_FRAME.default_state.with("facing", "north"); - // let f_south = Block::END_PORTAL_FRAME.default_state.with("facing", "south"); - // let f_east = Block::END_PORTAL_FRAME.default_state.with("facing", "east"); - // let f_west = Block::END_PORTAL_FRAME.default_state.with("facing", "west"); + // Helper to apply eye state + let with_eye = |mut p: EndPortalFrameLikeProperties, has_eye: bool| { + p.eye = has_eye; + BlockState::from_id(p.to_state_id(&Block::END_PORTAL_FRAME)) + }; - // Helper to stringify eye boolean for state mapping - //let eye_str = |b: bool| if b { "true" } else { "false" }; + // North Row + inner.add_block(chunk, with_eye(f_north, eyes[0]), 4, 3, 8, &box_limit); + inner.add_block(chunk, with_eye(f_north, eyes[1]), 5, 3, 8, &box_limit); + inner.add_block(chunk, with_eye(f_north, eyes[2]), 6, 3, 8, &box_limit); - // inner.add_block(chunk, &f_north.with("eye", eye_str(eyes[0])), 4, 3, 8, &box_limit); - // inner.add_block(chunk, &f_north.with("eye", eye_str(eyes[1])), 5, 3, 8, &box_limit); - // inner.add_block(chunk, &f_north.with("eye", eye_str(eyes[2])), 6, 3, 8, &box_limit); + // South Row + inner.add_block(chunk, with_eye(f_south, eyes[3]), 4, 3, 12, &box_limit); + inner.add_block(chunk, with_eye(f_south, eyes[4]), 5, 3, 12, &box_limit); + inner.add_block(chunk, with_eye(f_south, eyes[5]), 6, 3, 12, &box_limit); - // inner.add_block(chunk, &f_south.with("eye", eye_str(eyes[3])), 4, 3, 12, &box_limit); - // inner.add_block(chunk, &f_south.with("eye", eye_str(eyes[4])), 5, 3, 12, &box_limit); - // inner.add_block(chunk, &f_south.with("eye", eye_str(eyes[5])), 6, 3, 12, &box_limit); + // East Row + inner.add_block(chunk, with_eye(f_east, eyes[6]), 3, 3, 9, &box_limit); + inner.add_block(chunk, with_eye(f_east, eyes[7]), 3, 3, 10, &box_limit); + inner.add_block(chunk, with_eye(f_east, eyes[8]), 3, 3, 11, &box_limit); - // inner.add_block(chunk, &f_east.with("eye", eye_str(eyes[6])), 3, 3, 9, &box_limit); - // inner.add_block(chunk, &f_east.with("eye", eye_str(eyes[7])), 3, 3, 10, &box_limit); - // inner.add_block(chunk, &f_east.with("eye", eye_str(eyes[8])), 3, 3, 11, &box_limit); - - // inner.add_block(chunk, &f_west.with("eye", eye_str(eyes[9])), 7, 3, 9, &box_limit); - // inner.add_block(chunk, &f_west.with("eye", eye_str(eyes[10])), 7, 3, 10, &box_limit); - // inner.add_block(chunk, &f_west.with("eye", eye_str(eyes[11])), 7, 3, 11, &box_limit); + // West Row + inner.add_block(chunk, with_eye(f_west, eyes[9]), 7, 3, 9, &box_limit); + inner.add_block(chunk, with_eye(f_west, eyes[10]), 7, 3, 10, &box_limit); + inner.add_block(chunk, with_eye(f_west, eyes[11]), 7, 3, 11, &box_limit); + // Fill Portal Liquid if all_eyes { let portal = Block::END_PORTAL.default_state; inner.fill_with_outline(chunk, &box_limit, false, 4, 3, 9, 6, 3, 11, portal, portal); @@ -251,7 +349,8 @@ impl StructurePieceBase for PortalRoomPiece { self.spawner_placed = true; let spawner = Block::SPAWNER.default_state; inner.add_block(chunk, spawner, 5, 3, 6, &box_limit); - // In a real implementation, you'd add the TileEntityData for Silverfish here + // Note: You must check your engine's API to set the BlockEntity (Silverfish). + // Example: chunk.set_block_entity(pos, BlockEntity::Spawner(EntityType::Silverfish)); } } } diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/prison_hall.rs b/pumpkin-world/src/generation/structure/structures/stronghold/prison_hall.rs index 03e987cd9..f8ef6e067 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/prison_hall.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/prison_hall.rs @@ -1,4 +1,7 @@ -use pumpkin_data::Block; +use pumpkin_data::{ + Block, BlockState, + block_properties::{BlockProperties, OakFenceLikeProperties}, +}; use pumpkin_util::{ BlockDirection, math::block_box::BlockBox, @@ -11,7 +14,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -65,12 +71,25 @@ impl StructurePieceBase for PrisonHallPiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { // Prison hall usually only has one forward exit - self.piece - .fill_forward_opening(start, collector, random, 1, 1, pieces_to_process, None); + self.piece.fill_forward_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 1, + pieces_to_process, + None, + ); } fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { @@ -157,23 +176,33 @@ impl StructurePieceBase for PrisonHallPiece { ); // 5. Iron Bars with Directional Properties - // let bars_ns = Block::IRON_BARS.default_state.with("north", "true").with("south", "true"); - // let bars_nse = bars_ns.with("east", "true"); - // let bars_we = Block::IRON_BARS.default_state.with("west", "true").with("east", "true"); + let mut props = OakFenceLikeProperties::default(&Block::IRON_BARS); - // for i in 1..=3 { - // inner.add_block(chunk, &bars_ns, 4, i, 4, &box_limit); - // inner.add_block(chunk, &bars_nse, 4, i, 5, &box_limit); - // inner.add_block(chunk, &bars_ns, 4, i, 6, &box_limit); + // North-South facing bars + props.north = true; + props.south = true; + let bar_ns = BlockState::from_id(props.to_state_id(&Block::IRON_BARS)); + props.east = true; + let bar_nse = BlockState::from_id(props.to_state_id(&Block::IRON_BARS)); + // West-East facing bars + props.north = false; + props.south = false; + props.west = true; + let bar_we = BlockState::from_id(props.to_state_id(&Block::IRON_BARS)); - // inner.add_block(chunk, &bars_we, 5, i, 5, &box_limit); - // inner.add_block(chunk, &bars_we, 6, i, 5, &box_limit); - // inner.add_block(chunk, &bars_we, 7, i, 5, &box_limit); - // } + for i in 1..=3 { + inner.add_block(chunk, bar_ns, 4, i, 4, &box_limit); + inner.add_block(chunk, bar_nse, 4, i, 5, &box_limit); + inner.add_block(chunk, bar_ns, 4, i, 6, &box_limit); + + inner.add_block(chunk, bar_we, 5, i, 5, &box_limit); + inner.add_block(chunk, bar_we, 6, i, 5, &box_limit); + inner.add_block(chunk, bar_we, 7, i, 5, &box_limit); + } // // Top bars above the doors - // inner.add_block(chunk, &bars_ns, 4, 3, 2, &box_limit); - // inner.add_block(chunk, &bars_ns, 4, 3, 8, &box_limit); + inner.add_block(chunk, bar_ns, 4, 3, 2, &box_limit); + inner.add_block(chunk, bar_ns, 4, 3, 8, &box_limit); // // 6. Iron Doors (2-block high structure) // let door_bottom = Block::IRON_DOOR.default_state.with("facing", "west").with("half", "lower"); diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/right_turn.rs b/pumpkin-world/src/generation/structure/structures/stronghold/right_turn.rs index c12d43c0d..de0fa78fc 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/right_turn.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/right_turn.rs @@ -11,7 +11,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -65,19 +68,37 @@ impl StructurePieceBase for RightTurnPiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { let facing = self.piece.piece.facing.unwrap_or(BlockDirection::North); - // For a Right Turn, we swap the logic from the Left Turn: - // If facing North/East, we go "South-East", otherwise "North-West" if facing == BlockDirection::North || facing == BlockDirection::East { - self.piece - .fill_se_opening(start, collector, random, 1, 1, pieces_to_process); + self.piece.fill_se_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 1, + pieces_to_process, + ); } else { - self.piece - .fill_nw_opening(start, collector, random, 1, 1, pieces_to_process); + self.piece.fill_nw_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 1, + pieces_to_process, + ); } } @@ -88,7 +109,6 @@ impl StructurePieceBase for RightTurnPiece { let inner = &p.piece; // 1. 5x5x5 Outline (Using randomizer) - // Note: Use your existing fill_outline_random for this part inner.fill_outline_random( 0, 0, diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/spiral_staircase.rs b/pumpkin-world/src/generation/structure/structures/stronghold/spiral_staircase.rs index 11e560b96..7119b6a8a 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/spiral_staircase.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/spiral_staircase.rs @@ -12,7 +12,8 @@ use crate::{ structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, stronghold::{ - EntranceType, StoneBrickRandomizer, StrongholdPiece, StrongholdPieceType, + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, }, }, }, @@ -47,11 +48,7 @@ impl SpiralStaircasePiece { pub fn new_start(random: &mut impl RandomImpl, x: i32, z: i32) -> Self { let orientation = StructurePiece::get_random_horizontal_direction(random); let bounding_box = BlockBox::create_box(x, 64, z, orientation.get_axis(), 5, 11, 5); - let mut piece = StrongholdPiece::new( - StructurePieceType::StrongholdSpiralStaircase, - 0, - bounding_box, - ); + let mut piece = StrongholdPiece::new(StructurePieceType::StrongholdStart, 0, bounding_box); piece.piece.set_facing(Some(orientation)); Self { piece, @@ -120,6 +117,10 @@ impl StructurePieceBase for SpiralStaircasePiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { @@ -127,6 +128,8 @@ impl StructurePieceBase for SpiralStaircasePiece { start, collector, random, + weights, + last_piece_type, 1, 1, pieces_to_process, diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/square_room.rs b/pumpkin-world/src/generation/structure/structures/stronghold/square_room.rs index 04c222c83..eb8d54ed8 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/square_room.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/square_room.rs @@ -1,4 +1,7 @@ -use pumpkin_data::Block; +use pumpkin_data::{ + Block, BlockState, + block_properties::{BlockProperties, HorizontalFacing, LadderLikeProperties}, +}; use pumpkin_util::{ BlockDirection, math::block_box::BlockBox, @@ -11,7 +14,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -48,6 +54,7 @@ impl SquareRoomPiece { bounding_box, ); piece.piece.set_facing(Some(orientation)); + piece.entry_door = EntranceType::get_random(random); let room_type = random.next_bounded_i32(5) as u32; @@ -68,6 +75,10 @@ impl StructurePieceBase for SquareRoomPiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { @@ -77,6 +88,8 @@ impl StructurePieceBase for SquareRoomPiece { start, collector, random, + weights, + last_piece_type, 4, // left_right_offset 1, // height_offset pieces_to_process, @@ -89,6 +102,8 @@ impl StructurePieceBase for SquareRoomPiece { start, collector, random, + weights, + last_piece_type, 1, // height_offset 4, // left_right_offset pieces_to_process, @@ -100,6 +115,8 @@ impl StructurePieceBase for SquareRoomPiece { start, collector, random, + weights, + last_piece_type, 1, // height_offset 4, // left_right_offset pieces_to_process, @@ -236,13 +253,14 @@ impl StructurePieceBase for SquareRoomPiece { } // Ladder - // let ladder = Block::LADDER.default_state.with("facing", "west"); - // inner.add_block(chunk, &ladder, 9, 1, 3, &box_limit); - // inner.add_block(chunk, &ladder, 9, 2, 3, &box_limit); - // inner.add_block(chunk, &ladder, 9, 3, 3, &box_limit); + let mut props = LadderLikeProperties::default(&Block::LADDER); + props.facing = HorizontalFacing::West; + let ladder = BlockState::from_id(props.to_state_id(&Block::LADDER)); + inner.add_block(chunk, ladder, 9, 1, 3, &box_limit); + inner.add_block(chunk, ladder, 9, 2, 3, &box_limit); + inner.add_block(chunk, ladder, 9, 3, 3, &box_limit); // Chest - // Note: Ensure you have `LootTables` defined or use a string ID // p.add_chest(chunk, &box_limit, random, 3, 4, 8, LootTables::StrongholdCrossingChest); inner.add_block(chunk, Block::CHEST.default_state, 3, 4, 8, &box_limit); } diff --git a/pumpkin-world/src/generation/structure/structures/stronghold/stairs.rs b/pumpkin-world/src/generation/structure/structures/stronghold/stairs.rs index ec6f8327d..6c05e8672 100644 --- a/pumpkin-world/src/generation/structure/structures/stronghold/stairs.rs +++ b/pumpkin-world/src/generation/structure/structures/stronghold/stairs.rs @@ -1,4 +1,7 @@ -use pumpkin_data::Block; +use pumpkin_data::{ + Block, BlockState, + block_properties::{BlockProperties, HorizontalFacing, OakStairsLikeProperties}, +}; use pumpkin_util::{ BlockDirection, math::block_box::BlockBox, @@ -11,7 +14,10 @@ use crate::{ piece::StructurePieceType, structures::{ StructurePiece, StructurePieceBase, StructurePiecesCollector, - stronghold::{EntranceType, StoneBrickRandomizer, StrongholdPiece}, + stronghold::{ + EntranceType, PieceWeight, StoneBrickRandomizer, StrongholdPiece, + StrongholdPieceType, + }, }, }, }; @@ -64,11 +70,24 @@ impl StructurePieceBase for StairsPiece { &self, start: &StructurePiece, random: &mut RandomGenerator, + weights: &mut Vec, + last_piece_type: &mut Option, + _has_portal_room: &mut bool, + collector: &mut StructurePiecesCollector, pieces_to_process: &mut Vec>, ) { - self.piece - .fill_forward_opening(start, collector, random, 1, 1, pieces_to_process, None); + self.piece.fill_forward_opening( + start, + collector, + random, + weights, + last_piece_type, + 1, + 1, + pieces_to_process, + None, + ); } fn place(&mut self, chunk: &mut ProtoChunk, random: &mut RandomGenerator, _seed: i64) { @@ -99,7 +118,9 @@ impl StructurePieceBase for StairsPiece { p.generate_entrance(chunk, &box_limit, EntranceType::Opening, 1, 1, 7); // 4. Stairs Generation - let stair_state = Block::COBBLESTONE_STAIRS.default_state; + let mut props = OakStairsLikeProperties::default(&Block::COBBLESTONE_STAIRS); + props.facing = HorizontalFacing::South; + let stair_state = BlockState::from_id(props.to_state_id(&Block::COBBLESTONE_STAIRS)); let stone_bricks = Block::STONE_BRICKS.default_state;