diff --git a/DanisRace Debug.txt b/DanisRace Debug.txt new file mode 100644 index 0000000..7b38ac6 --- /dev/null +++ b/DanisRace Debug.txt @@ -0,0 +1,13 @@ +[] Animation Staires + +[] Animation Jump + +[] Walk Speed + +[] Auto Animation Swimming + +[] Auto Control Swimming on Water + +[] Jump Bugged + +[] Slower Walk Animation \ No newline at end of file diff --git a/LICENSE b/LICENSE index aa5f782..7becb79 100644 --- a/LICENSE +++ b/LICENSE @@ -1,232 +1,7 @@ -GNU GENERAL PUBLIC LICENSE -Version 3, 29 June 2007 +Software: GNU General Public License Version 3 or any later version +Game Art: Creative Commons Attribution-ShareAlike -Copyright © 2007 Free Software Foundation, Inc. +Some art was taken from other projects that have other ( compatible ) licenses. Most of them are taken from Moria's Race movie sources. Like for example the sound files. Most of those are either CC0 or in some cases CC-BY or CC-BY-SA. -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. - - DanisRace - Copyright (C) 2024 BlenderDumbass - - 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: - - DanisRace Copyright (C) 2024 BlenderDumbass - 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 . +For full list of sound files used in this project please refer to SOUNDLICENSES file. +For Font Licenses please look into the fonts folder. diff --git a/README.md b/README.md index 465da41..c626cc3 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,5 @@ -# DanisRace +# Dani's Race -The Libre Game Dani's Race. \ No newline at end of file +A Free / Libre Open World Game. + +[Dani's Race Website](https://blenderdumbass.codeberg.page/DanisRace/) diff --git a/SOUNDLICENSES b/SOUNDLICENSES new file mode 100644 index 0000000..55b7cc3 --- /dev/null +++ b/SOUNDLICENSES @@ -0,0 +1,49 @@ +The files are renamed and or changed in some way for the game. Not the entire list of those sounds might be present in the game. ( Those are sounds used in Moria's Race movie and are being reused for the game ). + +____________________________________________________________________________________________ + + +Ambience, Night Wildlife, A.wav - CC-BY by InspectorJ (www.jshaw.co.uk) of Freesound.org https://freesound.org/people/InspectorJ/sounds/352514/ + +Leather Bow Stretch - CC-BY by EminYILDIRIM from Freesound .org https://freesound.org/people/EminYILDIRIM/sounds/536085/ + +Eurofighter Typhoon Flyby 004 – Close Proximity - CC-BY by TimoSchmied https://freesound.org/people/TimoSchmied/sounds/640498/ + +jet engine start, CRJ 900 - CC-BY by alex36917 https://freesound.org/people/alex36917/sounds/582249/ + +Jet Engine 1 - CC-BY by InSintesi https://freesound.org/people/InSintesi/sounds/346034/ + +motor_drill01 - CC-BY by Taira Komori https://freesound.org/people/Taira%20Komori/sounds/212397/ + +ELECTRIC_MOTOR - CC-BY by jacobsteel https://freesound.org/people/jacobsteel/sounds/336665/ + +Dental_chair_servo_switch - CC-BY by beerbelly38 https://freesound.org/people/beerbelly38/sounds/160432/ + +steps_sneakers - CC-BY by neohylanmay https://freesound.org/people/neohylanmay/sounds/333201/ + +Pipe_Drag_01 - CC-BY by dheming https://freesound.org/people/dheming/sounds/177782/ + +robot step short medium-heavy - CC-BY by keemocore https://freesound.org/people/keemocore/sounds/148309/ + +Swoosh_EminYILDIRIM CC-BY by EminYILDIRIM https://freesound.org/people/EminYILDIRIM/sounds/555057/ + +bunch_of_swooshes CC-BY by berglindsi https://freesound.org/people/berglindsi/sounds/402977/ + + Horn Honk CC-BY by mkoenig https://freesound.org/people/mkoenig/sounds/81173/ + +underwater_ambience CC-BY by akemov https://freesound.org/people/akemov/sounds/255597/ + +Water_Swirl_Small_3 CC-BY by InspectorJ https://freesound.org/people/InspectorJ/sounds/398723/ + +aprox-5000 CC-BY by lonemonk https://freesound.org/people/lonemonk/sounds/100840/ + +unfa's-swoosh CC-0 by unfa https://freesound.org/people/unfa/sounds/584188/ + +tire_puncture CC-BY by WaveAdventurer +https://freesound.org/people/WaveAdventurer/sounds/702718/ + +Big Explosion CC-BY by Lamoot +https://opengameart.org/content/big-explosion + +spray painting CC-BY by Jay_You +https://freesound.org/people/Jay_You/sounds/460408/ diff --git a/ScriptSnippits.txt b/ScriptSnippits.txt new file mode 100644 index 0000000..20d2854 --- /dev/null +++ b/ScriptSnippits.txt @@ -0,0 +1,106 @@ +[01_Bring_Neonspeedster_To_Racetrack] + +Dani wakes up in the room. Nobody is home. Telephone rings after 10 seconds. + +Dani: Yes? +Paps Voice: Dani, I can't believe that I'm telling this. Jack just brought +the truck over without the car. Moria has to start practicing. And there is no +car. Dani. Bring here the Neonspeedster. It should be in the garage. You have +3 minutes. Not a single scratch. You hear me! Not a single scratch. + +Message: Bring Neonspeedster to Paps onto the Racetrack. + +A directional arrow appears and it points to Neonspeedster in the garage. As +soon as the player takes the car. The arrow will point at the racetrack. There +by the entrance, stands a Blue Truck. Paps, Jack and Moria are next to it. + +If the car has less then 100% health: + Paps: I told you not a single scratch, Dani. What do you think should we + do with a broken car? + + Message: Assignment Failed. + +If the car is fully healthy: + Paps: Excellent! One, day, if you prove yourself, I'll let you drive on the + racetrack, Dani. Now get out. + + If Dani gets out: + Message: Assignment Passed. + Moria takes the car and goes onto the racetrack on it. + Game goes to next assignment. + +______________________________________________________________________ + +Dani delivered the car to the racetrack and got out of the car. Moria took the car without talking much and drove it to the racetrack. Paps was left with Dani and Jack alone. + +Paps: Jack. +Jack: Yes. +Paps: Go wait in the truck! + +Jack goes into the truck. Sitting on the passenger's seat. + +Paps: Dani. This is unbelievable. Well, okay... Jack didn't break the truck. But the fact that he came here without Neonspeedster is frankly insane to say the least. I know what we can do. +Dani: Yes? +Paps: You will drive the truck home with him as a passenger. He is supposed to be a professional race driver, but your 11 yr old sister already drives better than him! And now you, will drive him instead of him driving you. Well this is something. That will teach him a lesson. + +Dani laughs a bit. + +Paps: Dani. Go get the truck back home. Keep Jack inside. +Dani: Okay. + +Message: Bring the truck back home. + +As soon as you sit into the truck and start going. + +Jack: Let me guess... He is trying to punish me by making you drive the truck? Hm... He doesn't know me. It's not punishing. It's amazing! Also he probably told you that I didn't bring a car here. Well he is lying. I brought a car. It's in the truck right now. It's just not a Neonspeedster. And Moria apparently only likes Neonspeedsters. He is not punishing me for that, you know. He is punishing me for crashing on a tower spin. On a tower spin of all places! Who will not crash there? Even Moria barely made it through. Hell Tali Shtern crashed that day on the Tower Spin. And he is still punishing me for this. + +As the truck is getting close to the house. + +Jack: Wait, wait. Let's not go home. Wanna race? +Dani: Yes. +Jack: Let's go this way. There is a race coming up and we have a spare race car with us. Hell I'll let you race. +Dani: Okay! + +Message: Drive the truck to the race. + +The previous assignment is aborted and now they are driving to a race beside a huge building. There are a few cars already waiting and ready to race. + +As soon as you get there: + +Jack: Press here to unload the car. +Message: Press D to unload the car. + +This is needed for later, when people could change their keybindings. + +As soon as Dani is in the car, a circle appears into which you can drive. + +Message: Press R to start the race. + +The race starts. The race is very messy and with a lot of off-road-ing and breaking through stuff. A part of it goes through the place where Dani lives. + +_____________________________________ + +Jack's Reactions to Dani's Driving. + +Hitting cars: + Dani, your will break the car! + Dani, the car will break, dani! + We are, wrecking the car! + Paps will be pissed! + Dani, what are you doing man! + +"Oh God!": + Dani, can you please concentrate, please! + You almost killed us, dani! + Ooooaaahhh! + +Speeding up on a race: + Triple caution!!! + Dani! Double caution!!! Triple caution!!! + Concentrate, Dani, Please! + Concentrate! + You are going too fast! + We will miss the turn! + Slow down! Listen to me! Dani! + + diff --git a/Scripts/Character_Controll.py b/Scripts/Character_Controll.py new file mode 100644 index 0000000..5f924bc --- /dev/null +++ b/Scripts/Character_Controll.py @@ -0,0 +1,1083 @@ +# This will controll characters. + +import os +import bge +from Scripts import Opt +import math +import json +import time +import mathutils +import random + + +from Scripts import Reuse +from Scripts.Common import * +from Scripts import Vehicle +from Scripts import Multiplayer_Shared + +controls = { + + "forward" :keycodes["W"], + "backward":keycodes["S"], + "left" :keycodes["A"], + "right" :keycodes["D"], + "jump" :keycodes["LCtrl"], + "getcar" :keycodes["E"], + "swimdown":keycodes["LShift"], +} + +if "chr_controls" not in bge.logic.globalDict: + bge.logic.globalDict["chr_controls"] = controls +controls = bge.logic.globalDict["chr_controls"] + +# Loading animation data. +def LoadAnimations(): + + # Loading Json animation data files. + + folder = bge.logic.expandPath("//animdata/chr/") + + if "character-anim-data" not in bge.logic.globalDict: + animations = {} + + for f in os.listdir(folder): + if f.endswith(".json"): + print("Reading", f, "...") + with open(folder+f) as fl: + data = json.load(fl) + + name = f.replace(".json", "") + + animations[name] = data + bge.logic.globalDict["character-anim-data"] = animations + + return bge.logic.globalDict["character-anim-data"] + +animations = LoadAnimations() + +def DaniUpdate(): + + # This function is specifically meant for Dani. + # Since he is the main character. But if you + # want to modify the game, so somebody else is + # the main character. Use this function on that + # character. + + cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + dani = cont.owner + + keys = bge.logic.globalDict.get("keys", []) + + + + + if not dani["driving"]: + + # Jump animation logic + onground = OnGround(dani) + if dani.get("not-onground", 0) > 20 and onground and "Water" not in str(onground): + dani["landing"] = 13 + + if onground: + dani["not-onground"] = 0 + else: + try: dani["not-onground"] += 1 + except: pass + + if not onground or dani.get("grab"): + GrabEdge(dani) + + dani["onground"] = onground + + if dani.get("landing"): + ApplyAnimation(dani, "Land") + WalkForward(dani) + + device = bge.logic.globalDict["SoundDevice"] + sound = bge.logic.globalDict["sounds"]["dani_land"] + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + StopWalkSound(dani) + + dani["landing"] -= 1 + elif not onground and not dani.get("underwater"): + if not dani.get("grab"): + ApplyAnimation(dani, "Fly") + else: + ApplyAnimation(dani, "Hang") + StopWalkSound(dani) + + + # Moving back and forth + if controls["forward"] in keys: + WalkForward(dani) + elif controls["backward"] in keys: + WalkBackward(dani) + + # Moving left to right + if controls["left"] in keys: + WalkLeft(dani) + elif controls["right"] in keys: + WalkRight(dani) + + # Jumping + if controls["jump"] in keys: + Jump(dani) + else: dani["jumping"] = False + + # Swiming down + if controls["swimdown"] in keys: + GoDown(dani) + + if not dani.get("swimming_down"): + WaterBoyancy(dani) + + + # Standing still animation + if not keys and not dani["walkingtowardcar"]: + ApplyAnimation(dani, "Stand") + StopWalkSound(dani) + #AutoStand(dani) + + # If getting car + if controls["getcar"] in keys: + getIntoCar(dani) + + elif dani["walkingtowardcar"]: + walkTowardCar(dani) + + if any(controls[x] in keys for x in ["forward", "backward", "left", "right"]): + getOutCar(dani) + + else: + + ApplyAnimation(dani, "Drive") + + # If getting out of car + if controls["getcar"] in keys: + getOutCar(dani) + + + + + # Executing camera changes + ExecuteChangeCameraTarget() + CameraRuleOfThirds(dani) + + # Reverting swimming + dani["swimming_down"] = False + +def OnGround(character): + + # Returns whether the car + # in on the ground or not. + + # Getting the relative position of the ground + down = Vehicle.RelativePoint(character, [0,0,-0.6]) + center = Vehicle.RelativePoint(character, [0,0, 0.6]) + + # See if there is grown under the car + ray = character.rayCast(down, center, poly=True) + + # If nothing is sensed, we return None + if not ray[1]: + return None + + # Else we return the material of the object underneith + # Useful to see on what kind of ground the car is. + else: + try: return ray[3].material + # If there is no material data it will game the name + # of the object + except: return ray[0].name + +def WalkForward(character): + + toFPS = Opt.ToFPS() + character.applyMovement([0,-0.05*toFPS,0], True) + + # Facing correctly + rig = getRig(character) + rot = rig.localOrientation.to_euler() + target = 0 + rot[2] = rot[2] + (( target - rot[2] ) / 5 ) + rig.localOrientation = rot + + if not character.get("underwater"): + ApplyAnimation(character, "Walk") + else: + ApplyAnimation(character, "Swim") + + WalkSound(character) + +def WalkBackward(character): + + toFPS = Opt.ToFPS() + character.applyMovement([0,0.05*toFPS,0], True) + + # Facing correctly + rig = getRig(character) + rot = rig.localOrientation.to_euler() + target = math.pi + if rot[2] < 0: target -= math.pi*2 + rot[2] = rot[2] + (( target - rot[2] ) / 5 ) + rig.localOrientation = rot + + if not character.get("underwater"): + ApplyAnimation(character, "Walk") + else: + ApplyAnimation(character, "Swim") + + WalkSound(character) + +def WalkLeft(character): + + toFPS = Opt.ToFPS() + character.applyMovement([0.05*toFPS,0,0], True) + + # Facing correctly + if not character.get("grab"): + rig = getRig(character) + rot = rig.localOrientation.to_euler() + target = math.pi/2 + if rot[2] < -(math.pi/2): rot[2] += math.pi*2 + rot[2] = rot[2] + (( target - rot[2] ) / 5 ) + rig.localOrientation = rot + + + + if not character.get("underwater"): + ApplyAnimation(character, "Walk") + else: + ApplyAnimation(character, "Swim") + + WalkSound(character) + +def WalkRight(character): + + toFPS = Opt.ToFPS() + character.applyMovement([-0.05*toFPS,0,0], True) + + # Facing correctly + if not character.get("grab"): + rig = getRig(character) + rot = rig.localOrientation.to_euler() + target = -(math.pi/2) + if rot[2] > math.pi/2: rot[2] -= math.pi*2 + rot[2] = rot[2] + (( target - rot[2] ) / 5 ) + rig.localOrientation = rot + + if not character.get("underwater"): + ApplyAnimation(character, "Walk") + else: + ApplyAnimation(character, "Swim") + + WalkSound(character) + +def Jump(character): + + toFPS = Opt.ToFPS() + character.applyMovement([0, 0, 0.07*toFPS], False) + + character["jumping"] = True + + if "Dani" in character.name: + + device = bge.logic.globalDict["SoundDevice"] + sound = bge.logic.globalDict["sounds"]["dani_jump"] + + if OnGround(character) and ( not sound["play"] or not sound["play"].status ) : + + sound["play"] = device.play(sound["sound"]) + +def GoDown(character): + + toFPS = Opt.ToFPS() + character.applyMovement([0, 0, -0.1*toFPS], False) + character["swimming_down"] = False + +def WaterBoyancy(character): + + # Creating water boyancy underwater + + toFPS = Opt.ToFPS() + scene = bge.logic.getCurrentScene() + + + if character["underwater"] and character.position[2] < -10.5 and not character["driving"]: + + #character.worldLinearVelocity.z /= 10 + #if round(character.worldLinearVelocity.z, 1) == 0.0: + character.worldLinearVelocity.z = 0.5 + + #character.applyForce([0,0,character.mass*9.8*-min(0, character.worldLinearVelocity[2])]) #*min(1.1, (character.position[2]+9.5)*-10) + scene.objects["DanisRig"]["underwater"] = True + elif character.position[2] > -9.5: + character["underwater"] = False + scene.objects["DanisRig"]["underwater"] = False + +def getIntoCar(character, car=None, passanger=False): + + # This function will run when any character wants to get into + # any car + + # If character already in a car or walking to another car + # we give this up. + if character.get("driving") or character.get("walkingtowardcar") or character.get("last_change",0) > time.time()-1: + return + + + if not car: + # If a car is not specified we are going to choose the + # closest car within 10 meters away from Dani. + + closest = 10 + car = None + + for trycar in bge.logic.globalDict["allcars"]: + distance = trycar.getDistanceTo(character) + if distance < closest: + car = trycar + closest = distance + if not car: + + # There is no car within this distance we give up + return + + # Then we want to choose the closest car target. + + dani_stand_target = car.children["Dani_Stand_Target"] + try: other_stand_target = car.children["Other_Stand_Target"] + except: other_stand_target = dani_stand_target + + # Now we want to activate the movement. + + character["walkingtowardcar"] = car + if not passanger: + character["walkingtowardcartarget"] = dani_stand_target + else: + character["walkingtowardcartarget"] = other_stand_target + character["becomepassanger"] = passanger + + # Readjust the camera + + # if character.name == "Dani_Box": + # car.children["CarCamTarget"].orientation = [0.2,0,0] + # zoom = car["specs"].get("cameraZoom", 3) + # ChangeCameraTarget(car.children["CarCamTarget"], 100, [zoom,zoom,zoom]) + pass + +def walkToward(character, point): + + # Function used when character needs to walk somewhere + + walkVector = character.getVectTo(point) + + # We want the character to walk only to the strongest + # vector. So he will not end up flapping arround all + # the time. + + fro = character.position.copy() + fro.z += 1 + + ray = Vehicle.BeautyRayCast(character, "walk", + point, + fro) + if ray[0] and ray[0].position != point and ray[0] != point and ray[0].visible: + + obstacleVector = character.getVectTo(ray[1]) + + # Instead of trying to walk toward the target we will instead walk + # around the obstacle + + WalkForward(character) + if obstacleVector[2][0] > 0: + character.alignAxisToVect(obstacleVector[1], 0, 0.5) + else: + character.alignAxisToVect(-obstacleVector[1], 0, 0.5) + character.alignAxisToVect( (0,0,1), 2, 1.0 ) + + + return walkVector + + else: + walkVectorIs = walkVector[2] + + if walkVectorIs[1] < 0: + WalkForward(character) + elif walkVectorIs[1] > 0: + WalkBackward(character) + + if walkVectorIs[0] > 0: + character.applyRotation([0,0,walkVectorIs[0]/5], False) + #WalkLeft(character) + elif walkVectorIs[0] < 0: + #WalkRight(character) + character.applyRotation([0,0,walkVectorIs[0]/5], False) + + if walkVectorIs[2] > 0.1: + Jump(character) + elif walkVectorIs[2] < -0.1: + GoDown(character) + + + return walkVector + +def ChangeCameraTarget(targetobject, delay, scaling=None, callable=None, rotate=True): + + # This function changes dani's camera target + # useful for when getting into cars and the + # mounting point of the camera is changed. + # And because I can, it is also delayed. + + scene = bge.logic.getCurrentScene() + + bge.logic.globalDict["CameraTargetChange"] = {"delay":delay, + "remaining":delay, + "target":targetobject, + "targetscale":scaling, + "callable":callable, + "rotate":rotate} + + cam_parent = scene.objects["DaniCam_Parent"] + cam_parent.removeParent() + + cam_parent["startingPosition"] = cam_parent.position.copy() + cam_parent["startingOrientation"] = mathutils.Vector(cam_parent.orientation.to_euler()) + + # This fixes the spins that are more than 360 degrees + targetRotation = mathutils.Vector(targetobject.orientation.to_euler()) + for i in range(3): + d = targetRotation[i] - cam_parent["startingOrientation"][i] + if d > math.pi: + cam_parent["startingOrientation"][i] += 2 * math.pi + elif d < -math.pi: + cam_parent["startingOrientation"][i] -= 2 * math.pi + + cam_parent["startingScaling"] = cam_parent.scaling.copy() + +def CameraRuleOfThirds(dani): + + # This function implements the rule of thirds. + # The rule of thirds is a technique to compose + # pretty images. Where the main subject in the + # frame is positioned on in the center of the + # frame, but rather slightly to the side. + # Imagine dividing the frame in 3. And putting + # your subject on one of those division lines. + # This is rule of thirds. + + # One more think to keep in mind. We want more empty + # frame toward the direction where the main character + # is looking. + + scene = bge.logic.getCurrentScene() + cam = scene.objects["DaniCam"] + + # We do that only for the walking. + if not dani["driving"]: + + # We want to get the z rotation of Dani's rig + # which is determening where he is looking. + # We are going to use local space orientation + # since it is in relation to the parent, which + # rotates in sinc with the camera. + + daniZrot = getRig(dani).localOrientation.to_euler()[2] + + # Finding the multiplication fraction of the effect + # When dani points in the same direction as the camera + # we want the effect to be 0. The same as if we look + # directly at him from the front. But looking at him + # from the side should give 100% effect. + + frac = math.cos(daniZrot * 2) - 1 + + # Limiting the effect to be between 0 and 1 + frac = min(0, max(-1, frac)) + + # Multiplying the effectiveness of the effect + frac = frac ** 0.6 + if type(frac) == complex: frac = float(frac.real) + + + # Flipping the effect when he looks right, versus left. + if daniZrot > 0: frac *= -1 + + # Applying the math to the camera + cam.localOrientation = [math.pi/2, # X - Move camera up, so it is not looking at the floor + -frac/3, # Y - Add a dutch angle since it's cool and cool is cool + math.pi+frac] # Z - Add the rule of thirds. + + else: + # Applying the math to the camera + cam.localOrientation = [math.pi/2, # X - Move camera up, so it is not looking at the floor + 0, + math.pi] + +def ExecuteChangeCameraTarget(): + + # This function will execute the actual camera movement itself. + + + scene = bge.logic.getCurrentScene() + + if "CameraTargetChange" not in bge.logic.globalDict: + return + + cam_parent = scene.objects["DaniCam_Parent"] + + + targetObject = bge.logic.globalDict["CameraTargetChange"]["target"] + delayFull = bge.logic.globalDict["CameraTargetChange"]["delay"] + delayRemaining = bge.logic.globalDict["CameraTargetChange"]["remaining"] + ToRotate = bge.logic.globalDict["CameraTargetChange"]["rotate"] + + + # Counting down frames of the delay + bge.logic.globalDict["CameraTargetChange"]["remaining"] -= 1 + + # Getting a fraction of how much from that other target the character walked + try: + camFactor = ( delayRemaining / delayFull ) ** 2 + except: + camFactor = 1 + camFactor = camFactor * -1 + 1 + camFactor = min(1, max(0, camFactor)) + + + # Applying camera location and rotation based on this factor + + targetObject.orientation = [0.2,0,0] + + target_position = targetObject.position + target_orientation = mathutils.Vector(targetObject.orientation.to_euler()) + + + if bge.logic.globalDict["CameraTargetChange"]["targetscale"]: + target_scale = mathutils.Vector(bge.logic.globalDict["CameraTargetChange"]["targetscale"]) + + if delayRemaining <= 0: + + + if ToRotate: + cam_parent.orientation = target_orientation + else: + vect = cam_parent.getVectTo(target_position) + vect[1].z = min(-0.1, vect[1].z) + cam_parent.alignAxisToVect(-vect[1], 1, 0.1) + + + cam_parent.position = target_position + + if bge.logic.globalDict["CameraTargetChange"]["targetscale"]: + cam_parent.scaling = target_scale + cam_parent.setParent(targetObject, 0, 0) + + if bge.logic.globalDict["CameraTargetChange"]["callable"]: + bge.logic.globalDict["CameraTargetChange"]["callable"]() + + del bge.logic.globalDict["CameraTargetChange"] + + return + + cam_parent.position = ( cam_parent["startingPosition"] + ( target_position - cam_parent["startingPosition"] ) * camFactor ) + if ToRotate: + cam_parent.orientation = ( cam_parent["startingOrientation"] + ( target_orientation - cam_parent["startingOrientation"] ) * camFactor ) + if bge.logic.globalDict["CameraTargetChange"]["targetscale"]: + cam_parent.scaling = ( cam_parent["startingScaling"] + ( target_scale - cam_parent["startingScaling"] ) * camFactor ) + +def walkTowardCar(character): + + # This function is running while the character is getting into car + + settings = bge.logic.globalDict["settings"] + + walkVector = walkToward(character, character["walkingtowardcartarget"]) + + scene = bge.logic.getCurrentScene() + car = character["walkingtowardcar"] + finaltarget = car.children["Dani_Stand_Target"] + passanger = character["becomepassanger"] + + if passanger: + try: finaltarget = car.children["Other_Stand_Target"] + except: pass + + # Change to final target + + if walkVector[0] < 1 and character["walkingtowardcartarget"] != finaltarget: + character["walkingtowardcartarget"] = finaltarget + + # Change to sitting inside of the car + + elif walkVector[0] < 1 and character["walkingtowardcartarget"] == finaltarget: + + StopWalkSound(character) + character["walkingtowardcar"] = False + if character.name == "Dani_Box": + car["active"] = True + + character["driving"] = car + + if not passanger: + car["driver"] = character + else: + if "passangers" not in car: + car["passangers"] = [] + car["passangers"].append(character) + + car["npc"] = "" + car["enemy"] = "" + car["shake"] = 0.0 + + rig = getRig(character) + rig["driving"] = True + target = car.children["Dani_Sit_Target"] + + if passanger: + try: target = car.children["Other_Sit_Target"] + except: pass + + rig.removeParent() + pos = target.worldPosition + rig.position = [pos[0],pos[1],pos[2]] + pos = target.worldOrientation + rig.orientation = [pos[0],pos[1],pos[2]] + rig.setParent(target, False, False) + + character.suspendPhysics(True) + character.setParent(car, False, False) + + + #car_rig = car.children[car.get("rig","RIG")] + #car_rig.playAction(car.get("rigaction", "NeonspeedsterRIGAction"), 10,20) + + if character.name == "Dani_Box": + scene.objects["Gage"].visible = True + scene.objects["Gage_Arrow"].visible = True + scene.objects["Gage_Health"].visible = True + scene.objects["Gage_Nitro"].visible = True + scene.objects["Gage_Tachometer"].visible = True + scene.objects["Gage_Gear"].visible = True + scene.objects["Gage_Redline"].visible = True + if settings.get("veh_mouse"): + + if settings.get("veh_mouse_guides"): + scene.objects["Mouse_Turn_UI_Piece"].visible = True + scene.objects["Mouse_Turn_UI"].visible = True + scene.objects["Mouse_Accelation_UI_Piece"].visible = True + scene.objects["Mouse_Accelation_UI"].visible = True + + if settings.get("veh_mouse_cursor"): + bge.render.showMouse(True) + + + Vehicle.RemoveNetId(car) + + print(consoleForm(character), "in car:", consoleForm(car)) + + + + # Sound device for sounds. + device = bge.logic.globalDict["SoundDevice"] + + sound = bge.logic.globalDict["sounds"]["door"] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].volume = 1.5 + + + # Readjust the camera + + if character.name == "Dani_Box": + car.children["CarCamTarget"].orientation = [0.2,0,0] + zoom = car["specs"].get("cameraZoom", 3) + ChangeCameraTarget(car.children["CarCamTarget"], 20, [zoom,zoom,zoom]) + +def getOutCar(character): + + # Fucntion to get out of car + + scene = bge.logic.getCurrentScene() + + # If character is not in the car + if character["walkingtowardcar"]: + car = character["walkingtowardcar"] + soundDo = False + elif character["driving"]: + car = character["driving"] + soundDo = True + else: + return + + rig = getRig(character) + + if character.name == "Dani_Box": + car["active"] = False + + # Unfucking the camera + cam = bge.logic.getCurrentScene().active_camera + cam.shift_x = 0 + cam.shift_y = 0 + cam.lens = 20 + + if character == car.get("driver"): + car["driver"] = None + if character in car.get("passangers",[]): + car["passangers"].remove(character) + + rig["driving"] = False + + character["last_change"] = int(time.time()) + + character.restorePhysics() + character.removeParent() + character.localLinearVelocity = [0,0,0] + if -car.localLinearVelocity[1] > 10 and not character["walkingtowardcar"]: + character["landing"] = 13 + + pos = car.worldOrientation.to_euler() + character.orientation = [0,0,float(pos[2])] + + if not character.get("walkingtowardcar"): + character.position[2] += 1.5 + + character["driving"] = False + + + rig.removeParent() + pos = character.worldPosition + rig.position = [pos[0],pos[1],pos[2]] + pos = character.worldOrientation + rig.orientation = [pos[0],pos[1],pos[2]] + rig.setParent(character, False, False) + + # Readjust camera + if character.name == "Dani_Box": + character.children["Dani_cam_Target"].orientation = [0,0,0] + ChangeCameraTarget(character.children["Dani_cam_Target"], 20, [1,1,1]) + + # if soundDo: + # rig = car.children["RIG"] + # rig.playAction(car.get("rigaction", "NeonspeedsterRIGAction"), 10,20) + + scene.objects["Gage"].visible = False + scene.objects["Gage_Arrow"].visible = False + scene.objects["Gage_Health"].visible = False + scene.objects["Gage_Nitro"].visible = False + scene.objects["Gage_Tachometer"].visible = False + scene.objects["Gage_Gear"].visible = False + scene.objects["Gage_Redline"].visible = False + scene.objects["Mouse_Turn_UI_Piece"].visible = False + scene.objects["Mouse_Turn_UI"].visible = False + scene.objects["Mouse_Accelation_UI_Piece"].visible = False + scene.objects["Mouse_Accelation_UI"].visible = False + + bge.render.showMouse(False) + + + if not character.get("walkingtowardcar"): + device = bge.logic.globalDict["SoundDevice"] + sound = bge.logic.globalDict["sounds"]["door"] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].volume = 1.5 + + character["walkingtowardcar"] = False + + print(consoleForm(character), "got out of:", consoleForm(car)) + +def getRig(character): + + scene = bge.logic.getCurrentScene() + + if type(character.get("rig", "")) == str and character.get("rig", ""): + rig = scene.objects[character.get("rig", "")] + elif character.get("rig"): + rig = character.get("rig") + + return rig + +def ApplyAnimation(character, animation, keep=False, **kwargs): + + # Optimization + cam = bge.logic.getCurrentScene().active_camera + if character in Reuse.reuse.get(character.name, [])\ + or cam.getDistanceTo(character) > 150: + return + + # Exctracting data request + rig = getRig(character) + action = rig.get("Action") + startframe = animation[0] + endframe = animation[1] + + # Failsafe + if not action: + return + + # If we are reading animation from the database + if type(animation) == str and animation in animations.get(character.name, {}): + + data = animations[character.name][animation] + action = data.get("Action", action) + keep = data.get("Keep", keep) + startframe = data["Frames"][0] + endframe = data["Frames"][1] + kwargs = data.get("kwargs", kwargs) + + # Sometimes we might want to keep the animation at it's + # last frame when it ends. + if keep and rig.getActionFrame(kwargs.get("layer", 0)) == endframe: + startframe = endframe + + + # Executing the animation + rig.playAction(action, startframe, endframe, **kwargs) + + # To activate the stand animation we want to know if anything is playing + if kwargs.get("layer", 0) == 0: + + # Recording the animation + rig["animation"] = animation + +def AutoStand(character): + + # This function will automatically play the stand animation if + # nothing else is playing. + + + rig = getRig(character) + + if not rig.get("animation"): + ApplyAnimation(character, "Stand") + + else: + + animation = rig.get("animation") + + startframe = animation[0] + endframe = animation[1] + + # If we are reading animation from the database + if type(animation) == str and animation in animations.get(character.name, {}): + + data = animations[character.name][animation] + keep = data.get("Keep", None) + startframe = data["Frames"][0] + endframe = data["Frames"][1] + kwargs = data.get("kwargs", {}) + + if rig.getActionFrame(0) == endframe: + rig["animation"] = None + +def GrabEdge(character): + + rig = getRig(character) + + chest = Vehicle.RelativePoint(rig, [0,0,0.7]) + fromchest = Vehicle.RelativePoint(rig, [0,-1,0.7]) + + chestray = Vehicle.BeautyRayCast(character, "chest", fromchest, chest) + + face = Vehicle.RelativePoint(rig, [0,0,1.5]) + fromface = Vehicle.RelativePoint(rig, [0,-1,1.5]) + + faceray = Vehicle.BeautyRayCast(character, "face", fromface, face) + + try: facedist = math.dist(face, faceray[1]) + except: facedist = 1 + try: chestdist = math.dist(chest, chestray[1]) + except: chestdist = 1 + + if chestdist < facedist: + + + if not character.get("jumping"): + + dist = chestdist+0.01 + toedge = Vehicle.RelativePoint(rig, [0,-dist,1.0]) + fromedge = Vehicle.RelativePoint(rig, [0,-dist,1.5]) + edgeray = Vehicle.BeautyRayCast(character, "edge", toedge, fromedge) + if edgeray[1]: + character["grab"] = float(edgeray[1].z-1.2) + else: + character["grab"] = float(character.position.z) + character.position.z = character["grab"] + character.worldLinearVelocity = [0,0,0] + + + + else: + + character["grab"] = character.position.z + + + # Ratate character to face the point. + vect = character.getVectTo(chestray[2]) + character.alignAxisToVect(chestray[2], 1, 1) + character.alignAxisToVect((0,0,1), 2, 1) + + vect = rig.getVectTo(chestray[2]) + rig.alignAxisToVect(chestray[2], 1, 1) + rig.alignAxisToVect((0,0,1), 2, 1) + + character.applyMovement((0,-0.01,0), True) + + + else: + character["grab"] = None + + + +### SOUND STUFF #### + +def WalkSound(character): + + if "Dani" in character.name and character.get("onground") and not character.get("landing"): + device = bge.logic.globalDict["SoundDevice"] + sound = bge.logic.globalDict["sounds"]["dani_walk"] + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) +def StopWalkSound(character): + + try: bge.logic.globalDict["sounds"]["dani_walk"]["play"].stop() + except: pass + + +##### STORY RELATED FUNCTIONS #### + +def StoryCharacter(character): + + # It may run from an actuator, so we want to make sure we get the object + # and not the actuator. + scene, character = Vehicle.GetSceneAndCar(character) + + + if character.get("walkingtowardcar"): + walkTowardCar(character) + + + if not character.get("driving"): + + AutoStand(character) + + else: + + ApplyAnimation(character, "Drive") + + ReactionsToDaniDriving(character) + +def ReactionsToDaniDriving(character): + + # This function will make characters react to dani's driving + # when they are sitting in the same car. + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + + # First we need to make sure that the character is in the + # car as dani. + + if not character.get("driving") == dani.get("driving") or not character.get("driving"): + return + + # Then we want to get data about what if going on with the car. + + car = character.get("driving") + crash = car.get("crash", 0.0) + near = car.get("near", 0.0) + speed = -car.localLinearVelocity[1] + + car["crash"] = 0.0 + car["near"] = 0.0 + + react = None + + if crash > 0.1: + react = "crash" + + if speed > 50: + react = "speed" + + if near >= 0.4: + react = "near" + + + + device = bge.logic.globalDict["SoundDevice"] + + try: + voices = bge.logic.globalDict[character.name+"Voices"] + except: + return + + if ( not character.get("saying") or not character.get("saying").status ) and react: + sound = random.choice(voices[react]) + while character.get("lastsaid") == sound: + sound = random.choice(voices[react]) + character["lastsaid"] = sound + sound["play"] = device.play(sound["sound"]) + character["saying"] = sound["play"] + + sound["play"].volume = 2 + + + ApplyAnimation(character, "Talk") + + +##### NETWORK RELATE FUNCTIONS #### + +def Encode(character): + + data = { + "ID" :id(character), + "type" :"chr", + "name" :character.name, + "netId" :character.get("netId"), + "position" :list(character.position), + "orientation" :list(character.orientation.to_euler()) + } + + character["netId"] = data["netId"] + + netObjects = bge.logic.globalDict["netObjects"] + if data["ID"] not in netObjects["pythonId"]: + netObjects["pythonId"][data["ID"]] = character + + return data + +def Decode(data, network=False): + + # Decoding character data + + netObjects = bge.logic.globalDict["netObjects"] + + if data.get("netId") not in netObjects["netId"]: + + # Making the character + try: + character = Reuse.Create(data.get("name")) + except: + character = Reuse.Create("MoriaBox") + + netObjects["netId"][data.get("netId")] = character + + character = netObjects["netId"][data.get("netId")] + character["netId"] = data.get("netId") + + character.position = data.get("position", (0,0,0)) + character.orientation = data.get("orientation", (0,0,0)) diff --git a/Scripts/Cinema.py b/Scripts/Cinema.py new file mode 100644 index 0000000..20f33cc --- /dev/null +++ b/Scripts/Cinema.py @@ -0,0 +1,185 @@ +# This script will make the cinema work. + + + +import bge +import aud + +def Main(w): + + scene = bge.logic.getCurrentScene() + cont = bge.logic.getCurrentController() + obj = cont.owner + + inCinema = scene.objects["CinemaColider"]["InCinema"] + + def cTprint(t): + cText = scene.objects["CinemaText"] + if cText["Text"] != t: + cText["Text"] = t + print(t) + + black = scene.objects["CinemaScreen.Black"] + + # Play the movie + + + + if obj.get("Movie"): + + cTprint("") + + if not obj.get("Video"): + movieTexture = bge.texture.VideoFFmpeg(obj.get("Movie")) + + print(dir(movieTexture)) + + texture = bge.texture.Texture(obj, 0, 0) + texture.source = movieTexture + texture.source.play() + texture.refresh(False) + + width, height = movieTexture.size + obj.scaling[1] = obj.scaling[1] / height * 1080 + + device = aud.Device() + sound = aud.Sound(obj.get("Movie")) + + obj["Sound"] = device.play(sound) + + + obj["Video"] = texture + obj["Time"] = bge.logic.getRealTime() + obj["StartTime"] = bge.logic.getRealTime() + obj["soundref"] = 100 + black.visible = False + + + elif inCinema: + + if obj.get("iTimer", 0): + obj["iTimer"] = obj.get("iTimer", 1) -1 + + # Move the audio forward + if 87 in bge.logic.globalDict["keys"] and not obj.get("iTimer", 0): + obj["AudOffset"] = obj.get("AudOffset", 0) + 0.25 + obj["iTimer"] = 10 + elif 85 in bge.logic.globalDict["keys"] and not obj.get("iTimer", 0): + obj["AudOffset"] = obj.get("AudOffset", 0) - 0.25 + obj["iTimer"] = 10 + + if bge.logic.getRealTime() - obj["Time"] > (1/30): + + + obj["Video"].source.refresh()#obj["Sound"].position) + obj["Video"].refresh(False) + obj["Time"] = bge.logic.getRealTime() + + #print(obj["Video"].source.framerate) + #print("Video Refreshed at:", obj["Time"], obj["Sound"].position) + + if not obj["soundref"]: + + try: + obj["Sound"].position = bge.logic.getRealTime()-obj["StartTime"]+obj.get("AudOffset", 0) + except: + + obj["Movie"] = None + obj["Video"] = None + black.visible = True + obj.scaling[1] = 1 + + obj["cTfolder"] = obj["cTfolder"][:obj["cTfolder"].rfind("/")] + obj["cTfolder"] = obj["cTfolder"][:obj["cTfolder"].rfind("/")] + "/" + + obj["soundref"] = 100 + else: + obj["soundref"] -= 1 + else: + obj["Sound"].stop() + obj["Video"].source.stop() + obj["Movie"] = None + obj["Video"] = None + + obj.scaling[1] = 1 + + black.visible = True + + obj["cTfolder"] = obj["cTfolder"][:obj["cTfolder"].rfind("/")] + obj["cTfolder"] = obj["cTfolder"][:obj["cTfolder"].rfind("/")] + "/" + + # FILE MANAGER ( I know I'm crazy ) + elif inCinema: + + + import os + + + + obj["cTfolder"] = obj.get("cTfolder", "/") + obj["cTselect"] = obj.get("cTselect", 0) + + # Files and folders + + formats = [".mp4", ".ogg", ".ogv", ".avi"] + + try: + listdir = [] + for i in os.listdir(obj["cTfolder"]): + for format in formats: + if not os.path.isfile(obj["cTfolder"]+i) or i.endswith(format): + listdir.append(i) + break + listdir = list(sorted(listdir)) + except: + listdir = [] + + obj["Movie"] = obj["cTfolder"][:-1] + + if not listdir: + obj["cTfolder"] = obj["cTfolder"][:obj["cTfolder"].rfind("/")] + obj["cTfolder"] = obj["cTfolder"][:obj["cTfolder"].rfind("/")] + "/" + + if obj.get("iTimer", 0): + obj["iTimer"] = obj.get("iTimer", 1) -1 + + + # Up and down + if 72 in bge.logic.globalDict["keys"] and not obj.get("iTimer", 0): + obj["cTselect"] = ( obj["cTselect"] -1 ) % len(listdir) + obj["iTimer"] = 10 + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["checkpoint"]["sound"]) # Play a Ding + elif 70 in bge.logic.globalDict["keys"] and not obj.get("iTimer", 0): + obj["cTselect"] = ( obj["cTselect"] +1 ) % len(listdir) + obj["iTimer"] = 10 + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["checkpoint"]["sound"]) # Play a Ding + elif 7 in bge.logic.globalDict["keys"] and not obj.get("iTimer", 0): + obj["cTfolder"] = obj["cTfolder"] + listdir[obj["cTselect"]] + "/" + obj["cTselect"] = 0 + obj["iTimer"] = 10 + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["checkpoint"]["sound"]) # Play a Ding + elif 59 in bge.logic.globalDict["keys"] and not obj.get("iTimer", 0): + obj["cTfolder"] = obj["cTfolder"][:obj["cTfolder"].rfind("/")] + obj["cTfolder"] = obj["cTfolder"][:obj["cTfolder"].rfind("/")] + "/" + obj["cTselect"] = 0 + obj["iTimer"] = 10 + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["checkpoint"]["sound"]) # Play a Ding + + # Drawing + t = "[ "+obj["cTfolder"]+" ]\n\n" + for n, f in enumerate(listdir[max(0,obj["cTselect"]-5):max(0,obj["cTselect"]-5)+10]): + if listdir[obj["cTselect"]] == f: + t = t + " [ " + else: + t = t + " " + + t = t+f + + if listdir[obj["cTselect"]] == f: + t = t + " ] " + else: + t = t + " " + + t = t + "\n" + + cTprint(t) diff --git a/Scripts/Common.py b/Scripts/Common.py new file mode 100644 index 0000000..e3e9f9e --- /dev/null +++ b/Scripts/Common.py @@ -0,0 +1,161 @@ +import random + +# Common things needed for various things + +# Colors are used to make the +clr = { + "norm":"\033[00m", # Reset to normal + "bold":"\033[01m", # Bold Text + "ital":"\033[03m", # Italic Text + "undr":"\033[04m", # Underlined + "blnk":"\033[05m", # Blinking + + # Text + "tdbl":"\033[30m", # Dark Black + "tdrd":"\033[31m", # Dark Red + "tdgr":"\033[32m", # Dark Green + "tdyl":"\033[33m", # Dark Yellow + "tdbu":"\033[34m", # Dark Blue + "tdma":"\033[35m", # Dark Magenta + "tdcy":"\033[36m", # Dark Cyan + "tdwh":"\033[37m", # Dark White + + "tbbl":"\033[90m", # Bright Black + "tbrd":"\033[91m", # Bright Red + "tbgr":"\033[92m", # Bright Green + "tbyl":"\033[93m", # Bright Yellow + "tbbu":"\033[94m", # Bright Blue + "tbma":"\033[95m", # Bright Magenta + "tbcy":"\033[96m", # Bright Cyan + "tbwh":"\033[97m", # Bright White + # Background + "bdbl":"\033[40m", # Dark Black + "bdrd":"\033[41m", # Dark Red + "bdgr":"\033[42m", # Dark Green + "bdyl":"\033[43m", # Dark Yellow + "bdbu":"\033[44m", # Dark Blue + "bdma":"\033[45m", # Dark Magenta + "bdcy":"\033[46m", # Dark Cyan + "bdwh":"\033[47m", # Dark White + + "bbbl":"\033[100m", # Bright Black + "bbrd":"\033[101m", # Bright Red + "bbgr":"\033[102m", # Bright Green + "bbyl":"\033[103m", # Bright Yellow + "bbbu":"\033[104m", # Bright Blue + "bbma":"\033[105m", # Bright Magenta + "bbcy":"\033[106m", # Bright Cyan + "bbwh":"\033[108m" # Bright White +} + +keycodes = { + "A":23, + "B":24, + "C":25, + "D":26, + "E":27, + "F":28, + "G":29, + "H":30, + "I":31, + "J":32, + "K":33, + "L":34, + "M":35, + "N":36, + "O":37, + "P":38, + "Q":39, + "R":40, + "S":41, + "T":42, + "U":43, + "V":44, + "W":45, + "X":46, + "Y":47, + "Z":48, + + "0":13, + "1":14, + "2":15, + "3":16, + "4":17, + "5":18, + "6":19, + "7":20, + "8":21, + "9":22, + + "F1" :88, + "F2" :89, + "F3" :90, + "F4" :91, + "F5" :92, + "F6" :93, + "F7" :94, + "F8" :95, + "F9" :96, + "F10":97, + "F11":98, + "F12":99, + + "LShift" :55, + "RShift" :54, + "LCtrl" :50, + "RCtrl" :53, + "LAlt" :51, + "RAlt" :52, + "Tab" :57, + "Space" :8, + "Enter" :7, + "BackSpace" :59, + "Delete" :60, + "Home" :110, + "End" :109, + "PageUp" :111, + "PageDown" :112, + + "UpArrow" :72, + "DownArrow" :70, + "RightArrow":71, + "LeftArrow" :69 + +} + +mousecodes = { + + "RMB" : 118, + "MMB" : 117, + "LMB" : 116 +} + +IDColors = {} + +def consoleForm(obj): + + # Function that prints a pretty object info about + + ID = hex(id(obj))[2:].upper() + NAME = str(obj) + + c = IDcolor(ID) + + return c + " " + ID + " " + NAME + " " + clr["norm"] + +def IDcolor(ID): + + # Coloring + if ID not in IDColors: + r = random.random() + g = random.random() + b = random.random() + + ct = clr["tbwh"] + if max(r, g, b ) > 0.9 or sum((r, g, b))/3 > 0.5: + ct = clr["tdbl"] + + c = clr["bold"] + "\033[48;2;"+str(int(r*256))+";"+str(int(g*256))+";"+str(int(b*256))+"m"+ct + IDColors[ID] = c + c = IDColors[ID] + return c diff --git a/Scripts/Destruction.py b/Scripts/Destruction.py new file mode 100644 index 0000000..84265fc --- /dev/null +++ b/Scripts/Destruction.py @@ -0,0 +1,683 @@ +# This is full of destruction stuff + +import bge +import aud + +import random +import numpy +import math +import mathutils +import types + +from Scripts import Reuse +from Scripts import Opt +from Scripts import Multiplayer_Shared + +from Scripts.Common import * + +scene = bge.logic.getCurrentScene() +cam = scene.active_camera + +if "Destruction" not in bge.logic.globalDict: + bge.logic.globalDict["Destruction"] = {} + +database = bge.logic.globalDict["Destruction"] + +# Particle system +def particles(object, # Particle object + point, # Point where to spawn them + despawn, # Time ( in frames before disappearing ) + amount=5, # Amount of them to spawn + max=50, # Max amount of them in the scene + spread=0, # Spread of particles on start + normal=0.05, # Spread of particles over time + velocity=[0,0,0], # Velocity of particles + scale = 1 # Scale of the particle object + ): + + # Performance kill switch + if not Opt.GoodFPS("particles", 0.5): + return + + + # Not do the effect if not in view + if cam.getDistanceTo(point) > 100 or not cam.pointInsideFrustum(point) == cam.INSIDE: + return + + # We will need a place to register particles + if "particles-registry" not in bge.logic.globalDict: + bge.logic.globalDict["particles-registry"] = {} + registry = bge.logic.globalDict["particles-registry"] + + # Making sure that registry contains particle type + if object not in registry: + registry[object] = [] + + for i in range(amount): + + # If we reached max, we abort + inscene = len( registry[object] ) + inreuse = len( Reuse.reuse.get(object, []) ) + spawned = inscene - inreuse + + if spawned > max: + return + + # Making particle + particle = Reuse.Create(object, despawn) + particle.scaling = [scale, scale, scale] + + # Recording into registry + if particle not in registry[object]: + registry[object].append(particle) + + # Puting it into position ( adding the spread ) + particle.position = point + mathutils.Vector([random.uniform(-spread, spread), + random.uniform(-spread, spread), + random.uniform(-spread, spread)]) + + # The next part will require an actuator + + # Introducting random spread over time + particle.actuators["Motion"].dLoc = mathutils.Vector([random.uniform(-normal, normal), + random.uniform(-normal, normal), + random.uniform(-normal, normal)]) + + # Adding the velocity + particle.actuators["Motion"].dLoc += mathutils.Vector(velocity) + +def Smoke(point, strength=0.5): + + # Not do the effect if not in view + if cam.getDistanceTo(point) > 100 or not cam.pointInsideFrustum(point) == cam.INSIDE: + return + + if numpy.random.choice([True, False], p=[strength, 1-strength]): + + # Performance kill switch + def do(point): + + smokeParticle = Reuse.Create("Smoke", random.randint(30, 60)) + smokeParticle.position = point + + if Opt.GoodFPS("Smoke", strength): + do(point) + +def Dust(point, strength=0.5): + + # Not do the effect if not in view + if cam.getDistanceTo(point) > 100 or not cam.pointInsideFrustum(point) == cam.INSIDE: + return + + if numpy.random.choice([True, False], p=[strength, 1-strength]): + + # Performance kill switch + def do(point): + + smokeParticle = Reuse.Create("Dust", random.randint(30, 60)) + smokeParticle.position = point + + if Opt.GoodFPS("Dust", 0.85): + do(point) + else: + pass + +def FireElementTextureLogic(obj): + + if type(obj) == bge.types.SCA_PythonController: + obj = obj.owner + + vect = obj.getVectTo(cam) + obj.alignAxisToVect((0,0,1), 2, 1) + obj.alignAxisToVect(vect[1], 1, 1) + obj.blenderObject["fire"] -= 0.1 + + + try: v = obj.parent.worldLinearVelocity.copy() + except: + try: v = obj.parent.parent.worldLinearVelocity.copy() + except:return + + v = -v + mathutils.Vector((0,0,30)) + obj.alignAxisToVect(v, 2, 1) + +def AttatchFireBall(point, attachment=None, size=1, duration=0): + + # This function will attach a fireball to a point. + + # Making the FireBall + FireBall = Reuse.Create("FireBall", duration) + + # Attatching the Fireball + FireBall.position = point.copy() + FireBall.scaling = [size, size, size] + if attachment: + FireBall.setParent(attachment, True) + + # Attatching a light to it. + if not duration: + if "Light" not in FireBall: + + light = Reuse.Create("FireLamp") + + light.position = FireBall.position + light.setParent(FireBall) + FireBall["Light"] = light + + light = FireBall["Light"] + light.blenderObject.data.use_shadow = False + + # In case the fireball is farther than 50 meters + # from the camera, we execute this ones. + FireElementTextureLogic(FireBall) + + # Storring it and returning + + #ID = Multiplayer_Shared.RandomString() + #database[ID] = FireBall + #FireBall["ID"] = ID + + return FireBall + +def DeleteFireBall(FireBall): + + # To save space we gonna do that. + if "Light" in FireBall: + + light = FireBall["Light"] + light.removeParent() + Reuse.Delete(light) + + Reuse.Delete(FireBall) + FireBall.removeParent() + +def FireBurst(point, size): + + # This will make a short burst of fire. + + FireBurst = Reuse.Create("FireBurst", 10) + FireBurst.position = point.copy() + FireBurst.scaling = [size, size, size] + FireBurst.blenderObject["fire"] = 1 + +def SpreadOnGround(object, point, size, spread, amount, duration=0, randomDuration=True): + + # This works similarly to particles, but + # instead puts the objects onto the ground. + + for i in range(amount): + + if randomDuration: + d = random.uniform( duration / 2 , duration ) + else: d = duration + + obj = Reuse.Create(object, d) + + + obj.position = mathutils.Vector(point) + mathutils.Vector([random.uniform(-spread, spread), + random.uniform(-spread, spread), + random.uniform(-spread, spread)]) + + obj.scaling = [size, size, size] + + # Puttin the object on the flor + obj.position.z += spread + 1 + topos = obj.position.copy() + topos.z -= 200 + ray = obj.rayCast(topos) + if ray[1]: obj.position = ray[1] + + +def Fire(point, strength=0.5): + + + # Not do the effect if not in view + if cam.getDistanceTo(point) > 100 or not cam.pointInsideFrustum(point) == cam.INSIDE: + return + + if numpy.random.choice([True, False], p=[strength, 1-strength]): + + # Performance kill switch + def do(point): + + #fireParticle = Reuse.Create("Fire", random.randint(30, 60)) + #fireParticle.position = point + #FireBurst(point, 0.5) + + # Adding some sparks to the fire + particles("FireBurst", point, + despawn = 10, + amount = random.randint(0, 3), + max = 50, + spread = 0.5, + normal = 0.01, + velocity = [0,0,0.1], + scale=random.uniform(0.2,1.0)) + + if Opt.GoodFPS("Fire", strength): + do(point) + +def WaterSplash(point, strength=0.5, rot=0, force=1): + + if numpy.random.choice([True, False], p=[strength, 1-strength]): + waterSplash = Reuse.Create("WaterSplashCar", random.randint(10, 20)) + waterSplash.position = point + waterSplash.orientation = [0,0,rot] + waterSplash.scaling = [ + random.uniform(0.6, 1.4), + random.uniform(0.6, 1.4), + random.uniform(0.6*force, 1.4*force) + ] + + # Sound + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/splash.ogg" + + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = point + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = 5 * min(2, force) + +def Explosion(point, visible=True, size=3, mass=10, effectcars=True): + + # Shockwave + + # Shockwave will be done using math applied to every object within + # a certain size to the explosion. + + dani = scene.objects["Dani_Box"] + if dani.get("driving"): + dani["driving"]["shake"] = mass * 10 / dani.getDistanceTo(point) + + for object in scene.objects: + + # Getting distance to the object + distance = object.getDistanceTo(point) + + # Skipping every object outside of the size of the explosion + if distance > size: + continue + + # Calculating forces on the object ( father away less forces ). + force = ( 1 - distance / size ) * mass * object.mass + + # Calculating direction of the force + vector = object.getVectTo(point)[1] * -force + + # Applying force + try: + object.applyImpulse(point, vector) + + + # If it is a car we can apply damage to it, by sending it a fake + # impulse of collision. + + # But first we need to make sure to have the data for this + # operation + Apoint = types.SimpleNamespace() + Apoint.worldPoint = mathutils.Vector(point) + Apoint.appliedImpulse = force * 10 + points = [Apoint] + + # Then we send it over + for callback in object.collisionCallbacks: + callback(None, point, None, points) + + except: + pass + + # Then we also have objects that use this very module to be + # destructable. The all have a collision actuator listening + # to a collision with a property "explosion" to execute their + # function. + + if "spawned_by" in object: + for controller in object.controllers: + + # This is a bit hacky, but it works. + try: + if "Destruction" in controller.script: + exec(controller.script.replace("Scripts.Destruction.", "")+"(controller)") + except: pass + + # And also we want to unsuspend it's physics. + + object.restoreDynamics() + + + # Fire particles + if visible: + + # particles("Fire", point, + # despawn = random.randint(int(size/2), int(size)), + # amount = random.randint(int(size/2), int(size)), + # max = 200, + # spread = 0, + # normal = 0.2, + # velocity = [0,0,0.01], + # scale = 2) + + FireBurst(point, size/2) + + particles("Sparkle", point, + despawn = random.randint(0, 100), + amount = random.randint(10, 50), + max = 100, + spread = size/2, + normal = 0.5, + velocity = [0,0,0.01]) + + Smoke(point, 1) + + #AttatchFireBall(point, + # size = 0.5, + # duration = 200) + + SpreadOnGround("FireBall", + point, + 0.5, + size/2, + 10, + 500) + + + # Sound + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/boom.ogg" + + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = point + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = 20 + + +def DeclareBroken(cont): + + #cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + part = cont.owner + + part["spawned_by"]["broken"] = True + +def WoodGate_level0(cont): + + + #cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + part = cont.owner + + part["spawned_by"]["broken"] = True + + + colision = cont.sensors["Collision"] + car = colision.hitObject + + def do(cont, car, scene, part): + + # Optimization trick + if len(Reuse.reuse.get("GatePart.Level0", [])) >= 4: + + + # Spawning madness + for i in range(4): + chunk = Reuse.Create("GatePart.Level0", 10, selfDestructInactive=True) + Reuse.SelfDestruct(chunk, 500) + chunk.mass = 0 + chunk.position = part.position + chunk.orientation = part.orientation + chunk.applyMovement( [ 0, i*1.24-2.45, 0 ] , True) + + if car: + cv = car.getLinearVelocity() + chunk.setLinearVelocity( cv / 2) + + + + + # Sound + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/wood.ogg" + + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = chunk.position + sound["play"].velocity = chunk.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = 5 + + # Deleting the wooden gate + Reuse.Delete(part) + + if Opt.GoodFPS("WoodGate_level0", 0.8) or (car and car.get("active")): + do(cont, car, scene, part) + else: + Opt.ScheduleTask("WoodGate_level0 ["+str(id(part))+"]", 0.8, do, + cont, car, scene, part) + +def WoodGate_level1(cont): + + # Performance kill switch + if not Opt.GoodFPS("WoodGate_level1", 0.7): + return + + + #cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + part = cont.owner + + + # Optimization trick + if len(Reuse.reuse.get("GatePart.Level1", [])) >= 4: + + + # Spawning madness + chunks = [] + for i in range(4): + chunk = Reuse.Create("GatePart.Level1", 10, selfDestructInactive=True) + Reuse.SelfDestruct(chunk, 100) + chunk.mass = 0 + chunk.position = part.position + chunk.orientation = part.orientation + chunks.append(chunk) + + + chunks[0].applyMovement( [ 0, 0, 0.75 ] , True) + chunks[0].applyRotation( [ math.pi/2, 0, 0 ] , True) + + chunks[1].applyMovement( [ 0, -0.75, 0 ] , True) + + chunks[2].applyMovement( [ 0.1, 0, 0 ] , True) + chunks[2].applyRotation( [ math.pi/4, 0, 0 ] , True) + + chunks[3].applyRotation( [ -( math.pi/4 ), 0, 0, ] , True) + + # Deleting the wooden part + Reuse.Delete(part) + +def LightStand(cont): + + + + + #cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + part = cont.owner + + colision = cont.sensors["Collision"] + car = colision.hitObject + + def do(cont, car, scene, part): + + part["spawned_by"]["broken"] = True + + + + head = None + tail = None + if Reuse.reuse.get("LightStand.Borked.Head"): + # Head ( top part ) + head = Reuse.Create("LightStand.Borked.Head", 300, selfDestructInactive=True) + Reuse.SelfDestruct(head, 600) + head.position = part.position + head.orientation = part.orientation + + if Reuse.reuse.get("LightStand.Borked.Tail"): + # Tail ( or leg ) + tail = Reuse.Create("LightStand.Borked.Tail", 300, selfDestructInactive=True) + Reuse.SelfDestruct(tail, 600) + tail.position = part.position + tail.orientation = part.orientation + tail.applyMovement( [ 0, 0, -1.3 ] , True) + tail.localAngularVelocity = [random.uniform(-1,1),random.uniform(-1,1),random.uniform(-1,1)] + + if car: + cv = car.getLinearVelocity() + if head: head.setLinearVelocity( cv ) + if tail: tail.setLinearVelocity( cv ) + + # Deleting the light stand + Reuse.Delete(part) + + if head: + + # Sound + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/hit.ogg" + + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = head.position + sound["play"].velocity = head.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = 5 + + if Opt.GoodFPS("LightStand", 0.9) or (car and car.get("active")): + do(cont, car, scene, part) + else: + Opt.ScheduleTask("LightStand ["+str(id(part))+"]", 0.8, do, + cont, car, scene, part) + +def HouseShelf(cont): + + # Performance kill switch + if not Opt.GoodFPS("HouseShelf", 0.5): + return + + + #cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + part = cont.owner + + part["spawned_by"]["broken"] = True + + # -753 -940 412 + + despawn = 200 + + # Shelfs + for i in range(5): + shelf = Reuse.Create("Shelf", despawn) + shelf.position = part.position + shelf.orientation = part.orientation + shelf.applyMovement([0.09,0,(0.3*i)-0.65], True) + + # Legs + leg = Reuse.Create("Shelf_Leg1", despawn) + leg.position = part.position + leg.orientation = part.orientation + leg.applyMovement([1.3,0,0], True) + + leg = Reuse.Create("Shelf_Leg2", despawn) + leg.position = part.position + leg.orientation = part.orientation + leg.applyMovement([-1.1,0,0], True) + + # Lego bricks + brick = Reuse.Create("LegoBrick", despawn) + brick.position = part.position + brick.orientation = part.orientation + brick.applyMovement([0.3,0,0.31], True) + brick.applyRotation([0,0,-(math.pi/4)], True) + + brick = Reuse.Create("LegoBrick", despawn) + brick.position = part.position + brick.orientation = part.orientation + brick.applyMovement([-0.2,0,0], True) + brick.applyRotation([0,0,-(math.pi/4)], True) + + # Toy Boxes + box = Reuse.Create("Toy_Box", despawn) + box.position = part.position + box.orientation = part.orientation + box.applyMovement([-0.6,0,0], True) + + box = Reuse.Create("Toy_Box", despawn) + box.position = part.position + box.orientation = part.orientation + box.applyMovement([0.3,0,0.7], True) + + box = Reuse.Create("Toy_Box", despawn) + box.position = part.position + box.orientation = part.orientation + box.applyMovement([0.3,0,-0.7], True) + + # Deleting the light stand + Reuse.Delete(part) + + # Sound + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/wood.ogg" + + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = box.position + sound["play"].velocity = box.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = 5 diff --git a/Scripts/Garage.py b/Scripts/Garage.py new file mode 100644 index 0000000..63607e5 --- /dev/null +++ b/Scripts/Garage.py @@ -0,0 +1,307 @@ +# GPLv3-or-later +# (C) J.Y.Amihud ( blenderdumbass ) + +# This will deal with garage and inventory system. + +import bge + +import mathutils + +from Scripts import Reuse + +from Scripts import Vehicle +from Scripts import Money + +from Scripts.Common import * + +if "inventory" not in bge.logic.globalDict: + bge.logic.globalDict["inventory"] = {} +inventory = bge.logic.globalDict["inventory"] + +# Items you can buy for the inventory +shop = { + "Wheels":{ + "name":"Car Wheels", + "cost":392, + "model":"Garage_wheel", + "icon":[0,1], + "usemodel":"HatchBack01-white-wheel", + "usescale":0.383585 + }, + "Parts":{ + "name":"Body-work\nParts Kit", + "cost":283, + "model":"Garage_bodypart", + "icon":[0,2], + "usemodel":"Redkiss_Car_body.005" + }, + "Nitros":{ + "name":"NiTRO", + "cost":465, + "model":"Garage_Nitro", + "icon":[0,0], + "usemodel":"NitroCan" + }, + "Spoiler1":{ + + "name":"Smooth-Bruh\nSpoiler", + "cost":249, + "model":"Garage_Spoiler1", + "icon":[0,3], + "usemodel":"Spoiler1_Attatch", + "effects":{ + "drag" :0.75, + "downforce":1.3 + } + }, + "Spoiler2":{ + + "name":"Metalcore\nSpoiler", + "cost":353, + "model":"Garage_Spoiler2", + "icon":[0,4], + "usemodel":"Spoiler2_Attatch", + "effects":{ + "drag" :0.50, + "downforce":1.51 + } + }, + "Spoiler3":{ + + "name":"Stupid\nSpoiler", + "cost":481, + "model":"Garage_Spoiler3", + "icon":[0,5], + "usemodel":"Spoiler3_Attatch", + "effects":{ + "drag" :0.90, + "downforce":1.05 + } + }, + "Spoiler4":{ + + "name":"6C\nSpoiler", + "cost":699, + "model":"Garage_Spoiler4", + "icon":[0,6], + "usemodel":"Spoiler4_Attatch", + "effects":{ + "drag" :0.3, + "downforce":2.01 + } + } +} + + +def Encode(): + + # We want to look through all cars that are in the garage. + # And see which ones are in the garage + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + garage = scene.objects["GarageColider"] + garageDistance = 31 + + # Making sure that we are not running it forever + saved = bge.logic.globalDict.get("garage-saved", []) + if saved: + return saved + + # Saving the cars. + + # IMPORTANT! The data here should be possible to save into json. + + for car in bge.logic.globalDict["allcars"]: + if car.getDistanceTo(garage) < garageDistance: + + cardata = Vehicle.Encode(car) + saved.append(cardata) + Reuse.Delete(car) + + bge.logic.globalDict["garage-saved"] = saved + return saved + +def Decode(): + + # This will restore cars into the garage + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + garage = scene.objects["GarageColider"] + + # Making sure that we are not running it forever + saved = bge.logic.globalDict.get("garage-saved", []) + if not saved: + return + + for cardata in saved: + + Vehicle.Decode(cardata, new=True) + + bge.logic.globalDict["garage-saved"] = [] + UpdateShelfs() + +def Inside(): + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + garage = scene.objects["GarageColider"] + garageDistance = 31 + + ingarage = [] + + for car in bge.logic.globalDict["allcars"]: + if car.getDistanceTo(garage) < garageDistance: + ingarage.append(car) + + return ingarage + + +def Computer(): + + # The inventory computer in the Garage + + scene = bge.logic.getCurrentScene() + cont = bge.logic.getCurrentController() + dani = scene.objects["Dani_Box"] + + computer = scene.objects["Computer"] + computerIcon = scene.objects["ComputerIcon"] + computerIconTop = scene.objects["ComputerIcon.Top"] + computerIconBottom = scene.objects["ComputerIcon.Bottom"] + computerItemName = scene.objects["Garage_Computer_Text.Name"] + computerItemHave = scene.objects["Garage_Computer_Text.Have"] + computerItemPrice = scene.objects["Garage_Computer_Text.Price"] + + keys = bge.logic.globalDict["keys"] + + if dani.getDistanceTo(computer) > 3: + return + + else: + + # Updating the icons + + # You need to trigger the depsgraph on them for them + # to get the material updated. So we do a dummy operation. + + computerIcon.applyRotation((0,0,0)) + computerIconTop.applyRotation((0,0,0)) + computerIconBottom.applyRotation((0,0,0)) + + for n, name in enumerate(shop): + item = shop[name] + have = inventory.get(name, 0) + + if computer.get("selection",0) == n: + + printname = item.get("name", name) + computerItemName["Text"] = printname + + have = "have "+str(int(inventory.get(name, 0))) + computerItemHave["Text"] = have + + price = "$"+str(item.get("cost", 0)) + computerItemPrice["Text"] = price + + + + icon = item.get("icon", [16,16]) + computerIcon.blenderObject["raw"] = icon[0] + computerIcon.blenderObject["column"] = icon[1] + + elif computer.get("selection",0) + 1 == n: + + icon = item.get("icon", [16,16]) + computerIconBottom.blenderObject["raw"] = icon[0] + computerIconBottom.blenderObject["column"] = icon[1] + + elif computer.get("selection",0) + 1 == len(shop): + + icon = [15,15] + computerIconBottom.blenderObject["raw"] = icon[0] + computerIconBottom.blenderObject["column"] = icon[1] + + elif computer.get("selection",0) - 1 == n: + + icon = item.get("icon", [16,16]) + computerIconTop.blenderObject["raw"] = icon[0] + computerIconTop.blenderObject["column"] = icon[1] + + elif computer.get("selection",0) == 0: + + icon = [15,15] + computerIconTop.blenderObject["raw"] = icon[0] + computerIconTop.blenderObject["column"] = icon[1] + + + + if keycodes["UpArrow"] in keys and not computer.get("timer"): + computer["selection"] = max(computer.get("selection",0) - 1, 0) + computer["timer"] = 5 + + elif keycodes["DownArrow"] in keys and not computer.get("timer"): + computer["selection"] = min(computer.get("selection",0) + 1, len(shop)-1) + computer["timer"] = 5 + + elif keycodes["Enter"] in keys and not computer.get("timer"): + Buy(list(shop.keys())[computer.get("selection",0)]) + computer["timer"] = 5 + + if computer.get("timer"): computer["timer"] -= 1 + +def Buy(name): + + # This function will execute a buying of an item + + # First we are going to test whether the player has enough money + cost = shop.get(name, {}).get('cost', 0) + model = shop.get(name, {}).get('model') + + if Money.Have(cost): + + Money.Pay(cost) + + if name not in inventory: + inventory[name] = 1 + else: + inventory[name] += 1 + + UpdateShelfs() + + else: + bge.logic.globalDict["print"] = "Not enough Money to buy "+name+"." + +def Use(name): + + if name in inventory: + inventory[name] = max(inventory[name] - 1, 0) + UpdateShelfs() + +def Has(name): + + return inventory.get(name) + +def UpdateShelfs(): + + # This function updates the shelfs in the game to show if you + # have stuff. Otherwise you would need to come every time to the + # computer. + + scene = bge.logic.getCurrentScene() + + for name in inventory: + + model = shop.get(name, {}).get('model') + amount = inventory[name] + amount = min( amount, 5 ) + + if model: + + model = scene.objects[model] + model.blenderObject.modifiers["Array"].count = amount + if not amount: + model.visible = False + else: + model.visible = True diff --git a/Scripts/LEGACY_Car_controll.py b/Scripts/LEGACY_Car_controll.py new file mode 100644 index 0000000..50e1184 --- /dev/null +++ b/Scripts/LEGACY_Car_controll.py @@ -0,0 +1,1769 @@ +import bge +import time +import math +import bpy +import random +import numpy +import aud +import mathutils + +import inspect + +import Reuse +import Distraction +import Opt + +import Character_Controll + +deleteAtDistance = 500 +fixGaragePosition = [-754.61, -1043.9, 409.32] + + +raceNames = {"RedKissBox":"Tali", + "NeonSpeedsterBox":"Jack"} + +def start_car(): + + # This function runs when the car is created. + # It creates the Bullet Physics RayCast Vehicle + # wrapper for the car. + + # Getting basic things + cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + car = cont.owner + + # Creating the vehicle constraint itself + vehicle = bge.constraints.createVehicle(car.getPhysicsId()) + + # Saving the ID of the constraint so we could access it later. + car["cid"] = vehicle.getConstraintId() + bge.logic.globalDict[car["cid"]] = {"wheels":[]} + + + ###### SETTING UP WHEELS ##### + + # LEFT BACK + wheel0 = car.children[car.get("wheelsname", "Wheel")+""] + bge.logic.globalDict[car["cid"]]["wheels"].append(wheel0) + wheel0.removeParent() + vehicle.addWheel(wheel0, + [car.get("wheelswidth", 0.89), car.get("wheelsback", 1.65), car.get("wheelsdown", -0.1)], # Pos Local + [0, 0, -1], # Dir Local + [-1, 0, 0], # Axl Local + 0.2, # Suspention length + car.get("wheelradius", 0.42), # Wheel Radius + 1 + ) + + # RIGHT BACK + wheel1 = car.children[car.get("wheelsname", "Wheel")+".001"] + bge.logic.globalDict[car["cid"]]["wheels"].append(wheel1) + wheel1.removeParent() + vehicle.addWheel(wheel1, + [-car.get("wheelswidth", 0.89), car.get("wheelsback", 1.65), car.get("wheelsdown", -0.1)], # Pos Local + [0, 0, -1], # Dir Local + [-1, 0, 0], # Axl Local + 0.2, # Suspention length + car.get("wheelradius", 0.42), # Wheel Radius + 1 + ) + + # LEFT FRONT + wheel2 = car.children[car.get("wheelsname", "Wheel")+".002"] + bge.logic.globalDict[car["cid"]]["wheels"].append(wheel2) + wheel2.removeParent() + vehicle.addWheel(wheel2, + [car.get("wheelswidth", 0.89), car.get("wheelsfront", -1.25), car.get("wheelsdown", -0.1)], # Pos Local + [0, 0, -1], # Dir Local + [-1, 0, 0], # Axl Local + 0.2, # Suspention length + car.get("wheelradius", 0.42), # Wheel Radius + 1 + ) + + # RIGHT FRONT + wheel3 = car.children[car.get("wheelsname", "Wheel")+".003"] + bge.logic.globalDict[car["cid"]]["wheels"].append(wheel3) + wheel3.removeParent() + vehicle.addWheel(wheel3, + [-car.get("wheelswidth", 0.89), car.get("wheelsfront", -1.25), car.get("wheelsdown", -0.1)], # Pos Local + [0, 0, -1], # Dir Local + [-1, 0, 0], # Axl Local + 0.2, # Suspention length + car.get("wheelradius", 0.42), # Wheel Radius + 1 + ) + + # Setting up wheel physics characteristics. + # ( NOTE: ) This is not the only place where those are set. + # Damage and Rescue functions update those values. + + car["suspensions"] = [] + + for i in range(4): + vehicle.setSuspensionStiffness(car.get("suspension", 200), i) + car["suspensions"].append(car.get("suspension", 200)) + vehicle.setTyreFriction(car.get("friction", 1), i) + vehicle.setRollInfluence(car.get("roll", 0), i) + + + + + ##### SETTING UP THE DAMAGE MODEL ##### + + + def collision_callback(object, point, normal): + + # This function will run whenever the car colides with anything. + + # Dani object ( main human character ) + dani = scene.objects["Dani_Box"] + + toFPS = Opt.ToFPS() + + # Near misses - when the player drives quickly + # near a different vehicle. If colision happens, + # near misses are canceled. + car["veryclose"] = None + + + # If anywhere within the proximity of the car, + # an explosion was set off ( car colided with the shockwave sphere ). + # we want to setup a timer, to blow car up in a few ticks after that. + # Doing it immediately makes the second explosion almost inconciaveable. + if object.get("explosion"): + car["blowuptimer"] = 20 + + # Canceling the rest of the calculation if we can't see it, or it is too far from the camera. + cam = scene.active_camera + if car.getDistanceTo("Dani_Box") > 20 or cam.pointInsideFrustum(car.worldPosition) == cam.OUTSIDE: + return + + # If human pushes the car, there is no damage + if dani == object: + return + + # Separation is an invisible object that bots see to navigate. + # We don't want it to cause damage. + if "Separation" in object.name: + return + + #### WATER INTERACTIONS #### + + if object.name == "Water": + + car["underwater"] = True + if car["active"]: + dani["underwater"] = True + + # Sound + device = bge.logic.globalDict["SoundDevice"] + s = "//sfx/splash.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + sound["play"].location = point + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + + + return + + # Returning if performance is not good + if not Opt.GoodFPS("Car Collisions", 0.9) and not car.get("active"): + return + + # Velocity of the car + cv = car.getLinearVelocity() + + # Averagin the values + av = sum(cv) / 3 + if av < 0: av *= -1 + + av /= (toFPS ** 2) + + # Setting off sparks if the velocity is beyond a certain amount + if av > 0.1: + Distraction.particles("Sparkle", point, + despawn = random.randint(30, 100), + amount = random.randint(0, 3), + max = 50, + spread = 0, + normal = 0.04, + velocity = cv * 0.015) + + # Restricting all the rest of the calculations based on a timer + # between the colisions. + if car.get("lastdamage"): + return + car["lastdamage"] = random.randint(10, 60) + + + # Adding nitro on colision ( based on the speed of the car ) + car["nitro"] += av*20 + + + + #### ENEMY VOICES #### + + # NPC cars might get angry and start attacking you if you hit them. + # When they get angry they swear at you. This is the logic of the + # swearing sounds. And the getting angry logic. + + if object.get("engine") and car.get("npc") and car.get("npc") != "racer" and object.get("enemy") != car: + + device = bge.logic.globalDict["SoundDevice"] + + if not car["npc"] == "enemy": + + sound = random.choice(bge.logic.globalDict["sounds_angry_start"]) + sound = bge.logic.globalDict["sounds"][sound] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.position + sound["play"].relative = False + sound["play"].distance_maximum = 50 + sound["play"].distance_reference = 5 + sound["play"].attenuation = 1 + sound["play"].volume = 1.5 + + + car["enemysound"] = sound["play"] + + if object.get("active"): + scene.objects["Angry_pusuit"]["pusuit"] = True + if not dani.get("active_sound"): + dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_ohno"]["sound"]) + bge.logic.globalDict["sounds"]["dani_ohno"]["play"] = dani["active_sound"] + dani["active_sound_is"] = "dani_ohno" + + elif car["enemy"] == object and ( not car.get("enemysound") or not car["enemysound"].status ): + + sound = random.choice(bge.logic.globalDict["sounds_angry_hit"]) + sound = bge.logic.globalDict["sounds"][sound] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.position + sound["play"].relative = False + sound["play"].distance_maximum = 50 + sound["play"].distance_reference = 5 + sound["play"].attenuation = 1 + sound["play"].volume = 1.5 + + car["enemysound"] = sound["play"] + + if object.get("active") and not dani.get("active_sound_is") == "dani_ohnoindeed" and not dani.get("active_sound"): + dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_ohnoindeed"]["sound"]) + bge.logic.globalDict["sounds"]["dani_ohnoindeed"]["play"] = dani["active_sound"] + dani["active_sound_is"] = "dani_ohnoindeed" + + # Getting angry + + car["npc"] = "enemy" + car["enemy"] = object + object["chased"] = True + + + + + # Getting car's body. + rig = car.children[car.get("rig","RIG")] + body = rig.children["Car_body"] + + + # Getting the velocity of the colision ( comparing to the valocity of the object we are coliding with ) + ov = object.getLinearVelocity() + ov /= (toFPS ** 2) + sp = cv - ov + + # We can't simply average the velocity since it could be negative. + sm = 0 + for i in sp: + if i > 0: + sm += i + else: + sm += i*-1 + sp = sm / 3 + + # Adding the mass of both objects to the calculation + obmass = object.mass + if not obmass: obmass = 1 + breakMultiply = max(0, min(1, 1-(sp * obmass)/55)) ** 0.05 + + breakMultiply *= (toFPS ** 2) + + # Blow up when getting a 0 breakMultiply + if not breakMultiply: + car["blowuptimer"] = 10 + + + ####### BREAKING THE CAR ####### + + if breakMultiply < 0.999 or object.get("explosion"): + + + # Hit sound + device = bge.logic.globalDict["SoundDevice"] + + # Hit versus scrape sound + if breakMultiply < 0.98: + s = "//sfx/hit.ogg" + else: + s = "//sfx/scrape.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + sound["play"] = device.play(sound["sound"]) + + sound["play"].location = point + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.choice([1, 0.8, 1.2, 0.6, 1.4]) + sound["play"].volume = 1 / max(0.1, breakMultiply ) + + + # slecting what area of the car to effect + areas = {"Front_Left" : normal[0] < 0.1 and normal[1] < 0.1, + "Front_Right": normal[0] < 0.1 and normal[1] > -0.1, + "Back_Left" : normal[0] > -0.1 and normal[1] < 0.1, + "Back_Right" : normal[0] > -0.1 and normal[1] < 0.1} + + wheels = {"Front_Left" : 2, + "Front_Right": 3, + "Back_Left" : 0, + "Back_Right" : 1} + + for area in areas: + if ( areas[area] and car[area] > 0 ) or object.get("explosion"): + + # Reducing health + car[area] *= breakMultiply + + # If broken toally we pop a wheel + if car[area] <= 0.01: + + # We can't simply undo the wheel from the constraint. But + # we can do the next best thing. + bge.logic.globalDict[car["cid"]]["wheels"][wheels[area]].setVisible(False) + vehicle.applyBraking(10, wheels[area]) + vehicle.setSuspensionStiffness(0,wheels[area]) + + # If it is an npc, we make him give up trying to drive. + if not car.get("racing"): + car["npc"] = "" + + # Tire Pop + sound = bge.logic.globalDict["sounds"]["tire_pop"] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = point + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].volume = 2 + + # If we are not totally broken, make the wheel worse handeling-wise. + elif car["Front_Left"] < 0.7: + vehicle.setSuspensionStiffness(car.get("suspension", 200)*car["Front_Left"],wheels[area]) + vehicle.setRollInfluence(1-car[area], wheels[area]) + + # If below a certain health, we make the car body look borked + if car[area] < 0.7: + body.replaceMesh(car.get("borked", "Borked")) + + # If the heal even lower. + if car[area] < 0.3: + + # Removing left door. + if "Left" in area: + try: + door = rig.children["Door_Pease.001"] + door.visible = False + except: + pass + + # Removing right door. + elif "Right" in area: + + try: + door = rig.children["Door_Pease"] + door.visible = False + except: + pass + + + # Adding the collision function to the car's collision functions. + car.collisionCallbacks.append(collision_callback) + +def update(): + + # This function is called on ever game logic tick for every car + # in the scene. This is the logic of the car. + + # BGE logic stuff + cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + car = cont.owner + + toFPS = Opt.ToFPS() + + carRacing = car.get("racing") + carEnemy = car.get("enemy") + carBehaviour = car.get("npc") + carActive = car.get("active") + carSpeed = car.get("speed") + + rig = car.children[car.get("rig","RIG")] + + cam = scene.active_camera + camtarget = car.children["CarCamTarget"] + + # Is the car in view + distanceToCam = car.getDistanceTo(cam) + inview = ( cam.pointInsideFrustum(car.worldPosition) == cam.INSIDE and distanceToCam < deleteAtDistance / 2 ) or distanceToCam < deleteAtDistance / 3 or distanceToCam > deleteAtDistance + + # Dani related things + dani = scene.objects["Dani_Box"] + + # Delete vehicle ( at distance, usually done for NPC cars ). + + if car.getDistanceTo(cam) > deleteAtDistance and not carRacing and car in bge.logic.globalDict["allcars"]: + + def RemovingTheCar(car, dani): + + # If car is waiting for a race to start, we don't delete it + if carBehaviour == "racer": + racename = car["race"] + race = bge.logic.globalDict["races"][racename] + starter = race["starters"][0] + if dani.getDistanceTo(starter["location"]) < deleteAtDistance or car["racing"]: + return + + + print("Removing Car:", car) + + # Making sure it can be respawned + if car.get("spawnPoint"): + car["spawnPoint"]["to_spawn"] = True + + # Removing the car from races + for racename in bge.logic.globalDict["races"]: + race = bge.logic.globalDict["races"][racename] + try: + race["racers"].remove(car) + except: + pass + if car in bge.logic.globalDict["allcars"]: + bge.logic.globalDict["allcars"].remove(car) + + # Removing from cache if too many cars in cache + if Reuse.reuse.get(car.name):# or len(bge.logic.globalDict["allcars"]) > bge.logic.globalDict["maxCars"]: + for i in bge.logic.globalDict[car["cid"]]["wheels"]: + i.endObject() + Reuse.EndObject(car) + # Otherwise storing it for later. + else: + Reuse.Delete(car, inactive=True) + + + + # Undoing angry pursuit + try: + if car["enemy"]["active"]: + scene.objects["Angry_pusuit"]["pusuit"] = False + except: + pass + + if Reuse.reuse.get(car.name) or len(bge.logic.globalDict["allcars"]) > bge.logic.globalDict["maxCars"]: + Opt.ScheduleTask("Removing Car ["+car.name+"]", 1.0, RemovingTheCar, car, dani) + else: RemovingTheCar(car, dani) + + + + # Cutting down on unnesesary running of the script + if ( not carRacing and not carActive ) and ( not inview ): + car["deactivated"] = True + car.suspendPhysics() + return + elif car.get("deactivated"): + car.restorePhysics() + car["deactivated"] = False + + + + # Physics constraint for the vehicle + vehicle = bge.constraints.getVehicleConstraint(car["cid"]) + + # Sound device for sounds. + device = bge.logic.globalDict["SoundDevice"] + + # Car's health + health = sum([car["Front_Left"], car["Front_Right"], car["Back_Left"], car["Back_Right"]])/4 + engineHealth = ( car["Front_Left"] + car["Front_Right"] ) / 2 + car["health"] = health + + # Fixing the car ( when in the garage) + if distanceToCam < 30 and car.getDistanceTo(fixGaragePosition) < 2 and health < 1: + bge.logic.globalDict["print"] = "Press F to fix the car." + + # Fix car if F is pressed + if 28 in bge.logic.globalDict["keys"]: + body = rig.children["Car_body"] + body.replaceMesh( body["good"] ) + for i in range(4): + vehicle.setSuspensionStiffness(car.get("suspension", 200), i) + vehicle.applyBraking(0, i) + vehicle.setTyreFriction(car.get("friction", 1), i) + vehicle.setRollInfluence(car.get("roll", 0), i) + bge.logic.globalDict[car["cid"]]["wheels"][i].setVisible(True) + for i in ["Front_Right", "Front_Left", "Back_Left", "Back_Right"]: + car[i] = 1 + + # Doors ( some cars have them falling ) + try: + rig.children["Door_Pease.001"].visible = True + rig.children["Door_Pease"].visible = True + except: + pass + + elif carActive and health < 0.98: + toprint = "You can fix the car in the garage at home." + if toprint not in bge.logic.globalDict["done-prints"]: + bge.logic.globalDict["print"] = toprint + + # Whether the car is on graound. + graypos = car.position.copy() + graypos[2] -= 1.5 + onGround = car.rayCastTo(graypos) + + + # Car's rotation + rot = car.localOrientation.to_euler() + upsideDown = not ( -2.5 < rot[0] < 2.5 and -2.5 < rot[1] < 2.5 ) + + + # Colision timer + if car.get("lastdamage"): + car["lastdamage"] -= 1 + + # Lauchtime timer + if "launchtime" not in car: + car["launchtime"] = 300 + car["launchtime"] -= 1 + + # Water boyancy + if car.position[2] < -9.5 and car.get("underwater"): + car.applyForce([0,0,-5*(car.position[2]+9.5)]) + + if car["speed"] < -1.5: + car.applyForce([0,-80,0], True) + car.orientation = [0,0,rot[2]] + + elif car.position[2] > -9.5: + car["underwater"] = False + + # Speed calculation + psp = vehicle.getWheelRotation(0)+vehicle.getWheelRotation(1) + sp = psp - car["prevwheelrot"] + car["prevwheelrot"] = psp + car["speed"] = sp + + # sp will be speed normalized + if sp < 0: sp *= -1 + + # Down Force + dforce = -5*sp*car.get("downforce", 1) + if onGround: + dforce = min(-5, dforce) + car.applyForce([0,0, Opt.Force(dforce)], True) + + + + # Gage + if carActive: + gagerotation = sp * 0.214 # 400 km/h == 4.27 radiance on the gage + gage = scene.objects["Gage_Arrow"] + gage.orientation = [0,0,-gagerotation] + + gage_health = scene.objects["Gage_Health"] + gage_health["Health"] = int(( (health ** 2 ** 3) / (1 ** 2 ** 3 ) )*100) + + + + + + + ###### NITRO ###### + + # Making sure that nitro isn't overboard. + car["nitro"] = max(0,min(10000, car.get("nitro", 0.0))) + if carActive: + gage_nitro = scene.objects["Gage_Nitro"] + gage_nitro["Nitro"] = car["nitro"]/100 + + + # Function activated when Nitro is used + def nitro(): + + if car["nitro"] <= 0: + stopNitro() + return + + + # Actual Nitro Effect on the car + #if car["engine"] < car.get("maxengine", 2)-0.1: + car["engine"] += car.get("maxaccel",0.003)* 5 * toFPS + #else: + # car["engine"] = car.get("maxengine", 2)*1.5 + car["nitro"] -= 20 + + # Anti bank + if car.get("roll",0): + car.applyRotation([0.05*car.get("roll",0),0,0], True) + car.applyForce([0,0, -20*car.get("roll",0)], True) + + + # Nitro Sound + s = "//sfx/nitro.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.position + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = 1 + sound["play"].volume = 5 + + car["nitroing"] = True + + if not carActive and not Opt.GoodFPS("Nitro", 0.9): + return + + for nitroObject in car.children: + if "nitro" in nitroObject.name: + if not nitroObject.get("nitroCone"): + nitroCone = Reuse.Create("NitroCone") + nitroObject["nitroCone"] = nitroCone + nitroCone.position = nitroObject.worldPosition + nitroCone.orientation = nitroObject.worldOrientation + nitroCone.setParent(nitroObject) + + # Camera effect + if carActive: + camtarget = car.children["CarCamTarget"] + if not car.get("zoom"): + car["zoom"] = camtarget.scaling[0] + camtarget.scaling *= 1.02 + + # Unction activated when nitro isn't used. + def stopNitro(): + + # Grpaphics stop + + for nitroObject in car.children: + if "nitro" in nitroObject.name: + if nitroObject.get("nitroCone"): + nitroObject["nitroCone"].removeParent() + Reuse.Delete(nitroObject["nitroCone"]) + nitroObject["nitroCone"] = None + + # Sound stop + s = "//sfx/nitro.ogg" + code = s+str(car["cid"]) + if code in bge.logic.globalDict["sounds"]: + sound = bge.logic.globalDict["sounds"][code] + try: + if sound["play"].volume > 0: + sound["play"].volume -= 0.5 + else: + sound["play"].stop() + except: + pass + + # Activating nitro + if carActive and 48 in bge.logic.globalDict["keys"]: + # 48 is Z + nitro() + else: + stopNitro() + + if not onGround: + car["nitro"] += 5 + + # Telling the player that the car is availble + if dani.get("doesntknowhowtositintocars", True) and not dani["driving"] and distanceToCam < 7: + bge.logic.globalDict["print"] = "Press E to sit into the car." + dani["doesntknowhowtositintocars"] = False + + # Camera changes ( help camera show you where you are going ). + + if carActive and not upsideDown and sp > 0.1: + + dontseecar = cam.rayCastTo(car) != car + + if dontseecar and camtarget.scaling[0] > 0.1: + if not car.get("zoom"): + car["zoom"] = camtarget.scaling[0] + camtarget.scaling *= 0.97 + + elif car.get("zoom") and scene.objects["SecondaryCamSensor"].rayCastTo(car) == car: + + newzoom = camtarget.scaling[0] + (( car["zoom"] - camtarget.scaling[0] ) / ( 30 / toFPS ) ) + camtarget.scaling = [ newzoom, newzoom, newzoom ] + if camtarget.scaling[0] == car["zoom"]: + car["zoom"] = False + + + camrot = mathutils.Vector(camtarget.localOrientation.to_euler()) + + reference = car.getVectTo(car.position + car.getLinearVelocity())[2] + rotref = car.localAngularVelocity + + backorfoth = ((reference[1]+1)/2) + if backorfoth > 0.6: backorforth = 1 + else: backorforth = 0 + + if camtarget["back"] and backorforth: backorforth = 0 + elif camtarget["back"]: backorforth = 1 + + camnewrot = mathutils.Vector( [ 0.3 - max(-0.1, min(0.2, rotref[0]/2)),# - (car["engine"]*0.005), + 0 - (rotref[2]/10) - (rotref[1]/6), + ( math.pi * backorforth ) - (rotref[2]/4)] ) + + if backorforth and camrot[2] < 0: + camnewrot[2] -= math.pi * 2 + + camnewrot = mathutils.Vector(camnewrot) + camrot = camrot + (( camnewrot - camrot ) / 10 ) + camtarget.localOrientation = camrot + + elif carActive and cam.rayCastTo(car) != car: + camrot = camtarget.localOrientation.to_euler() + camrot[0] -= 0.02 + #camrot[2] += 0.01 + camtarget.localOrientation= camrot + + + # Smoke ( if health too low ) + if not car["underwater"] and inview: + + smokeCoefitient = max(0, 1-engineHealth*1.1)*0.2 + fireCoefitient = max(0, 1-engineHealth*2)*0.2 + smokeemiter = car.children["SmokeEmmiter"] + Distraction.Smoke(smokeemiter.position, smokeCoefitient) + Distraction.Fire(smokeemiter.position, fireCoefitient) + + + # Exposion + + if engineHealth < 0.2 and not car.get("blown"): + boompos = smokeemiter.position.copy() + boompos[2] -= 2 + Distraction.Explosion(boompos, mass=0.5, size=2) + car["blown"] = True + + # Blowuptimer + if car.get("blowuptimer") != None: + if car["blowuptimer"] == 0 and not car.get("blown"): + Distraction.Explosion(car.children["SmokeEmmiter"].position.copy(), mass=0.5, size=2) + car["blown"] = True + else: + car["blowuptimer"] -= 1 + + + ######### SOUND ######## + + + if carActive: + + # Dani yelling when in air + + if not onGround and not car["underwater"] and not dani.get("active_sound"): + dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_yelling"]["sound"]) + bge.logic.globalDict["sounds"]["dani_yelling"]["play"] = dani["active_sound"] + dani["active_sound_is"] = "dani_yelling" + elif onGround and dani["active_sound"] and dani["active_sound_is"] == "dani_yelling": + dani["active_sound"].stop() + dani["active_sound"] = None + + # Sinking + + if car.get("underwater") and not dani.get("active_sound") and not dani.get("active_sound_is") == "dani_wearesinking" and dani.get("sinking_timer", 0) < time.time(): + dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_wearesinking"]["sound"]) + bge.logic.globalDict["sounds"]["dani_wearesinking"]["play"] = dani["active_sound"] + dani["active_sound_is"] = "dani_wearesinking" + dani["sinking_timer"] = time.time() + 40 + + # Very close to another car + + + try: + for i in bge.logic.globalDict["allcars"]: + if i != car and i.getDistanceTo(car) < 6: + car["veryclose"] = i + break + except: + pass + + + if sp < 0.3: + car["veryclose"] = None + + elif car.get("veryclose") and car["veryclose"].getDistanceTo(car) > 7: + if not dani.get("active_sound"): + dani["active_sound"] = device.play(bge.logic.globalDict["sounds"]["dani_ohgod"]["sound"]) + bge.logic.globalDict["sounds"]["dani_ohgod"]["play"] = dani["active_sound"] + dani["active_sound_is"] = "dani_ohgod" + car["nitro"] += 200 + car["veryclose"] = None + + if not dani.get("active_sound") or not dani["active_sound"].status: + dani["active_sound"] = None + + # Engine sound + + s = car.get("engine_sound","//sfx/engines/neonspeedster.ogg") + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + esp = car["engine"] + if esp < 0: esp *= -1 + esp = math.log1p(esp) * 5 + + if 0.01 < esp or car.get("engineStarted"): + + car["engineStarted"] = True + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + + + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + + + + revs = ( esp + ( esp % 1.5 ) ) * 0.5 + + sound["play"].pitch = max(0.4, revs ) + sound["play"].volume = engineHealth * 1 + + if carActive: + scene.objects["Gage_Tachometer"]["Health"] = revs * 30 + + else: + + try: + bge.logic.globalDict["sounds"][code]["play"].stop() + except: + pass + + + + # Grind sound + + s = "//sfx/grind.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + if 0.01 < sp: + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + + + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = max(0.3, engineHealth) * 2 + sound["play"].volume = (1-engineHealth) * min(1.5, sp *8) * 1 + + else: + + try: + bge.logic.globalDict["sounds"][code]["play"].stop() + except: + pass + + + + + # Drift sound + + s = "//sfx/drift.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + slide = car.localLinearVelocity[0] + if car.get("deccelerating"): + slide = car.localLinearVelocity[1] + if slide < 0: + slide *= -1 + + sound = bge.logic.globalDict["sounds"][code] + if slide > 1 and -0.5 < rot[0] < 0.5 and -0.5 < rot[1] < 0.5 and onGround: + + car["nitro"] += 2 + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + #sound["play"].pitch = 0.8 + sound["play"].volume = min(2, slide / 5) / 5 + + else: + + try: + bge.logic.globalDict["sounds"][code]["play"].stop() + except: + pass + + + #### RACE RESCUE FUNCTION #### + + def rescue(): + + if car.get("launchtime", 0) > 0: + return + + # Sparkles on disappearing positiong + Distraction.particles("Sparkle", car.position.copy(), + despawn = random.randint(30, 100), + amount = 30, + max = 30, + spread = 2, + normal = 0.05, + scale = 7) + + # Camera + if carActive: + def resetCarZoom(): + car["zoom"] = False + Character_Controll.ChangeCameraTarget(car.children["CarCamTarget"], 100, [3,3,3],callable=resetCarZoom) + car.children["CarCamTarget"].orientation = [0.2,0,0] + + # Teleportation + car.position = car["rescue"] + car.localLinearVelocity = [0,0,0] + + + car["checkpoint"] = ( car.get("rescueTo", car["checkpoint"]-1) + 1 ) % len(bge.logic.globalDict["races"][car.get("race")]["checkpoints"]) + nextcheckpointn = car["checkpoint"] + nextcheckpoint = bge.logic.globalDict["races"][car.get("race")]["checkpoints"][ nextcheckpointn ]["location"] + + prevcheckpoint = ( car["checkpoint"]-2 ) % len(bge.logic.globalDict["races"][car.get("race")]["checkpoints"]) + car["prevrescue"] = bge.logic.globalDict["races"][car.get("race")]["checkpoints"][ prevcheckpoint ]["location"] + + print("Rescue at: Car:", car, "Checkpoint:", car["checkpoint"]) + + # Sparkles on the new position + Distraction.particles("Sparkle", car.position, + despawn = random.randint(30, 100), + amount = 50, + max = 100, + spread = 2, + normal = 0.05, + scale = 5) + + + + # Rotation the car based on the locations of checkpoints. + tocheck = car.getVectTo(nextcheckpoint) + car.alignAxisToVect(tocheck[1], 1, 1.0) + car.alignAxisToVect( (0,0,1), 2, 1.0 ) + + # Rotating the car 180 degrees since forward it -Y not Y. + rot = car.worldOrientation.to_euler() + car.worldOrientation = [rot[0], rot[1], rot[2]+math.pi] + + # Adding the rotation of the previous checkpoint. +# if car.get("prevrescue"): +# tocheck = car.getVectTo(car.get("prevrescue")) +# car.alignAxisToVect(tocheck[1], 1, 0.7) +# car.alignAxisToVect( (0,0,1), 2, 1.0 ) + + # Putting the car a bit higher than the road. + car.position[2] += 2 + car["launchtime"] = 200 + car["nitro"] = max(1000, car["nitro"]) + car["underwater"] = False + car["engine"] = 0 + + # Restoring the wheels. + for i in range(4): + #vehicle.setSuspensionStiffness(car.get("suspension", 200), i) + car["suspensions"][i] = car.get("suspension", 200) + vehicle.applyBraking(0, i) + vehicle.setTyreFriction(car.get("friction", 1), i) + vehicle.setRollInfluence(car.get("roll", 0), i) + bge.logic.globalDict[car["cid"]]["wheels"][i].setVisible(True) + for i in ["Front_Right", "Front_Left", "Back_Left", "Back_Right"]: + car[i] = max(0.8, car[i]) + + + # Rescuing the active car in the race when pressing Backspace ( code 59 ) + if carActive and dani.get("race") and dani.get("rescue") and ( ( car["speed"] > -0.02 and car["engine"] > 0.5 ) or engineHealth <= 0.2 or car["underwater"]): + #if carActive and dani.get("race") and dani.get("rescue") and 59 in bge.logic.globalDict["keys"] and car["launchtime"] <=0: + + if not car.get("toresquetimer", 50): + + nextcheckpoint = bge.logic.globalDict["races"][dani.get("race")]["checkpoints"][ ( dani.get("checkpoint") + 1 ) % len(bge.logic.globalDict["races"][dani.get("race")]["checkpoints"]) ]["location"] + + car["rescue"] = dani["rescue"] + car["race"] = dani["race"] + car["checkpoint"] = dani["checkpoint"] + car["rescueTo"] = dani["rescueTo"] + car["prevrescue"] = dani["prevrescue"] + rescue() + + car["toresquetimer"] = 50 + else: + car["toresquetimer"] = car.get("toresquetimer", 50) -1 + + bge.logic.globalDict["cheat"] = [0] + + + ###### NPC LOGIC ####### + + if car.get("npc") and bge.logic.globalDict["derby-cheat"]: + + debryclosest = None + derbydistance = 1000 + for i in scene.objects: + if "speed" in i and i != car: + idist = car.getDistanceTo(i) + if idist < derbydistance and car.rayCastTo(i.position) == i: + derbydistance = idist + debryclosest = i + + if derbydistance < 10: + break + + if debryclosest: + car["npc"] = "enemy" + car["enemy"] = debryclosest + + + ## NPC PREPARATIONS ## + + if car.get("npc"): + + #### CAR VISION OF THE ROAD AND OBSTACLES #### + + # To make the vision work we are going to use Ray Casting + # but instead of doing a whole path tracing thing to see + # everything in front of the car ( which you be slow ) + # instead we trace only 5 specifically chosen points. + + # Each car model has those 5 points positioned by hand + # in respecive car blend files. Those 5 points are. + + left = 50 # To the forward - left diagonal \ + # } On the height of the car + right = 50 # To the forward - right diagonal / + + leftf = 50 # To the forward - left diagonal \ + # } Slightly lower than the car ( to scan road ) + rightf = 50 # To the forward - right diagonal / + + forward = 50 # Directly in front of the car. + + + forwardData = None # Data of the ray casting for the forward. + leftm = "" # Material on the left of the car + rightm = "" # Material on the right of the car + + # Each ray has it's default value be 50. During ray casting, the value will + # change to the distance of whatever the car "sees". If it sees nothing, the + # values will stay 50. + + # Few more things to consider. + road = "Track2" # Material of the road ( to ignore ) + separator = "Track_light" # Material of the separator line ( drawn in the middle of the road ). + separator_object = "Separation" # Name of an invisible object sticking out of this separator line. + + + # Left, slightly lower ray + ray = car.rayCast(car.children["Left"], car, poly=True) + + # rayCast returns a list of values [ Hit Object , Hit Location, Hit Angle, Hit Polygon ] + + # If we have any hit object ( exluding the road separator line if the car is a racing npc ) + if ray[1] and ( not carRacing or not str(ray[0]) == separator_object ): + + # Excluding if the Polygon's material is road material + if not ray[3] or road not in str(ray[3].material): + + # Getting the distance to the hit location and storing it as both + left = car.getDistanceTo(ray[1]) # left and + leftf = left # leftf + try: leftm = str(ray[3].material)# And storing the material name for later + except: pass + + # Doing something similar to the left point on the same heigh as the car + ray = car.rayCast(car.children["Left.001"],car, poly=True) + if ray[1] and ( not carRacing or not str(ray[0]) == separator_object ): + if not ray[3] or road not in str(ray[3].material): + + # Only instead of doing the complex appending, we only + # overwrite the left variable, if the distance is lower. + # Basically left will end up the closest visible point from + # the left of the car. And leftf will be the original left. + + left = min(car.getDistanceTo(ray[1]), left) + + + # Doing something similar for the right side as well + ray = car.rayCast(car.children["Right"],car, poly=True) + + # Only now we exclude the separator line regardless if it + # is a racer or not. So if the car ends up on the wrong + # side of the road and goes British, it could find its way + # back to the right side of the road. + + if ray[1] and not str(ray[0]) == separator_object: + if not ray[3] or road not in str(ray[3].material) : + right = car.getDistanceTo(ray[1]) + rightf = right + try: rightm = str(ray[3].material) + except: pass + + # And we slightly enfoce it to go to the right side of + # the road. By folling it to think that there is something + # of the left. + + elif not carRacing and str(ray[0]) == separator_object: + left = min(car.getDistanceTo(ray[1]), left) + + # And now for the slightly higher point on the right. + ray = car.rayCast(car.children["Right.001"],car, poly=True) + if ray[1] and not str(ray[0]) == separator_object: + if not ray[3] or road not in str(ray[3].material): + right = min(car.getDistanceTo(ray[1]), right) + elif not carRacing and str(ray[0]) == separator_object: + left = min(car.getDistanceTo(ray[1]), left) + + # And for the forward. + ray = car.rayCast(car.children["Forward"],car, poly=True) + forwardData = ray + if ray[1] and str(ray[0]) == separator_object: + if not ray[3] or road not in str(ray[3].material): + forward = car.getDistanceTo(ray[1]) + + # Minimum value will be the smallest distance out of all of them + # which is useful for all sorts of things. + minimum = min(left, right, forward) + + # And now we calculate how much the car needs to actually turn. + turn = max(-0.8, min(0.8, (( max(0, min(1, (right/12))) - max(0, min(1, (left/12))) ) *-2 ))) + + ## NORMAL NPC CARS ## + + if car.get("npc") == "npc": + + # Normal NPC cars are those you can see driving around the city + # at a regular speed. + + + # Activate enemy logic when pursuit cheat is enabled + + if distanceToCam < 100 and bge.logic.globalDict["pursuit-cheat"]: + car["npc"] = "enemy" + for i in scene.objects: + if i.get("active"): + car["enemy"] = i + break + + # Applying the turn calculated in vision step + car["turn"] = turn + + + # Accelerating the car, with the target speed based on distance + # to the closest seen point. + + targetSpeed = 4 * ((minimum/12)-0.4) + + # We also ignore target speed, and make the car drive as fast + # as it can, when it is chased by somebody. + + if (sp < targetSpeed or car.get("chased")) and minimum > 2: + accelerate(car) + else: + deccelerate(car) + + # To help it avoid obstacles better. + if minimum < 2 and car.get("accelerating"): + car["turn"] = -0.8 + + # If we about to hit a car head first + if forwardData[0] and forwardData[0].get("speed"): + turn = max(-0.8, min(0.8, turn*10)) + car["engine"] = car["speed"]*10 + + # Cliffs +# turnback = True +# if rot[0] > 0.1 and not car.get("chased"): +# accelerate(car) +# elif rot[0] < -0.9 and sp < 0.9 and onGround: +# car.applyForce([0,-20, 0], True) +# turnback = False +# +# +# + +# if minimum < 5: +# deccelerate(car) +# if 0.9 < turn or turn < -0.9: +# car["turn"] = turn *-1 +# car.applyTorque([0.0,0.0,turn*-30],1) +# +# elif turnback: +# car["turn"] = 0.3 +# car.applyTorque([0.0,0.0,car["turn"]*30],1) +# +# if "launchtime" not in car: +# car["launchtime"] = 300 +# car["launchtime"] -= 1 +# if 0.03 > sp and ( car["engine"] / max(0.1, engineHealth) > 1 ) and car["launchtime"] < 0: +# if not rot[0] < -0.1 and (leftm or rightm): +# car.applyTorque([0.0,0.0,50],1) +# car.position[2] += 0.05 +# +# +# if sp > 2.8 and (0.3 < turn or turn < -0.3): +# for i in range(4): +# vehicle.applyEngineForce(car["speed"]*5* toFPS, i) +# if sp < 0: sp *= -1 +# car.applyTorque([0.0,0.0,car["turn"]*sp*5* toFPS],1) +# + + + #for i in range(4): + # vehicle.applyEngineForce(car["engine"], i) + + ## ANGRY CARS GUIDED TO A POINT ## + + elif car.get("npc") == "enemy" or ( car.get("npc") == "racer" and car.get("racing")): + + # If racing + if car.get("npc") == "racer": + + + checkpoint = bge.logic.globalDict["races"][car.get("race")]["checkpoints"][car.get("checkpoint")].copy() + if "raceData" in bge.logic.globalDict["races"][car.get("race")]: + raceData = bge.logic.globalDict["races"][car.get("race")]["raceData"] + targetTime = raceData["times"][car["checkpoint"]] + try: prevTime = raceData["times"][car["checkpoint"]-1] + except: prevTime = 0 + lastCheckPointTime = car.get("last-checkpoint-time", bge.logic.globalDict["races"][car.get("race")]["start-time"]) + currentTime = bge.logic.getRealTime() - bge.logic.globalDict["races"][car.get("race")]["start-time"] + + targetTimeToCheckpoint = targetTime - prevTime + currentTimeToCheckpoint = currentTime - lastCheckPointTime + else: + targetTimeToCheckpoint = 1 + currentTimeToCheckpoint = 1 + + car["enemy"] = checkpoint["location"] + + # If the racing bot is following the player, it will mimic the player + #racePositions = bge.logic.globalDict["races"][car.get("race")].get("positions", [dani, car]) + #if dani in racePositions and dani.get("driving") != car and racePositions.index(dani) < racePositions.index(car): + # car["enemy"] = dani["driving"] + + # If reached checkpoint + if car.getDistanceTo(car["enemy"]) < checkpoint["radius"]: + if not checkpoint.get("OnLoop"): + car["prevrescue"] = car["rescue"] + car["rescue"] = checkpoint["location"] + car["rescueTo"] = car["checkpoint"] + + # Printing if the bot is on time + if "raceData" in bge.logic.globalDict["races"][car.get("race")]: + car["last-checkpoint-time"] = currentTime + + + print("Race Time of [", car, "]:", round(currentTimeToCheckpoint, 2), round(targetTimeToCheckpoint - currentTimeToCheckpoint, 2)) + + # Calibrating the stopping distance + if checkpoint.get("Uturn"): + targetSpeed = -checkpoint.get("Uturn") * toFPS + car["stopdistance"] = car.get("stopdistance", -18) * ( car["speed"] / targetSpeed ) + print("Race Stopping Calibration [", car, "]:", car.get("stopdistance", -18), int( ( car["speed"] / targetSpeed ) * 100), "%") + + car["checkpoint"] = car["checkpoint"]+1 + car["launchtime"] = 50 + + try: + car["distanceBetweenCheckpoints"] = car.getDistanceTo(bge.logic.globalDict["races"][car.get("race")]["checkpoints"][car.get("checkpoint")]["location"]) + except: + pass + + # If finished lap + if car.get("checkpoint") == len(bge.logic.globalDict["races"][car.get("race")]["checkpoints"]): + print(car, "LAP") + car["checkpoint"] = 0 + car["lap"] += 1 + + # If finished race + if car["lap"] == bge.logic.globalDict["races"][car.get("race")]["laps"]: + car["racing"] = False + car["npc"] = "" + bge.logic.globalDict["print"] = raceNames.get(car.name, car.name) + " finished race!" + + # Making rescuing less sensitive when passing a Uturn + if bge.logic.globalDict["races"][car.get("race")]["checkpoints"][car.get("checkpoint")].get("Uturn"): + car["launchtime"] = 500 + + nextcheckpoint = bge.logic.globalDict["races"][car.get("race")]["checkpoints"][ ( car.get("checkpoint") + 1 ) % len(bge.logic.globalDict["races"][car.get("race")]["checkpoints"]) ]["location"] + tonextcheckpoint = car.getVectTo(nextcheckpoint) + + # Moving the indicator + car["beam"].position = car.position + car["beam"].position[2] += 1 + tocam = car["beam"].getVectTo(cam) + car["beam"].alignAxisToVect(tocam[1], 1, 1.0) + car["beam"].alignAxisToVect( (0,0,1), 2, 1.0 ) + beamscale = max( 0.2, car.getDistanceTo(dani) / 100 ) + car["beam"].scaling = [beamscale, beamscale, beamscale] + + + + try: + toenemy = car.getVectTo(car.get("enemy", dani)) + + + if car.get("npc") == "enemy" and sum([car["enemy"].get("Front_Right"), car["enemy"].get("Front_Left"), car["enemy"].get("Back_Right"), car["enemy"].get("Back_Left")])/4 < 0.2: + car["npc"] = "npc" + car["enemy"] = "" + except: + car["npc"] = "npc" + car["enemy"] = "" + return + + enemyturn = min(0.8, toenemy[2][0]*2) + onenemy = toenemy[2][1] *-1 + oldenemyturn = enemyturn + onoldenemy = onenemy + + # If we are too slow. NITRO + if car.get("npc") == "racer": + + betweencheckpoints = car.get("distanceBetweenCheckpoints", toenemy[0]) - checkpoint["radius"] + fractionDistance = 1 - (toenemy[0] / betweencheckpoints) + + if ( currentTimeToCheckpoint * 1.2 > targetTimeToCheckpoint * fractionDistance or onenemy > 0.6 ) and not car.get("deccelerating") and onGround: + nitro() + + else: + stopNitro() + + try: + tonext = 1-min(1, toenemy[0]/(car["speed"]*-50) ) ** 0.2 + except: + tonext = 0 + + if type(tonext) == complex: + tonext = float(tonext.real) + + targetSpeed = 0 + if car.get("npc") == "racer": + + if "raceData" in bge.logic.globalDict["races"][car.get("race")]: + + raceData = bge.logic.globalDict["races"][car.get("race")]["raceData"] + checkpoint["Uturn"] = raceData["targetSpeed"][car.get("checkpoint")] + + if checkpoint.get("Uturn"): + + # At current configuration car looses 1 speed at about 12.2 units of distance + targetSpeed = -checkpoint.get("Uturn") * toFPS + brakingDistance = ( car["speed"] - targetSpeed ) * -18# car.get("stopdistance", -18) + + + + if toenemy[0] <= brakingDistance and car["speed"] < targetSpeed: + deccelerate(car) + + + # We want to record the stopdistance +# if targetSpeed <= car["speed"]: +# car["stopdistance"] = car.get("stopdistance", -18) + toenemy[0] +# print(car, "stopdistance Calibrated at:", car["stopdistance"]) +# elif car["speed"] > targetSpeed and onenemy > 0.8: +# nitro() + + + if onenemy > 0.7 and minimum > 7: + + nextenemyturn = min(0.8, tonextcheckpoint[2][0]*2) + nextonenemy = tonextcheckpoint[2][1] *-1 + + enemyturn = enemyturn + ( ( nextenemyturn - enemyturn ) * tonext ) + onenemy = onenemy + ( ( nextonenemy - onenemy ) * tonext ) + + if type(onenemy) == complex: + onenemy = float(onenemy.real) + enemyturn = float(enemyturn.real) + + + + if car["speed"] > -0.2 or engineHealth <= 0.2 or car["underwater"]:# or onenemy < 0.4: + rescue() + + + + + smartturn = ( turn * onoldenemy ) + ( oldenemyturn * onenemy) + ( enemyturn ) / ( onoldenemy + onenemy + 1) + #if onenemy < 0.6: + # smartturn = oldenemyturn + #if toenemy[0] < 60: + #smartturn = enemyturn + #if onenemy < 0.9: + # smartturn = enemyturn + #if onoldenemy < 0.7: + # smartturn = oldenemyturn + + # If we about to hit a car head first + #if forwardData[0] and forwardData[0].get("speed"): + # handbreak(car) + # turn = max(-0.8, min(0.8, turn*-1000)) + # smartturn = turn + + + if (( minimum < 10 and onenemy > 0.3 ) or ( minimum < 5 and onenemy > 0.1 )) and toenemy[0] > 50: + smartturn = turn + + if minimum < 7: + car["turn"] = max(-0.5, min( 0.8 , car["turn"] * 1.5 )) + + + if carRacing: angleAt = -0.7 + else: angleAt = 0 + + if onenemy > angleAt: + + car["turn"] = smartturn + t = car["turn"] + if t < 0: t *= -1 + + # If we turning, turn harder + if t > 0.3 and car["speed"] < -1: + car["turn"] *= 3 + # If we are turning hard enough, drift + #if t > 0.4 and car["speed"] < -2 and car["speed"] < targetSpeed / 2: + # handbreak(car) + # If we are turning very hard, use breaks + if t > 0.4 and car["speed"] < -1.5 and car["speed"] < targetSpeed: + deccelerate(car) + + + + if not car.get("deccelerating"): + accelerate(car) + + + + else: + + if car.get("npc") == "racer": + rescue() + + car["turn"] = enemyturn *-1 + + deccelerate(car) + + #for i in range(4): + # vehicle.applyEngineForce(car["engine"], i) + + ################### APPLYING ########################## + + # This is here to trigger an error for if car's object + # has been deleted from the game during the execution + # of this script. Otherwise, applying things to the vehicle + # constraints might result in Segmentation Fault ( the engine + # crashing ). + car.position + + + + # Auto deceleration + if not car.get("accelerating") and car["engine"] > 0: + car["engine"] -= car.get("maxaccel",2) * toFPS + elif not car.get("accelerating") and car["engine"] < 0: + car["engine"] += car.get("maxaccel",2) * toFPS + else: + car["accelerating"] = False + + + # Auto going straiter + if not car.get("turning"): + car["turn"] *= 0.5 ** toFPS + else: + car["turning"] = False + + ApplyEnginePower(car, vehicle) +# if not car.get("deccelerating"): +# minusfrac = 1 +# if car["engine"] < 0: minusfrac = -1 +# applyforce = math.log1p(car["engine"]*minusfrac) / 4 +# if -0.05 < applyforce < 0.05: +# applyforce = 0 +# else: +# #applyforce = Opt.Force(applyforce**0.1*3*minusfrac) +# applyforce = Opt.Force(HorsePowerToNewton(applyforce))*minusfrac +# +# if car.get("nitroing"): +# applyforce *= Opt.Force(0.3) +# +# vehicle.applyEngineForce(applyforce*car["Back_Left"], 0) +# vehicle.applyEngineForce(applyforce*car["Back_Right"], 1) +# vehicle.applyEngineForce(0, 2) +# vehicle.applyEngineForce(0, 3) + + if not car.get("nitroing"): + car["engine"] = min( car.get("maxengine", 800)*engineHealth, max( -car.get("maxengine", 800)*engineHealth, car["engine"])) + else: + car["nitroing"] = False + + if car.get("deccelerating"): + car["deccelerating"] = False + + # Making sure the car is not turning more than it is possible. + car["turn"] = max(-0.8, min(0.8, car["turn"])) + + + # Suspension updates ( important due to FPS flactuations ) + + ApplySuspension(car, vehicle) + + # Turning Force Update + + vehicle.setSteeringValue(car["turn"]*car["Front_Left"] , 2) + vehicle.setSteeringValue(car["turn"]*car["Front_Left"] , 3) + + # Adding fake torque to the car, to make it easier to turn + #if carSpeed < -0.05 and onGround: + # car.applyTorque([0.0,0.0,Opt.Force(car["turn"]*10)],1) + #elif carSpeed > 0.05 and onGround: + # car.applyTorque([0.0,0.0,Opt.Force(car["turn"]*-10)],1) + +def ApplySuspension(car, vehicle): + + for i in range(4): + suspension = car["suspensions"][i] / 100 + suspense = Opt.Force(suspension*car.mass) + damping = 0.3 + + vehicle.setSuspensionCompression(1, i) + vehicle.setSuspensionStiffness(suspense, i) + vehicle.setSuspensionDamping(1, i) + +def ApplyEnginePower(car, vehicle): + + if not car.get("deccelerating"): +# minusfrac = 1 +# if car["engine"] < 0: minusfrac = -1 +# applyforce = math.log1p(car["engine"]*minusfrac) / 4 +# if -0.05 < applyforce < 0.05: +# applyforce = 0 +# else: +# #applyforce = Opt.Force(applyforce**0.1*3*minusfrac) +# applyforce = Opt.Force(HorsePowerToNewton(applyforce))*minusfrac +# +# if car.get("nitroing"): +# applyforce *= Opt.Force(0.3) + + + + # Getting car's horse power + horses = car.get("horses", 750) + rpm = car["engine"] + maxrpm = car.get("maxengine", 800) + + horses = EngineCurve(rpm, maxrpm, horses) + + if car["active"]: + print(horses) + + applyforce = Opt.Force(HorsePowerToNewton(horses / 2000)) + + vehicle.applyEngineForce(0, 0) + vehicle.applyEngineForce(0, 1) + vehicle.applyEngineForce(applyforce*car["Front_Left"], 2) + vehicle.applyEngineForce(applyforce*car["Front_Left"], 3) + +def HorsePowerToNewton(value): + return value * 745.6998715823 + +def EngineCurve(rpm, maxrpm, horses): + + # This function draws a simulated + # Engine RPM curve and outputs + # house power. + + return math.sin(rpm/maxrpm*(math.pi/4*3))*horses + +def accelerate(car): + + + if type(car) == bge.types.SCA_PythonController: + + cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + car = cont.owner + if not car["active"]: + return + + + toFPS = Opt.ToFPS() + + vehicle = bge.constraints.getVehicleConstraint(car["cid"]) + + + + car["engine"] += car.get("maxaccel",2) * toFPS + if car["engine"] > 0 and car["speed"] <= 0.2: + + car["accelerating"]= True + + + else: + car["engine"] *= 0.95 ** toFPS + car["deccelerating"]= True + for i in range(4): + vehicle.applyEngineForce(Opt.Force(car["speed"]*5*car.mass), i) + + + +def deccelerate(car): + + + + if type(car) == bge.types.SCA_PythonController: + + cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + car = cont.owner + if not car["active"]: + return + + + toFPS = Opt.ToFPS() + + vehicle = bge.constraints.getVehicleConstraint(car["cid"]) + + + car["engine"] -= car.get("maxaccel",0.003) * toFPS + + + if car["engine"] < 0 and car["speed"] >= -0.2: + car["accelerating"]= True + + else: + car["engine"] *= 0.95 ** toFPS + car["deccelerating"]= True + for i in range(4): + vehicle.applyEngineForce(Opt.Force(car["speed"]*5*car.mass), i) + + + +def turn(): + + cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + car = cont.owner + if not car["active"]: + return + + toFPS = Opt.ToFPS() + + if car["turn"] < 0: car["turn"] = 0 + car["turn"] += 0.005 * toFPS + car["turning"]= True + +def unturn(): + + cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + car = cont.owner + if not car["active"]: + return + + toFPS = Opt.ToFPS() + + if car["turn"] > 0: car["turn"] = 0 + car["turn"] -= 0.005 * toFPS + car["turning"]= True + +def handbreak(car): + + if type(car) == bge.types.SCA_PythonController: + + cont = bge.logic.getCurrentController() + scene = bge.logic.getCurrentScene() + car = cont.owner + if not car["active"]: + return + + toFPS = Opt.ToFPS() + + vehicle = bge.constraints.getVehicleConstraint(car["cid"]) + for i in range(4): + vehicle.applyEngineForce(Opt.Force(car["speed"]*2*car.mass), i) + + car["deccelerating"]= True + sp = car["speed"] + if sp < 0: sp *= -1 + car.applyTorque([0.0,0.0,Opt.Force(car["turn"]*sp*2*car.mass)],1) + + + +def get_in(): + + return + + # This function has been moved to Character Controlls + diff --git a/Scripts/Main_update.py b/Scripts/Main_update.py new file mode 100644 index 0000000..02b8fb1 --- /dev/null +++ b/Scripts/Main_update.py @@ -0,0 +1,1673 @@ +import bge +import bpy +import json +import os +import aud +import time +import datetime +import threading +import random +import numpy +import math +import mathutils + +from Scripts import Reuse +from Scripts import Destruction +from Scripts import Opt +from Scripts.Common import * + + +from Scripts import Map +from Scripts import Script +from Scripts import Vehicle +from Scripts import Settings +from Scripts import Tools +from Scripts import Money +from Scripts import Garage + + +from Scripts import Character_Controll +from Scripts import Multiplayer_Client + +def main(): + + + + # SPAWNING ASSETS AND STUFF + + spawnAtDistance = 250 + maxCars = 6 + + scene = bge.logic.getCurrentScene() + cont = bge.logic.getCurrentController() + dani = scene.objects["Dani_Box"] + cam = scene.active_camera + + #dani["driving"]["nitro"] = 10.0 + + #Script.Story["Jack"].position = dani.position + #Character_Controll.getOutCar(Script.Story["Jack"]) + #Script.Story["DarkShadow"] = dani["driving"] + # try: + # Destruction.Explosion(dani["driving"].position, mass=10, size=10) + # except:pass + + loading = scene.objects["Loading"] + if not loading.get("loaded"): + loading.scaling = [1,1,1] + loading["loaded"] = True + return + + + + if "fps-calc" not in bge.logic.globalDict: + bge.logic.globalDict["fps-calc"] = {"second":0, + "fps-list":[]} + + Opt.ExecuteScheduled() + goodFPS = Opt.GoodFPS(traceback=True) + fpsindicator = int(Opt.GoodFPS( boolean=False)*100) + scene.objects["Dani_Fps_Good_Indicator"]["Text"] = str(int(bge.logic.getAverageFrameRate()))+"\n"+str(fpsindicator)+"%" + scene.objects["Gage_FPS"]["Health"] = fpsindicator + + + #camSurround = Opt.Surround(cam.position, spawnAtDistance/2)#, cam.orientation.to_euler()) + camSurroundCars = Opt.Surround(cam.position, spawnAtDistance, cam.orientation.to_euler()) + + #changedCamSurround = Opt.SurroundChanged("camSurround", camSurround) + #changedCamSurroundCars = Opt.SurroundChanged("camSurroundCars", camSurroundCars) + + inCinema = scene.objects["CinemaColider"]["InCinema"] + + # Destroy those needing destroying + Reuse.SelfDestructDo() + + + + # Available car to spawn + # NPC_cars = ["NeonSpeedsterBox", + # "RedKissBox", + # "DarkShadowBox"] + # #"HatchBack01-white", + # #"HatchBack01-orange", + # #"HatchBack01-blue", + # #"HatchBack01-black"] + + # npccp = [1, + # 1, + # 1] + # #10, + # #8, + # #5, + # #3] + + # NPC_cars_probability = [] + # for i in npccp: + # NPC_cars_probability.append( 1 / sum(npccp) * i) + #print(sum(NPC_cars_probability), NPC_cars_probability) + + # Storing some values to the globalDict + + bge.logic.globalDict["spawnAtDistance"] = spawnAtDistance + bge.logic.globalDict["maxCars"] = maxCars + + + def GenerateDoorObject(i): + # Location doesn't matter when you dealing with rotation + doorObject = {"ClosedRotX":i.get("ClosedRotX", i.orientation.to_euler()[0]), + "ClosedRotY":i.get("ClosedRotY", i.orientation.to_euler()[1]), + "ClosedRotZ":i.get("ClosedRotZ", i.orientation.to_euler()[2]), + + "OpenRotX":i.get("OpenRotX", i.orientation.to_euler()[0]), + "OpenRotY":i.get("OpenRotY", i.orientation.to_euler()[1]), + "OpenRotZ":i.get("OpenRotZ", i.orientation.to_euler()[2]), + + "ClosedLocX":i.get("ClosedLocX", i.position[0]), + "ClosedLocY":i.get("ClosedLocY", i.position[1]), + "ClosedLocZ":i.get("ClosedLocZ", i.position[2]), + + "door": i + } + # But rotation and location both matter when you dealing with location + move = [0,0,0] + moveback = [0,0,0] + + for naxis, axis in enumerate(["X", "Y", "Z"]): + code = "OpenLoc"+axis + move[naxis] = i.get(code, 0) + moveback[naxis] = -i.get(code, 0) + + i.applyMovement(move, True) + for naxis, axis in enumerate(["X", "Y", "Z"]): + code = "OpenLoc"+axis + doorObject[code] = i.position[naxis] + i.applyMovement(moveback, True) + + return doorObject + + def OpenCloseDoor(door, openClose): + + onTarget = [] + + for n, axis in enumerate(["X", "Y", "Z"]): + + targetRot = door[openClose+"Rot"+axis] + targetLoc = door[openClose+"Loc"+axis] + + dRot = door["door"].orientation.to_euler() + + onRotTarget = round(targetRot, 2) == round(dRot[n], 2) + onLocTraget = round(targetLoc, 1) == round(door["door"].position[n], 1) + + if not onRotTarget: + dRot[n] += (targetRot - dRot[n])/15 + + + else: + dRot[n] = targetRot + + door["door"].orientation = dRot + + if not onLocTraget: + + door["door"].position[n] += (targetLoc - door["door"].position[n])/15 + + + else: + door["door"].position[n] = targetLoc + + onTarget.append( onRotTarget and onLocTraget ) + + return all( onTarget ) + + # INITIALIZING + + # This runs only on the first frame + + if not Opt.chunks: + + dani.suspendPhysics() + + + + # Making settings executed + Settings.Execute() + settings = Settings.load_settings() + bge.logic.globalDict["settings"] = settings + + Settings.LoadGame() + + Script.StatusText(" ") + + if not bge.logic.globalDict.get("start-time"): + bge.logic.globalDict["start-time"] = datetime.datetime.now().hour + datetime.datetime.now().minute / 60 + + + # Stuff + + bge.logic.globalDict["netObjects"] = { + "pythonId":{}, + "netId":{} + } + + bge.logic.globalDict["elevators"] = {} + bge.logic.globalDict["sound-ambiances"] = [] + bge.logic.globalDict["doors"] = {} + bge.logic.globalDict["races"] = {} + bge.logic.globalDict["garage-crates"] = [] + + # Cars related + + bge.logic.globalDict["spawns"] = {} + bge.logic.globalDict["allcars"] = [] + bge.logic.globalDict["cars"] = [] + bge.logic.globalDict["spawnedCarModels"] = [] + + # Cheat code modes + + bge.logic.globalDict["pursuit-cheat"] = False + bge.logic.globalDict["derby-cheat"] = False + bge.logic.globalDict["gravity"] = True + + # Navigation for NPCs + bge.logic.globalDict["Navigation"] = {"road" :[], + "parking":[]} + + Navigation = bge.logic.globalDict["Navigation"] + + for navtag in bpy.data.collections["Navigation"].objects: + + tagdata = {"position":navtag.location, + "orientation":navtag.rotation_euler, + "radius":navtag.scale[0], + "connected":[]} + + if navtag.get("parking"): + Navigation["parking"].append(tagdata) + else: + Navigation["road"].append(tagdata) + + + + # Races + for collection in bpy.data.collections['Races'].children: + + race = {"starters":[], + "checkpoints":[], + "racers":[], + "racer_spawns": [], + "started":False } + + # Getting racing data + rdf = os.listdir(bge.logic.expandPath("//racedata")) + + + if collection.name in rdf: + + fol = "//racedata/"+collection.name + race["raceData"] = {} + for f in os.listdir(bge.logic.expandPath(fol)): + if f.endswith(".json"): + with open(bge.logic.expandPath(fol+"/"+f)) as jf: + rd = json.load(jf) + race["raceData"][f.replace(".json", "")] = rd + + + + + + for object in collection.objects: + tag = {"location":object.location, + "rotation":object.rotation_euler, + "radius":object.scale[0], + "OnLoop":object.get("OnLoop"), + "IgnoreVision":object.get("IgnoreVision"), + "Uturn":object.get("Uturn"), + "cylinder":None} + + # Race starters ( the blue cylinder ) + + if object.name.startswith("Starter"): + race["starters"].append(tag) + race["laps"] = object["laps"] + race["after"] = object.get("after") + race["during"] = object.get("during") + race["type"] = object.get("type", "race-car") + race["reward"] = object.get("reward", 1000) + + # Race checkpoints ( the yellow cylinder ) + + else: + race["checkpoints"].append(tag) + + bge.logic.globalDict["races"][collection.name] = race + + # Characters + + Reuse.Delete(scene.objects["PapsBox"]) + #Reuse.Delete(scene.objects["MoriaBox"]) + Reuse.Delete(scene.objects["JackBox"]) + + # Objects + + for object in scene.objects: + + if "spawn" in object: + + objectData = {"position": object.worldPosition.copy(), + "orientation": object.worldOrientation.copy(), + "spawn": object.get("spawn"), + "to_spawn": object["to_spawn"], + "race":object.get("race"), + "npc":object.get("npc"), + "selected":object.blenderObject.select_get()} + + if object.get("race"): + race = bge.logic.globalDict["races"][object.get("race")] + if "amount-racers" not in race: + race["amount-racers"] = 0 + race["amount-racers"] += 1 + + addr = Opt.Address(objectData["position"], spawnAtDistance) + if addr not in bge.logic.globalDict["spawns"]: + bge.logic.globalDict["spawns"][addr] = [] + + bge.logic.globalDict["spawns"][addr].append(objectData) + + # For testing we will ignore optimization for selected ones. + if objectData["selected"]: + + if "selected" not in bge.logic.globalDict["spawns"]: + bge.logic.globalDict["spawns"]["selected"] = [] + bge.logic.globalDict["spawns"]["selected"].append(objectData) + print("Selected", object) + + object.endObject() + + # Crates + if str(object.name).startswith("Crate"): + crates = bge.logic.globalDict["garage-crates"] + crates.append(object) + + # Spawn objects + if "LightSpawn" in object: + name = object["LightSpawn"] + + if name == "LightStand": + if settings.get("poles"): + spawnObject = Opt.RegisterObject(object, spawnAtDistance) + spawnObject["name"] = object["LightSpawn"] + spawnObject["scaling"] = [1,1,1] + spawnObject["suspendDynamics"] = object.get("suspendDynamics", True) + else: + object.endObject() + + elif "Gate" in name: + if settings.get("fences"): + spawnObject = Opt.RegisterObject(object, spawnAtDistance) + spawnObject["name"] = object["LightSpawn"] + spawnObject["scaling"] = [1,1,1] + spawnObject["suspendDynamics"] = object.get("suspendDynamics", True) + else: + object.endObject() + + else: + spawnObject = Opt.RegisterObject(object, spawnAtDistance) + spawnObject["name"] = object["LightSpawn"] + spawnObject["scaling"] = [1,1,1] + spawnObject["suspendDynamics"] = object.get("suspendDynamics", True) + + + + + # Trees + elif "Tree" in object.name: + if settings.get("trees"): + treeObject = Opt.RegisterObject(object, spawnAtDistance) + treeObject["lods"] = { + "NormalTreeTrunk": 100, + #"TreeLowres": 200, # The model is ugly + "TreeBillboard": 5000 + } + treeObject["name"] = "TreeBillboard" + treeObject["scaling"] /= 1.603 + else: + object.endObject() + + + elif "Palm" in object.name: + if settings.get("trees"): + treeObject = Opt.RegisterObject(object, spawnAtDistance) + treeObject["lods"] = { + "NormalPalmTrunk": 100, + "PalmLow": 200, + "PalmCutout": 5000 + } + treeObject["name"] = "PalmCutout" + treeObject["scaling"] /= 1.445 + else: + object.endObject() + + # Elevators + elif "Elevator" in object: + elevator = {"elevator":object, + "radius":object.get("Radius", 5), + "levels":[], + "doors":{}} + for level in range(10): + if "Level"+str(level) in object: + elevator["levels"].append(object["Level"+str(level)]) + + + for door in scene.objects: + if door.get("ElevatorDoor") == object["Elevator"]: + + + # Door's motion matrix + doorObject = GenerateDoorObject(door) + elevator["doors"][door["Level"]] = doorObject + + bge.logic.globalDict["elevators"][object["Elevator"]] = elevator + + elif "Door" in object: + + doorObject = GenerateDoorObject(object) + if object["Door"] not in bge.logic.globalDict["doors"]: + bge.logic.globalDict["doors"][object["Door"]] = {"doors":[], + "radius":3} + bge.logic.globalDict["doors"][object["Door"]]["doors"].append(doorObject) + if "Radius" in object: + bge.logic.globalDict["doors"][object["Door"]]["radius"] = object["Radius"] + bge.logic.globalDict["doors"][object["Door"]]["position"] = object.position.copy() + + + elif "ambiance-sound" in object: + bge.logic.globalDict["sound-ambiances"].append(object) + + + # Precalculating objects for smoothness + preData = {"MetalGate_good":100, + "GatePart":100, + "LightStand": 50, + "NormalTreeTrunk":30, + "TreeBillboard":50, + "PalmCutout":32, + "PalmLow":16, + "NormalPalmTrunk":16, + "House_Shelf":4, + "Moria's Bed":1, + "Moria's Bed.001":1, + "Sparkle": 100, + "Smoke": 20, + "LightStand.Borked.Head": 5, + "LightStand.Borked.Tail": 5, + "GatePart.Level0": 50, + "GatePart.Level1": 50, + "Fire": 200, + "NitroCone":5, + "Road Blocker": 12 + } + + print("Precalculating... ") + for n, object in enumerate(preData): + for i in range( preData[object] ): + Reuse.Create(object, selfDestructFrames = 1, selfDestructInactive = False ) + + # Exiting scene + def OnExit(): + print("\nScene Spawn Totals:\n") + for obj in Reuse.amounts: + + precached = preData.get(obj, 0) + cached = Reuse.amounts[obj] + + d = cached - precached + if d > 0: + dc = clr["tdrd"] + d = "+"+str(d) + elif d <= 0: + dc = clr["tdgr"] + + print(" "+clr["bold"]+obj+clr["norm"]+" Exited with"+clr["bold"], cached, clr["norm"]+", Started with"+clr["bold"], precached, clr["norm"]+", Difference"+dc, d , clr["norm"] ) + + + print() + Settings.SaveGame() + print() + + scene.onRemove.append(OnExit) + + + bge.logic.globalDict["restore-physics-timer"] = 100 + bge.logic.globalDict["restore-physics"] = True + + # Running multiplayer daemon + if settings.get("multiplayer"): + multiplayer = threading.Thread(target=Multiplayer_Client.MainLoop) + multiplayer.setDaemon(True) + multiplayer.start() + + + + + + if bge.logic.globalDict["restore-physics-timer"]: + bge.logic.globalDict["restore-physics-timer"] -= 2 + return + elif bge.logic.globalDict["restore-physics"]: + bge.logic.globalDict["restore-physics"] = False + dani.restorePhysics() + + loading.visible = False + + #dpos = dani.position.copy() + #dpos.z += 0.5 + #Destruction.AttatchFireBall(dpos, + # dani, + # 1) + + + + settings = bge.logic.globalDict["settings"] + + # Updating all the objects + #if changedCamSurroundCars: + Opt.ScheduleTask("Scene Chunk Updates", 0.85, Opt.UpdateScene, + cam, spawnAtDistance, cam.orientation.to_euler()) + + + # Running the story of the game + Script.Run(scene, dani) + + # Multiplayer scene updates + Multiplayer_Client.SecondaryLoop() + + + # SPAWNING CARS + + spawnedCarModels = bge.logic.globalDict["spawnedCarModels"] + spawnedCars = len( bge.logic.globalDict["allcars"] ) + + Opt.ScheduleTask("Car Updates", 0.80, Vehicle.SpawnLogic, + camSurroundCars) + + #Vehicle.SpawnLogic(camSurroundCars) + Vehicle.UnspawnLogic() + + #print("Cars:", spawnedCars) + + # SpawnsCloseEnough = [] + # for i in camSurroundCars: + # SpawnsCloseEnough += bge.logic.globalDict["spawns"].get(i, []) + + + # for spawnPoint in SpawnsCloseEnough: + + # inview = cam.pointInsideFrustum(spawnPoint["position"]) == cam.INSIDE + # distance = cam.getDistanceTo(spawnPoint["position"]) + + # # Car information + # carModel = spawnPoint["spawn"] + # carBehaviour = spawnPoint.get("npc") + # carRace = spawnPoint.get("race") + + # # If this is a road npc, we want to choose a random car model + # if carBehaviour == "npc": + + # carModel = numpy.random.choice(NPC_cars, p=NPC_cars_probability) + # toSpawn = spawnPoint["to_spawn"] and inview and spawnAtDistance / 2 < distance < spawnAtDistance and spawnedCars < maxCars + + # else: + # toSpawn = spawnPoint["to_spawn"] and inview and distance < spawnAtDistance + + + # # Trying not to add any more cars than nessesary + # if toSpawn and carModel in Reuse.amounts and not Reuse.reuse.get(carModel): + + # force_add = False + + # # Making sure that the race still works + # if carRace and not dani.get("race"): + # for car in bge.logic.globalDict["allcars"]: + # if car.name == carModel: + # if not car.get("active"): + # Reuse.Delete(car, inactive=True) + # else: + # force_add = True + # break + # if carRace: + # force_add = True + + # if not force_add: + # continue + + + # # Spawning a car + # if toSpawn: + + # def AddCar(carModel, carBehaviour, carRace, spawnPoint): + + # print("Spawning Car at distance from Dani:", dani.getDistanceTo(spawnPoint["position"])) + + # # Making car + # car, new = Reuse.Create(carModel, declarenew=True) + + # # If you claimed the car, one more car! + # if car["active"]: + # car, new = Reuse.Create(carModel, declarenew=True) + + # Vehicle.Spawn(car, + # spawnPoint["position"], + # spawnPoint["orientation"], + # color="pallete") + + # if carBehaviour == "npc": + # car.setLinearVelocity([0,-15,0], True) + # else: + # car["spawnPoint"] = spawnPoint + + # car["npc"] = carBehaviour + # car["anger"] = random.random() + # car["enemy"] = "" + # car["chased"] = False + # car["engine"] = 0 + # car["launchtime"] = 300 + + # if carRace: + + # car["race"] = carRace + # car["racing"] = False + # car["checkpoint"] = 0 + # car["rescue"] = bge.logic.globalDict["races"][carRace]["starters"][0]["location"] + # car["lap"] = 0 + # car["blown"] = False + # bge.logic.globalDict["races"][carRace]["racers"].append(car) + # bge.logic.globalDict["races"][carRace]["racer_spawns"].append(spawnPoint) + + # # Cars are deformable during game, so we will need to restore them + # Vehicle.Fix(car) + + + + + # # Scheduling it for a task + # if not Reuse.reuse.get(carModel): + # Opt.ScheduleTask("Adding Car ["+carModel+"]", 0.80, AddCar, + # carModel, carBehaviour, carRace, spawnPoint) + # else: + # AddCar(carModel, carBehaviour, carRace, spawnPoint) + + # spawnPoint["to_spawn"] = False + # spawnPoint["to_spawn_timer"] = 500 + + # # If player is standing still keep spawning cars near by + # if not spawnPoint["to_spawn"] and spawnPoint["to_spawn_timer"]: + # spawnPoint["to_spawn_timer"] -= 1 + # elif not spawnPoint["to_spawn"] and carBehaviour == "npc": + # spawnPoint["to_spawn"] = True + + + # # Removing cars that were deleted + # for car in bge.logic.globalDict["allcars"]: + # try: + # car.position + # except: + # bge.logic.globalDict["allcars"].remove(car) + + + # ELEVATORS + + # Seeing on what level dani is. + def atLevel(object): + levelis = 0 + closest = 1000 + for n, i in enumerate(elevator["levels"]): + dist = object.position[2] - i + if dist < 0: dist *= -1 + + if dist < closest: + closest = dist + levelis = n + return levelis + + for elevatorname in bge.logic.globalDict["elevators"]: + + elevator = bge.logic.globalDict["elevators"][elevatorname] + + # Seeing if Dani is anywhere near an elevator + eloc = elevator["elevator"].position.copy() + eloc[2] = dani.position[2] + + if dani.getDistanceTo(eloc) < elevator["radius"]: + + + + # If dani not at the same level as the elevator + if atLevel(dani) != atLevel(elevator["elevator"]): + toprint = "Press L to call Elevator." + if toprint not in bge.logic.globalDict.get("done-prints",[]): + bge.logic.globalDict["print"] = toprint + + # Pressing L to call it + if 34 in bge.logic.globalDict["keys"]: + elevator["going_to"] = atLevel(dani) + elevator["closing_doors"] = True + + else: + toprint = "Press 0-"+str(len(elevator["levels"])-1)+" activate the elevator." + if toprint not in bge.logic.globalDict["done-prints"]: + bge.logic.globalDict["print"] = toprint + + numbers = [13, 14, 15, 16, 17, 18, 19, 20, 21, 22] + for num in numbers: + if num in bge.logic.globalDict["keys"] and numbers.index(num) < len(elevator["levels"]): + elevator["going_to"] = numbers.index(num) + elevator["closing_doors"] = True + + # Elevator coming + if elevator.get("going_to") != None and not elevator.get("closing_doors"): + destination = elevator["levels"][elevator["going_to"]] + position = elevator["elevator"].position[2] + + if destination < position: + elevator["elevator"].position[2] -= 0.1 * elevator["elevator"].get("speedFactor", 1) + else: + elevator["elevator"].position[2] += 0.1 * elevator["elevator"].get("speedFactor", 1) + + if round(destination, 1) == round(position, 1): + elevator["going_to"] = None + elevator["closing_doors"] = True + + # Doors + if elevator.get("closing_doors"): + + cancelBucket = [] + + # If elevator is moving, all doors should be closed. + if elevator.get("going_to") != None: + openLevel = -1 + else: + openLevel = atLevel(elevator["elevator"]) + + for level in elevator["doors"]: + + door = elevator["doors"][level] + + # The elevator is at the current door's level, + # the door should be open. + if level == openLevel: + openClose = "Open" + else: + openClose = "Closed" + + onTarget = OpenCloseDoor(door, openClose) + + cancelBucket.append(onTarget) + + if all(cancelBucket): + elevator["closing_doors"] = False + + # DOOR ( in general ) + + for doorname in bge.logic.globalDict["doors"]: + + doors = bge.logic.globalDict["doors"][doorname] + + if doors.get("doOpenClose"): + cancelBucket = [] + for door in doors["doors"]: + onTarget = OpenCloseDoor(door, doors["OpenClosed"]) + cancelBucket.append(onTarget) + if all(cancelBucket): + doors["doOpenClose"] = False + + else: + + + + if doors.get("OpenClosed", "Closed") == "Closed" and dani.getDistanceTo(doors["position"]) < doors["radius"]: + + doors["OpenClosed"] = "Open" + doors["doOpenClose"] = True + + elif doors.get("OpenClosed") == "Open" and dani.getDistanceTo(doors["position"]) > doors["radius"]: + + doors["OpenClosed"] = "Closed" + doors["doOpenClose"] = True + + # Sound + if doors.get("doOpenClose"): + + device = bge.logic.globalDict["SoundDevice"] + + for n, door in enumerate(doors["doors"]): + + s = "//sfx/home_door.ogg" + code = str(n)+s + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = door["door"].position + sound["play"].velocity = door["door"].getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(1, 2) + sound["play"].volume = 0.3 + + + # WHEEL UP DOWN + + cam_parent = scene.objects["DaniCam_Parent"] + + wheelup = cont.sensors["wheel_up"] + wheeldown = cont.sensors["wheel_down"] + if wheelup.status: + cam_parent.scaling /= 1.1 + elif wheeldown.status and cam_parent.scaling[0] < 20: + cam_parent.scaling *= 1.1 + + # CHEAT CODES + + cheat_spawn = scene.objects["Dani_Cheater"] + + keys = bge.logic.keyboard + + bge.logic.globalDict["keys"] = [] + for keyn in keys.inputs: + key = keys.inputs[keyn] + if key.active: + bge.logic.globalDict["keys"].append(keyn) + + keys = bge.logic.mouse + bge.logic.globalDict["mouse"] = [] + for keyn in keys.inputs: + key = keys.inputs[keyn] + if key.active: + bge.logic.globalDict["mouse"].append(keyn) + + keys = bge.logic.globalDict["keys"] + + # Control release + if "mouse-active" not in bge.logic.globalDict: + bge.logic.globalDict["mouse-active"] = True + if keycodes["Tab"] in keys and not bge.logic.globalDict.get("mouse-active-timer"): + bge.logic.globalDict["mouse-active"] = not bge.logic.globalDict["mouse-active"] + bge.logic.globalDict["mouse-active-timer"] = 10 + if bge.logic.globalDict.get("mouse-active-timer"): + bge.logic.globalDict["mouse-active-timer"] -= 1 + + + + + + + if keys and settings.get("dev-cheats"): + + #print("Key pressed code:",keys.events) + + if "cheat" not in bge.logic.globalDict: + bge.logic.globalDict["cheat"] = [0] + + for i in keys: + if i != bge.logic.globalDict["cheat"][-1]: + bge.logic.globalDict["cheat"].append(i) + + # SPAWN CHEATS + + cheats = { + "NeonSpeedsterBox": "NEONSPEDSTER", + "RedKissBox": "REDKIS", + "DarkShadowBox": "DARKSHADOW", + "TruckBox": "THETRUCK", + "HatchBack01Box": "HATCHBACK1", + } + + for i in cheats: + code = bge.logic.globalDict["cheat"][-(len(cheats[i])):] + + numericalcode = [] + for letter in cheats[i]: + numericalcode.append(keycodes[letter]) + + if code == numericalcode: + print("Spawning:", i) + + car = Vehicle.Spawn(i, + cheat_spawn.position, + cheat_spawn.orientation, + color="pallete") + + + bge.logic.globalDict["cheat"] = [0] + + + bge.logic.globalDict["print"] = "A Vehicle Is Waiting For You, Sir." + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + teleportations = [ + #[[-75.19, 1028, 0], "RACETRACK"], + [[-68.35, 946.6, 4.738], "RACETRACK"], + [[-752.78, -937.4, 411.08], "HOUSE"], + [[-755.1, -1041, 405.8], "GARAGE"], + [[-792.10,-980.60, 405.50], "PARKING"], + [[-937.3, -277.1, 257.7], "MANSION"], + [[-254.4, -508.1, 189.2], "BIGBUILDING"], + [[232.9, -120.4, 42.22], "NEARSEA"], + [[229.9, -811.9, 175.3], "PITOSTATUE"], + [[659.4, -616.1,202.9], "CONSTRUCTION"], + [[-2.122, 1322, 99.85], "TOWER0"], + [[-172.4, 1147, 99.68], "TOWER1"], + [[200, 1204, 100], "TOWER2"], + [[12.19, 1127, 139.3], "TOWER3"], + [[148.4, 941.7, 98.83], "TOWER4"], + [[-725, -984.6, 409.2], "CINEMA"] + ] + + for i in teleportations: + code = bge.logic.globalDict["cheat"][-(len(i[1])):] + + numericalcode = [] + for letter in i[1]: + numericalcode.append(keycodes[letter]) + + if code == numericalcode: + if not dani.get("driving"): + Character_Controll.ChangeCameraTarget(dani.children["Dani_cam_Target"], 100, [1,1,1]) + dani.position = i[0] + + else: + dani["driving"].position = i[0] + + bge.logic.globalDict["cheat"] = [0] + + bge.logic.globalDict["print"] = "Teleportation Finished, Sir." + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + # Money code + code = bge.logic.globalDict["cheat"][-(len("givememoney")):] + if code == [29, 31, 44, 27, 35, 27, 35, 37, 36, 27, 47]: + Money.Recieve(569) + bge.logic.globalDict["cheat"] = [0] + bge.logic.globalDict["print"] = "Enjoy The Money, Sir." + + # Puruit code + code = bge.logic.globalDict["cheat"][-(len("pursuit")):] + if code == [38, 43, 40, 41, 43, 31, 42]: + bge.logic.globalDict["pursuit-cheat"] = not bge.logic.globalDict["pursuit-cheat"] + bge.logic.globalDict["cheat"] = [0] + + if bge.logic.globalDict["pursuit-cheat"]: + bge.logic.globalDict["print"] = "Everybody Hates You, Sir. At Your Request, Sir." + else: + bge.logic.globalDict["print"] = "Everybody Stopped Hating You, Sir." + + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + # Derby code + code = bge.logic.globalDict["cheat"][-(len("derby")):] + if code == [26, 27, 40, 24, 47]: + bge.logic.globalDict["derby-cheat"] = not bge.logic.globalDict["derby-cheat"] + bge.logic.globalDict["cheat"] = [0] + + if bge.logic.globalDict["derby-cheat"]: + bge.logic.globalDict["print"] = "Everybody Is Crazy, Sir." + else: + bge.logic.globalDict["print"] = "Everybody Stopped Being Crazy, Sir." + + + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + + + # Gravity code + code = bge.logic.globalDict["cheat"][-(len("gravity")):] + if code == [29, 40, 23, 44, 31, 42, 47]: + bge.logic.globalDict["gravity"] = not bge.logic.globalDict["gravity"] + if bge.logic.globalDict["gravity"]: + bge.constraints.setGravity(0, 0, -9.8) + else: + bge.constraints.setGravity(0, 0, 0) + + bge.logic.globalDict["cheat"] = [0] + + if bge.logic.globalDict["gravity"]: + bge.logic.globalDict["print"] = "We're Back To Earth, Sir." + else: + bge.logic.globalDict["print"] = "We're Up In Space, Sir." + + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + # Explosion code + code = bge.logic.globalDict["cheat"][-(len("bom")):] + if code == [24,37,35]: + expos = cheat_spawn.position.copy() + expos[2] = dani.position[2] + Destruction.Explosion(expos, mass=10, size=10) + + bge.logic.globalDict["print"] = "Boom! , Sir." + bge.logic.globalDict["cheat"] = [0] + + # no ding here, the boom is enough + + # Fix car code: + code = bge.logic.globalDict["cheat"][-(len("fixcar")):] + if code == [28, 31, 46, 25, 23, 40]: + car = dani.get("driving") + Vehicle.Fix(car) + bge.logic.globalDict["print"] = "The Car is like New, Sir." + bge.logic.globalDict["cheat"] = [0] + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + # Color Car code: + code = bge.logic.globalDict["cheat"][-(len("colorcar")):] + if code == [25, 37, 34, 37, 40, 25, 23, 40]: + car = dani.get("driving") + Vehicle.SmartColor(car, color="pallete") + bge.logic.globalDict["print"] = "The Car is colored, Sir." + bge.logic.globalDict["cheat"] = [0] + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + # Color Car code: + code = bge.logic.globalDict["cheat"][-(len("carnpc")):] + if code == [25, 23, 40, 36, 38, 25]: + car = dani.get("driving") + car["target"] = [] + car["npc"] = "npc" + car["active"] = False + bge.logic.globalDict["print"] = "The Car is NPC, Sir." + bge.logic.globalDict["cheat"] = [0] + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + + # RACES + for racename in bge.logic.globalDict["races"]: + race = bge.logic.globalDict["races"][racename] + + pointer = scene.objects["PointingArrow"] + posindicator = scene.objects["Dani_Pos_Indicator"] + timeindicator = scene.objects["Dani_Time_Indicator"] + lapindicator = scene.objects["Dani_Lap_Indicator"] + + reward = race.get("reward", 1000) + bid = reward / (race.get("amount-racers", 1)+1) + + after = race.get("after") + during = race.get("during") + duringcheck = dani.get("race") == after and Script.Story.get(during) + aftercheck = duringcheck or after in Script.Story["passed"] or not after + + + + + # Show them on the map + if ( not dani.get("race") and aftercheck and Money.Have(bid)) or duringcheck: + + Map.Show(race["starters"][0]["location"], icon="Map_Circle", color=[0.01,0.01,1], ID=racename) + + if not race["started"] and ( ( not dani.get("race") and Money.Have(bid) ) or duringcheck ) and race["racers"] and aftercheck: + + for starter in race["starters"]: + + if not starter["cylinder"] and dani.getDistanceTo(starter["location"]) < spawnAtDistance: + starter["cylinder"] = Reuse.Create("Starter.Cylinder") + starter["cylinder"].position = starter["location"] + starter["cylinder"].orientation = starter["rotation"] + starter["cylinder"].scaling[0] = starter["radius"] + starter["cylinder"].scaling[1] = starter["radius"] + + if dani.getDistanceTo(starter["location"]) < starter["radius"]: + NPC_type_cars = bge.logic.globalDict.get("NPC_type_cars", {}) + cartype = race.get("type") + if dani["driving"] and dani["driving"].name in NPC_type_cars.get(cartype,[]): + if not duringcheck: + bge.logic.globalDict["print"] = "Press R to start the race.\nEverybody bids $"+str(int(bid))+"\nWinner gets $"+str(int(race.get("reward"))) + else: + bge.logic.globalDict["print"] = "Press R to start the race." + + if keycodes["R"] in keys: + + if not duringcheck: + Money.Pay(bid) + + race["started"] = True + Reuse.Delete(starter["cylinder"]) + starter["cylinder"] = None + + race["start-time"] = bge.logic.getRealTime() + + for racer in race["racers"]: + racer["racing"] = True + racer["launchtime"] = 100 + racer["race"] = racename + + # Testing + racer["npc"] = "racer" + racer["active"] = False + + # Beam of energy + racer["beam"] = scene.addObject("Racer.Indicator", "Racer.Indicator") + + dani["race"] = racename + dani["checkpoint"] = 0 + dani["lap"] = 0 + + + posindicator.visible = True + pointer.visible = True + timeindicator.visible = True + lapindicator.visible = True + + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + else: + bge.logic.globalDict["print"] = "Come here with a "+cartype+" to race." + + + # If this race is going + elif dani.get("race") == racename: + nextcheck = race["checkpoints"][dani["checkpoint"]] + nextcheckpoint = race["checkpoints"][ ( dani.get("checkpoint") + 1 ) % len(race["checkpoints"]) ] + + # Alight the arrow to the checkpoint + tocheck = pointer.getVectTo(nextcheck["location"]) + pointer.alignAxisToVect(tocheck[1], 1, 0.1) + + # Alighn the arrow to the next checkpoint if close enough to it. + if cam.pointInsideFrustum(nextcheck["location"]) == cam.INSIDE: + tonextcheck = pointer.getVectTo(nextcheckpoint["location"]) + pointer.alignAxisToVect(tonextcheck[1], 1, min(1, max(0, 1-(tocheck[0]/500))) ** 5 ) + + # Align the arrow vertically + pointer.alignAxisToVect( (0,0,1), 2, 1.0 ) + + # Time of the race + currentRaceTime = bge.logic.getRealTime() - race["start-time"] + formattedTime = str(datetime.timedelta(seconds=currentRaceTime))[:-4] + while formattedTime.startswith("0") or formattedTime.startswith(":"): + formattedTime = formattedTime[1:] + if formattedTime.startswith("."): + formattedTime = "0"+formattedTime + + timeindicator["Text"] = formattedTime + lapindicator["Text"] = str(dani["lap"]+1)+"/"+str(race["laps"]) + + if not nextcheck["cylinder"] and dani.getDistanceTo(nextcheck["location"]) < spawnAtDistance: + nextcheck["cylinder"] = Reuse.Create("Tag.Cylinder") + nextcheck["cylinder"].position = nextcheck["location"] + nextcheck["cylinder"].orientation = nextcheck["rotation"] + nextcheck["cylinder"].scaling[0] = nextcheck["radius"] * 0.8 + nextcheck["cylinder"].scaling[1] = nextcheck["radius"] * 0.8 + + Map.Show(nextcheck["location"], icon="Map_Circle", color=[1,0.3,0], ID="RaceCheckpoint") + Map.Show(nextcheckpoint["location"], icon="Map_Circle", color=[1,0.1,0], ID="RaceCheckpoint2") + + if dani.getDistanceTo(nextcheck["location"]) < nextcheck["radius"]*1.5: + + if not nextcheck.get("OnLoop"): + dani["prevrescue"] = dani.get("rescue") + dani["rescue"] = nextcheck["location"] + dani["rescueTo"] = dani["checkpoint"] + + Reuse.Delete(nextcheck["cylinder"]) + nextcheck["cylinder"] = None + + # Logging speed for better AI + #if "raceData" not in race: + nextcheck["speed"] = -dani["driving"].localLinearVelocity[1] + nextcheck["time"] = currentRaceTime + nextcheck["rot"] = list(dani["driving"].orientation.to_euler()) + #if race["positions"].index(dani) == 0: + # race["raceData"]["speed"][dani["checkpoint"]] = -dani["driving"].localLinearVelocity[1] + # race["raceData"]["time"][dani["checkpoint"]] = currentRaceTime + # race["raceData"]["rotz"][dani["checkpoint"]] = dani["driving"].orientation.to_euler()[2] + + dani["checkpoint"] += 1 + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["checkpoint"]["sound"]) # Play a Ding + + if dani["checkpoint"] == len(race["checkpoints"]): + dani["checkpoint"] = 0 + dani["lap"] += 1 + + print('"'+racename+'" : {') + print('"speed": [') + for c in race["checkpoints"]: + print(c.get("speed"),",") + print('],\n"time":[') + for c in race["checkpoints"]: + print(c.get("time"),",") + print('],\n"rot":[') + for c in race["checkpoints"]: + print(c.get("rot"),",") + print(']},') + + + + # If finished race + if dani["lap"] == race["laps"]:# or not dani["driving"]: + + + + dani["race"] = None + race["started"] = False + + posindicator.visible = False + timeindicator.visible = False + pointer.visible = False + lapindicator.visible = False + + + race["racer_spawns"] = [] + + for racer in race["racers"]: + racer["npc"] = "npc" + racer["beam"].endObject() + + race["racers"] = [] + + if nextcheck["cylinder"]: + Reuse.Delete(nextcheck["cylinder"]) + nextcheck["cylinder"] = None + + # If won + if race["positions"].index(dani) == 0: + bge.logic.globalDict["print"] = "You've Won This Race!\nTime: "+formattedTime+"\nReward: $"+str(int(race.get("reward", 0))) + Money.Recieve(race.get("reward", 0)) + # If finished + else: + bge.logic.globalDict["print"] = "You finished number "+str(race["positions"].index(dani)+1) + " Time: "+formattedTime + + Script.StatusText(" ") + continue + + + positions = [] + for racer in race["racers"]: + racer["into_race"] = [race["laps"] - racer["lap"], len(race["checkpoints"]) - racer["checkpoint"], racer.getDistanceTo(race["checkpoints"][racer["checkpoint"]]["location"])] + positions.append([racer["into_race"], racer]) + + dani["into_race"] = [race["laps"] - dani["lap"], len(race["checkpoints"]) - dani["checkpoint"], dani.getDistanceTo(race["checkpoints"][dani["checkpoint"]]["location"])] + positions.append([dani["into_race"], dani]) + + positions = sorted(positions) + race["positions"] = [] + for position, racer in positions: + race["positions"].append(racer) + + posindicator["Text"] = str(race["positions"].index(dani)+1) + + # The positions text ( with Dani's car being Bold and yellow ) + + pt = "" + bold = [] + for n, p in enumerate(race["positions"]): + n += 1 + if p == dani: + try: t = dani.get("driving", "On Foot")["specs"]["name"]+" | "+str(n)+"\n" + except: t = str(dani.get("driving", "On Foot"))+" | "+str(n)+"\n" + + bold = range(len(pt), len(pt)+len(t)) + + else: + try: t = p["specs"]["name"]+" | "+str(n)+"\n" + except: t = str(p)+" | "+str(n)+"\n" + pt = pt + t + Script.StatusText(pt, bold) + + + + # Messages + + if "done-prints" not in bge.logic.globalDict: + bge.logic.globalDict["done-prints"] = [] + + if bge.logic.globalDict.get("print") and bge.logic.globalDict.get("last_print") != bge.logic.globalDict["print"]: + + print() + for line in bge.logic.globalDict["print"].split("\n"): + print(" "+clr["bold"]+clr["tdyl"]+line+clr["norm"]) + + print() + + #scene.objects["Messages"]["Text"] = str(bge.logic.globalDict.get("print")) + scene.objects["Messages"].blenderObject.data.body = str(bge.logic.globalDict.get("print")) + scene.objects["Messages"].stopAction() + scene.objects["Messages"].playAction("MessagesAction", 0, 100) + + bge.logic.globalDict["print-change-size"] = True + + + + bge.logic.globalDict["last_print"] = bge.logic.globalDict["print"] + + + if bge.logic.globalDict["print"] not in bge.logic.globalDict["done-prints"]: + bge.logic.globalDict["done-prints"].append(bge.logic.globalDict["print"]) + + bge.logic.globalDict["print"] = "" + + elif bge.logic.globalDict.get("print-change-size"): + + # Changing the size of the element under the text + # This is done in a different frame, because we need + # to let it update the depsgraph with the text dimensions. + + camscale = scene.objects["DaniCam_Parent"].scaling[0] + + textSize = scene.objects["Messages"].blenderObject.dimensions + thingSize = scene.objects["Message_Background"].blenderObject.dimensions + thingScale = scene.objects["Message_Background"].worldScale + + textSize = mathutils.Vector(( + textSize.x + ( 0.04 * camscale ), + textSize.y + ( 0.02 * camscale ), + textSize.z + )) + + thingSize = mathutils.Vector(( + thingSize.x / thingScale.x, + thingSize.y / thingScale.y, + thingSize.z / thingScale.z + )) + + + thingScale = mathutils.Vector(( + textSize.x / thingSize.x, + textSize.y / thingSize.y, + 1.0 # It doesn't matter, and making it 0 collapes the object + )) + + scene.objects["Message_Background"].worldScale = thingScale + point = Vehicle.RelativePoint(scene.objects["Messages"], + (-0.007 * camscale, + (-0.010 * camscale) + (textSize.y / 2), + -0.001)) + scene.objects["Message_Background"].worldPosition = point + + bge.logic.globalDict["print-change-size"] = False + + + # SOUND + + #print(dir(aud)) + + if "SoundDevice" not in bge.logic.globalDict: + bge.logic.globalDict["SoundDevice"] = aud.Device() + bge.logic.globalDict["SoundDevice"].distance_model = aud.DISTANCE_MODEL_INVERSE_CLAMPED + bge.logic.globalDict["sounds"] = {} + + bge.logic.globalDict["sounds"]["active"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/active.ogg")), + "play":None} + + # Angry pursuit sounds + + bge.logic.globalDict["sounds_angry_start"] = [] + + for i in range(8): + i += 1 + i = str(i) + if len(i) < 2: + i = "0"+i + i = "//sfx/voices/angry_pursuit/start_"+i+".ogg" + bge.logic.globalDict["sounds"][i] = {"sound":aud.Sound(bge.logic.expandPath(i)), + "play":None} + bge.logic.globalDict["sounds_angry_start"].append(i) + + + bge.logic.globalDict["sounds_angry_hit"] = [] + + for i in range(5): + i += 1 + i = str(i) + if len(i) < 2: + i = "0"+i + i = "//sfx/voices/angry_pursuit/hit_"+i+".ogg" + bge.logic.globalDict["sounds"][i] = {"sound":aud.Sound(bge.logic.expandPath(i)), + "play":None} + bge.logic.globalDict["sounds_angry_hit"].append(i) + + # Dani voices + + bge.logic.globalDict["sounds"]["dani_yelling"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/yelling.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_entering"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/entering.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_ohno"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohno.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_ohnoindeed"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohnoindeed.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_wearesinking"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/wearesinking.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_ohgod"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohgod.ogg")), + "play":None} + + bge.logic.globalDict["sounds"]["dani_jump"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/jump.ogg")), + "play":None} + + bge.logic.globalDict["sounds"]["dani_land"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/land.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_walk"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/walk.ogg")), + "play":None} + + # Jack voices + bge.logic.globalDict["JackBoxVoices"] = {"crash":[], + "near" :[], + "speed":[]} + + + + JV = bge.logic.globalDict["JackBoxVoices"] + JF = bge.logic.expandPath("//sfx/voices/jack/") + for t in ["crash", "near", "speed"]: + for f in os.listdir(JF): + if f.startswith(t) and f.endswith(".ogg"): + name = f.replace(".ogg", "") + bge.logic.globalDict["sounds"]["jack_"+name] = {"sound":aud.Sound(JF+f), + "play":None} + JV[t].append(bge.logic.globalDict["sounds"]["jack_"+name]) + + + # Car Sounds + + bge.logic.globalDict["sounds"]["door"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/door.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["tire_pop"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/tire_pop.ogg")), + "play":None} + + # Race sounds + + bge.logic.globalDict["sounds"]["checkpoint"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/checkpoint.ogg")), + "play":None} + + + device = bge.logic.globalDict["SoundDevice"] + device.listener_location = scene.active_camera.worldPosition + device.listener_orientation = scene.active_camera.worldOrientation.to_quaternion() + device.listener_velocity = scene.active_camera.getLinearVelocity() + + for i in bge.logic.globalDict["sound-ambiances"]: + s = i["ambiance-sound"] + #s = "//sfx/ambiance/forest.ogg" + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + scale = i.scaling[0] + distance = i.getDistanceTo(dani) + + if inCinema: + sound["inCinema"] = sound.get("inCinema", 1) * 0.95 + elif sound.get("inCinema", 1) < 1: + if sound["inCinema"] < 0.1: + sound["inCinema"] = 0.1 + sound["inCinema"] = sound.get("inCinema", 1) * 1.05 + sound["play"].volume = max(0, min(1, (1-(distance / scale ))*5))*i.get("volume", 1) * sound.get("inCinema", 1) + + ########## LODS FOR CHUNKS OF THE ROAD ############# + if "LODchunks" not in bge.logic.globalDict: + bge.logic.globalDict["LODchunks"] = {"TheRacetrack":{ + "object":scene.objects["TheRacetrack"], + "high":"TheRacetrackHigh", + #"low":"TheRacetrackLow", + "radius":1000, + "now":"high", + "name":"Papses Racetrack" + }, + + "TheCity":{ + "object":scene.objects["TheCity"], + "high":"TheCityHigh", + #"low":"TheCityLow", + "radius":1500, + "now":"high", + "name":"Dune Town" + }, + + "TheHouse":{ + "object":scene.objects["TheHouse"], + "high":"TheHouseGood", + "low":"TheHouseBorked", + "radius":200, + "now":"high", + "name":"Looparound 8\nDani's Home" + }, + + + } + + chunks = bge.logic.globalDict["LODchunks"] + + for chunkname in chunks: + chunk = chunks[chunkname] + + if chunk["now"] == "low" and dani.getDistanceTo(chunk["object"]) < chunk["radius"]: + print(consoleForm(chunk["object"]), "is now highres.") + if "low" in chunk: + chunk["object"].replaceMesh(chunk["high"]) + else: + chunk["object"].visible = True + chunk["object"].restorePhysics() + chunk["now"] = "high" + + bge.logic.globalDict["print"] = chunk["name"] + + elif chunk["now"] == "high" and dani.getDistanceTo(chunk["object"]) > chunk["radius"]: + print(consoleForm(chunk["object"]), "is now lowres.") + if "low" in chunk: + chunk["object"].replaceMesh(chunk["low"]) + else: + chunk["object"].visible = False + + if not dani.get("race"): + chunk["object"].suspendPhysics() + + chunk["now"] = "low" + + + ############ Camera UI ########### + + Map.UpdateMapUI() + + #Map.Show([0,0,0], icon="Map_Circle") + + ############ GARAGE ########## + + # The idea is that all cars inside of the garage will + # stay in the garage. + + garage = scene.objects["GarageColider"] + garageDistance = 70 + + if dani.getDistanceTo(garage) > garageDistance: + Garage.Encode() + elif dani.getDistanceTo(garage) < garageDistance - 10: + Garage.Decode() + + # Crates + # There are crates in the house, in the garage. They are rigid body objects. + # the problem is that when you go far enough away, the ground beneath them + # stops being solid, so we need to deactiave their physics. + + crates = bge.logic.globalDict["garage-crates"] + for crate in crates: + if dani.getDistanceTo(crate) > 100: + crate.suspendPhysics() + elif dani.getDistanceTo(crate) < 90: + crate.restorePhysics() + + # Tools + Tools.MainLoop() + Garage.Computer() + + + ################ DAY NIGHT CYCLE ################ + + + clock = scene.objects["Dani_Clock_Indicator"] + bge.logic.globalDict["time"] = bge.logic.globalDict["start-time"] + ( bge.logic.getRealTime() / 3600 * settings.get("clockspeed", 20) ) + while bge.logic.globalDict["time"] > 24: + bge.logic.globalDict["time"] -= 24 + timeis = bge.logic.globalDict["time"] + + # Printing the clock + hrtext = str(int(timeis)) + if len(hrtext) < 2: hrtext = "0" +hrtext + mntext = str(int((timeis-int(timeis))*60)) + if len(mntext) < 2: mntext = "0" + mntext + clock["Text"] = hrtext+":"+mntext + + # Getting the time outthere + timefactor = timeis / 24 + + + # we want 100 sunlight to be at 12 AM not at 24 hours + #timefactor -= 0.5 + #if timefactor < 0: timefactor *= -1 + #timefactor *= -2 + #timefactor += 1 + + timecontroller = scene.objects["TimeController"] + timecontroller.position.x = ( timefactor * 100 ) - 100 + + # Sun rotation + sun = scene.objects["Sun"] + sunrot = sun.worldOrientation.to_euler() + sunrot.y = ( -timefactor * math.pi * 2 ) + math.pi + sun.worldOrientation = sunrot + + # trying to limit the next operations a bit + if not bge.logic.globalDict.get("time-update-timer"): + + + TheCity = scene.objects["TheCity"] + TheCity.blenderObject["time"] = timefactor + TheCity.applyRotation((0,0,0)) + + TheRacetrack = scene.objects["TheRacetrack"] + TheRacetrack.blenderObject["time"] = timefactor + TheRacetrack.applyRotation((0,0,0)) + + Water = scene.objects["Water"] + Water.blenderObject["time"] = timefactor + Water.applyRotation((0,0,0)) + + # we want 100 sunlight to be at 12 AM not at 24 hours + timefactor -= 0.5 + if timefactor < 0: timefactor *= -1 + timefactor *= -2 + timefactor += 1 + + sun.blenderObject.data.energy = 3 * timefactor + + bge.logic.globalDict["time-update-timer"] = 500 + + else: + bge.logic.globalDict["time-update-timer"] -= 1 + + + # Background racetrack / city backdrop calcualtion + + city_at = 137 + racetrack_at = 630 + now_at = float(cam.worldPosition.y) + + if now_at > racetrack_at: crFactor = 1.0 + elif now_at < city_at: crFactor = 0.0 + else: + crFactor = (now_at - city_at) / ( racetrack_at - city_at ) + timecontroller.position.y = ( crFactor * 100 ) - 100 + + + # Moving the background up and down depending on the position of + # the camera. + + max_z = 39 + min_z = -12 + now_z = float(cam.worldPosition.z) + move_z = 14 + + if now_z > max_z: mzFactor = 0.0 + elif now_z < min_z: mzFactor = 1.0 + else: + mzFactor = 1- ((now_z - min_z) / ( max_z - min_z )) + timecontroller.position.z = ( mzFactor * move_z ) + + + + # Now to position the racetrack properly we want to rotate it + + p1 = [7.39, -805.686] + p2 = [30 , 95.5932] + cur_x = float(cam.worldPosition.x) + rotFactor = (cur_x - p1[1]) / (p2[1] - p1[1]) * ( p2[0] - p1[0] ) + p1[0] + rotationcontroller = scene.objects["Racetrack_rotaion"] + rotationcontroller.position.x = rotFactor diff --git a/Scripts/Map.py b/Scripts/Map.py new file mode 100644 index 0000000..0553f19 --- /dev/null +++ b/Scripts/Map.py @@ -0,0 +1,191 @@ +# This file is for map functions. + +# The points where the map image file ends in the +# space of the game. + +# ( Note the map is 180 degrees rotated from the in game orientation of everything. +# This is due to modeling of the map ( while Moria's Race movie was being made ) was +# made before the lore for whether this place is was made up. ) + +xs = -2633 # The most east point +xe = 2383 # The most west point +ys = -1911 # The most north point +ye = 3106 # THe most south point + +# Widths of that image file in meters. + +xd = xe - xs +yd = ye - ys + +rr = 730 # Radius to the edge of the map in meters +mr = 0.05 # Same radius by as a point on the map. +map_factor = mr / rr # The factor by which the relative point is calculated. + + +import bge +import math +import mathutils + +from Scripts import Reuse +from Scripts import Character_Controll +from Scripts import Vehicle + +def UpdateMapUI(): + + # Scene stuff + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + cam = scene.active_camera + + # Map widget + cui = scene.objects["Map_UI"] + + # Rotation + + # We use the orientation of the camera + # and rotating it 180 degrees for the map. + + r = cam.orientation.to_euler().z + r += math.pi + if r > 2*math.pi: r -= 2*math.pi + r = [0,0,r] + cui.blenderObject["orientation"] = r + + # Position + + # This one is a bit harder, since it will + # move the mapping of the image texture arround + # in the material. And it's hard to tell it just + # the coordinations ( also there is 180 degree flip ). + + dl = dani.position + + px = 1-( ( dl.x - xs ) / xd ) - 0.13 + py = 1-( ( dl.y - ys ) / yd ) - 0.12 + + p = [px,py,0] + cui.blenderObject["position"] = p + + # Map Dani Indicator + + # This is even harder. Since the map is rotated with camera + # this widget should by ofsetted by the camera rotation. + + cr = r[2] + + mdi = scene.objects["Map_Dani_Indicator"] + + rig = Character_Controll.getRig(dani) + r = (2*math.pi)-rig.worldOrientation.to_euler().z + + r += cr + if r > 2*math.pi: r -= 2*math.pi + r = [0,r,0] + + mdi.localOrientation = r + + # Removing old show marks + + database = bge.logic.globalDict.get("map-icons", {}) + for ID in list(database.keys()): + data = database[ID] + + #if data["obj"] in Reuse.reuse.get(str(data["obj"]), [])\ + + if data["updated"]+0.5 < bge.logic.getRealTime(): + + Reuse.Delete(data["icon"]) + del database[ID] + +def Show(obj, color=[1,0,0], icon="Map_Icon_Directional", ID=""): + + # This function will show things on the map. + + # Scene stuff + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + cam = scene.active_camera + cui = scene.objects["Map_UI"] + + # Making the icon database to track changes. + if "map-icons" not in bge.logic.globalDict: + bge.logic.globalDict["map-icons"] = {} + + database = bge.logic.globalDict["map-icons"] + + # Making sure that there is an object in the database + if not ID: + if type(obj) == list: ID = str(obj) + else: ID = str(id(obj)) + + if ID not in database: + database[ID] = {"obj":obj} + + data = database[ID] + data["updated"] = bge.logic.getRealTime() + + # Making sure that there is an icon in the data + + if "icon" not in data: + + # Making an icon + icon = Reuse.Create(icon, frompoint=cui) + icon.setParent(cui, True, True) + icon.worldScale = cui.worldScale * 2 + + + icon.applyRotation([math.pi/2,0,0], True) + icon.blenderObject["color"] = color + + data["icon"] = icon + + icon = data["icon"] + + # Now that we have the icon we can map it to the map + + + f = map_factor + + # Getting vector to the target + try: + t = obj.worldPosition.copy() + torot = True + except: + t = mathutils.Vector(obj) + torot = False + + + d = dani.getDistanceTo(t) + + if d > rr: # If the target out of range of the map + f = mr / d # This puts the icon on the edge of the map + + f *= cui.worldScale.x * 2 + + t = t - dani.worldPosition + + cr = cam.worldOrientation.to_euler() + cr = mathutils.Euler((0,0,-cr.z)) + t.rotate(cr) + + + t = t * f + t.z = 0.001 + + icon.worldPosition = Vehicle.RelativePoint(cui, t) + + # Rotation + if torot: + + cr = cr.z + + r = obj.worldOrientation.to_euler().z + + r += cr + if r > 2*math.pi: r -= 2*math.pi + r = [math.pi/2,0,r] + + icon.localOrientation = r + + else: + icon.localOrientation = [math.pi/2, 0,0] diff --git a/Scripts/Money.py b/Scripts/Money.py new file mode 100644 index 0000000..718e03c --- /dev/null +++ b/Scripts/Money.py @@ -0,0 +1,61 @@ +# GPLv3 or later +# (C) J.Y.Amihud ( blenderdumbass ) + +# This scripts will deal with money in the game. + +import bge +import aud + +if "money" not in bge.logic.globalDict: + bge.logic.globalDict["money"] = 0.0 +money = bge.logic.globalDict["money"] + +def Set(amount): + + global money + money = amount + UpdateUI(sound=False) + +def Get(): + return money + +def Recieve(amount): + + global money + money += amount + UpdateUI() + +def Pay(amount): + + global money + money -= amount + UpdateUI() + +def Have(amount): + + if money >= amount: return True + else: return False + +def UpdateUI(sound=True): + + scene = bge.logic.getCurrentScene() + UI = scene.objects["Dani_Money_Indicator"] + UI["Text"] = "$"+str(int(money)) + + bge.logic.globalDict["money"] = money + if sound: + Sound() + +def Sound(): + + device = bge.logic.globalDict["SoundDevice"] + + code = "//sfx/money.ogg" + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(code)), + "play":None} + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + sound["play"].volume = 2 diff --git a/Scripts/Mouse.py b/Scripts/Mouse.py new file mode 100644 index 0000000..e2bbdd2 --- /dev/null +++ b/Scripts/Mouse.py @@ -0,0 +1,57 @@ +# Gpl3 or later +# (C) J.Y.Amihud 2024 + +# Stuff related to mouse control + +import bge + +def MouseLook(cont): + MouseLookActual(cont) + +def MouseLookActual(cont, workanyway=False): + + if not bge.logic.globalDict.get("mouse-active", True): + return + + # We need this to be able disable mouse look from the + # the code. + + # Getting object that will rotate. + if type(cont) == bge.types.SCA_PythonController: + obj = cont.owner + else: obj = cont + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + + # Disabling the camera rotation + if not workanyway and dani.get("driving") and dani["driving"].get("dynamiccam", True): + return + + + # Getting mouse position on the screen. + mouse = bge.logic.mouse + pos = list(mouse.position) + + # Mouse positions are normalized to be 0.5 at the center. + # We need center to be 0.0. + pos[0] -= 0.5 + pos[1] -= 0.5 + + # Getting factor information about how to move the mouse + # from the object. + my = -obj.get("mouse_Y", 1.0) + mx = -obj.get("mouse_X", 1.0) + mg = obj.get("mouse_global", True) + + # Applying the rotation. + obj.applyRotation((pos[1]*my, 0, pos[0]*mx), mg) + + # Centring the mouse. + CenterCursor() + +def CenterCursor(): + if not bge.logic.globalDict.get("mouse-active", True): + return + bge.render.setMousePosition(int(bge.render.getWindowWidth() / 2), int(bge.render.getWindowHeight() / 2)) + diff --git a/Scripts/Multiplayer_Client.py b/Scripts/Multiplayer_Client.py new file mode 100644 index 0000000..e0bf5d2 --- /dev/null +++ b/Scripts/Multiplayer_Client.py @@ -0,0 +1,222 @@ +# GPLv3 or later +# (C) J.Y.Amihud ( blenderdumbass ) 2024 + +# Multiplayer client functions + +import bge +import time +import json +import zlib + +import traceback + +from Scripts import Vehicle +from Scripts import Character_Controll +from Scripts import Opt +from Scripts import Settings +from Scripts.Multiplayer_Shared import * +from Scripts import Reuse + +from Scripts.Common import * + + +settings = Settings.load_settings() +host = settings.get("mp-host", "") + +def DescribeScene(): + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + cam = scene.active_camera + + garage = scene.objects["GarageColider"] + garageDistance = 31 + + chunksize = 250 + + data = {} + addr = Opt.Address(dani.position, chunksize) + if addr not in data: + data[addr] = [] + + danidata = Character_Controll.Encode(dani) + data[addr].append(danidata) + + userId = bge.logic.globalDict.get("userId") + + for car in bge.logic.globalDict["allcars"]: + if car.get("inview") and car.getDistanceTo(garage) > garageDistance: + + if car.get("ownerId") and car.get("ownerId") != userId: + continue + + addr = Opt.Address(car.position, chunksize) + cardata = Vehicle.Encode(car) + if addr not in data: + data[addr] = [] + data[addr].append(cardata) + + return data + +def MainLoop(): + + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + cam = scene.active_camera + chunksize = 250 + + bge.logic.globalDict["networkQueue"] = {} + + while True: + try: + + # A bit of delay, to not owerwhelm the server + time.sleep(0.1) + + + + + # Testing if the game is still runing. + # it will fail when the game engine stops. + try: bge.logic.getRealTime() + except: return + + + if bge.logic.globalDict.get("restore-physics-timer"): + continue + if bge.logic.globalDict.get("network-scene"): + continue + + + + ######### SENDING THE DATA TO SERVER ############ + + data = {} + + # Login + LoginEncode(data) + + # Scene + data["scene"] = DescribeScene() + data["vision"] = Opt.Surround(cam.position, + chunksize, + cam.orientation.to_euler()) + + # Queue + networkQueue = bge.logic.globalDict["networkQueue"] + for key in networkQueue: + if networkQueue[key]: + data[key] = networkQueue[key].pop(0) + + + ######### DEALING WITH RESPONSE ############ + + data = Send(host, data) + + for key in data: + + payload = data[key] + + if key == "login": + LoginDecode(payload) + + elif key == "scene": + bge.logic.globalDict["network-scene"] = payload + + elif key == "message": + bge.logic.globalDict["print"] = payload + + else: + print(key, payload) + + except Exception: + print("\n\nNETWORK CLIENT ERROR:", clr["bold"]+clr["tdrd"], traceback.format_exc(), clr["bold"]) + +def SecondaryLoop(): + + # This loop runs in the game loop, not the separate thread. + # And it deals with updates sent by the other thread. + if bge.logic.globalDict.get("network-scene"): + data = bge.logic.globalDict["network-scene"] + SceneDecode(data) + bge.logic.globalDict["network-scene"] = None + +def LoginEncode(data): + + data["login"] = {} + data["login"]["userId"] = bge.logic.globalDict.get("userId") + data["login"]["username"] = settings.get("mp-name") + data["login"]["room"] = settings.get("mp-room") + +def LoginDecode(data): + + if data.get("userId"): + bge.logic.globalDict["userId"] = data["userId"] + + if data.get("room"): + settings["mp-room"] = data["room"] + +def SceneDecode(data): + + netObjects = bge.logic.globalDict["netObjects"] + + if not data: + return + + for addr in data: + chunk = data[addr] + + for obj in chunk: + + # update = True + + # if obj.get("netId") in netObjects["netId"]: + # if obj.get("ownerId"): + # netObjects["netId"][obj.get("netId")]["ownerId"] = obj.get("ownerId") + + # obj["ID"] = id(netObjects["netId"][obj.get("netId")]) + + # Sometimes we want to update some things like netId. + if obj.get("ID") in netObjects["pythonId"]\ + and obj.get("name") == netObjects["pythonId"][obj["ID"]].name: + netObjects["pythonId"][obj["ID"]]["netId"] = obj.get("netId") + netObjects["netId"][obj.get("netId")] = netObjects["pythonId"][obj["ID"]] + + + if obj.get("type") == "veh": + Vehicle.Decode(obj, network=True) + elif obj.get("type") == "chr": + Character_Controll.Decode(obj, network=True) + +def QueueToSend(key, data): + + + networkQueue = bge.logic.globalDict["networkQueue"] + + if key not in networkQueue: + networkQueue[key] = [] + + networkQueue[key].append(data) + +def GrabOwnership(obj): + + + + # This function grabs ownership of an object. + # Like when a player steals a car spawned by + # another player. + + userId = bge.logic.globalDict["userId"] + ownerId = obj.get("ownerId") + netId = obj.get("netId") + + # First we will check that the ownership isn't ours. + if userId != ownerId and netId: + + QueueToSend("change-ownership",{ + "netId" :netId, + "userId":userId + }) + + obj["ownerId"] = userId diff --git a/Scripts/Multiplayer_Server.py b/Scripts/Multiplayer_Server.py new file mode 100644 index 0000000..ae8895c --- /dev/null +++ b/Scripts/Multiplayer_Server.py @@ -0,0 +1,455 @@ +# GNU GPL v3 or later +# ( C ) J.Y.Amihud ( blenderdumbass ) 2024 + +import os +import sys +import zlib +import json +import time +import threading +import subprocess + +import Settings +from Common import * +from Multiplayer_Shared import * + + +from http.server import BaseHTTPRequestHandler, HTTPServer + +class handler(BaseHTTPRequestHandler): + + def do_POST(self): + + data = self.Recieve() + response = ParseRequest(data) + self.Send(response) + + def Send(self, data): + + # Compressing + data = json.dumps(data) + data = data.encode("utf-8") + data = zlib.compress(data) + + # Sending + self.send_response(200) + self.send_header("Content-type","application/zip") + self.end_headers() + self.wfile.write(data) + + def Recieve(self): + + length = int(self.headers.get('content-length')) + data = self.rfile.read(length) + + data = zlib.decompress(data) + data = json.loads(data) + return data + + def log_message(self, format, *args): + # I don't want any logs. + return + +def ParseRequest(data): + + resp = {} + + for key in data: + + payload = data[key] + + if "login" == key: + resp[key] = Login(payload) + + + elif "change-ownership" == key: + resp[key] = ChangeOwnership(payload) + + elif "scene" == key: + resp[key] = Scene(data) + + elif "vision" == key: + pass + + else: + print(key, payload) + + + userId = data.get("login", {}).get("userId") + room = Safe(data.get("login", {}).get("room")) + messages = LoadData("messages", {}, room) + if messages.get(userId): + resp["message"] = messages[userId].pop(0) + SaveData("messages", messages, room) + + return resp + +def Login(data): + + resp = {} + newuser = False + + # Checking userId + if not data.get("userId"): + resp["userId"] = RandomString() + + + # Checking room + if not data.get("room"): + resp["room"] = "MainRoom" + elif data.get("room") != Safe(data.get("room", "")): + resp["room"] = Safe(data["room"]) + + data["timestamp"] = time.time() + + # Storring users data. + + users = LoadData("users", {}) + + if data.get("userId") and data.get("userId") not in users: + newuser = True + + if data.get("userId"): + users[data["userId"]] = data + + SaveData("users", users) + + if newuser: + userId = data.get("userId") + name = data.get("username") + room = Safe(data.get("room")) + NotifyOthers(userId, name+" joined game.", room) + print(Format(userId), "JOINED GAME!") + + return resp + +def ChangeOwnership(data): + + print(Format(data.get("userId")), "GRABBED", Format(data.get("netId"))) + + objects = LoadData("objects", {}) + if data.get("netId") in objects: + obj = objects[data.get("netId")] + obj["ownerId"] = data.get("userId") + + SaveData("objects", objects) + + # Changing ownership in chunk + addr = obj.get("chunk") + room = Safe(obj.get("room")) + chunk = LoadData(addr, {}, room) + for o in chunk: + if o.get("netId") == data.get("netId"): + o["ownerId"] = data.get("userId") + SaveData(addr, chunk, room) + + return True + else: + print(Format(data.get("netId")),"netId:",data.get("netId"), "NOT FOUND, WHILE CHANGING OWNERSHIP!") + return False + + + +def Scene(data): + + scene = data["scene"] + userId = data.get("login", {}).get("userId") + room = Safe(data.get("login", {}).get("room", "MainRoom")) + + # We are not updating scene if there is no userId yet. + if not userId: + return + + # Reading scene payload. + + resp = {} + objects = LoadData("objects", {}) + + for addr in scene: + chunk = scene[addr] + + for obj in chunk: + + # Some people might want to cheat by inputing + # values for other users. + if obj.get("netId"): + + + obj["ownerId"] = objects.get(obj.get("netId"), {}).get("ownerId") + if not obj["ownerId"]: + obj["ownerId"] = userId + + if obj.get("ownerId") and obj.get("ownerId") != userId: + chunk.remove(obj) + + + + + # Saving chunks data. + + for addr in scene: + chunk = scene[addr] + saved = LoadData(addr, [], room) + add = [] + + # Cleaning up + for obj in saved: + if obj.get("netId") not in objects or objects[obj.get("netId")].get("chunk") != addr: + saved.remove(obj) + + ids = [] + for obj in saved: + ids.append(obj.get("netId")) + + for obj in chunk: + + # If object is Dani and it has no netId + # we assume that somebody new joined the + # room. + + if obj.get("name") == "Dani_Box" and not obj.get("netId"): + obj["netId"] = userId + obj["ownerId"] = userId + add.append(obj) + + + elif not obj.get("netId"): + obj["netId"] = RandomString() + obj["ownerId"] = userId + add.append(obj) + + if obj.get("netId") not in ids: + saved.append(obj) + + else: + saved[ids.index(obj.get("netId"))] = obj + + + objects[obj["netId"]] = {"chunk":addr, + "name":obj.get("name"), + "timestamp":time.time(), + "ownerId":obj.get("ownerId"), + "room":room} + + SaveData(addr, saved, room) + + if addr in data.get("vision", []): + resp[addr] = [] + for obj in saved: + if obj.get("ownerId") != userId: + resp[addr].append(obj) + for obj in add: + resp[addr].append(obj) + + + SaveData("objects", objects) + + + + return resp + +def Notify(userId, message, room): + + messages = LoadData("messages", {}, room) + if userId not in messages: + messages[userId] = [] + messages[userId].append(message) + SaveData("messages", messages, room) + +def NotifyOthers(userId, message, room): + + users = LoadData("users", {}) + for user in users: + if user != userId and users[user].get("room") == room: + Notify(user, message, room) + +def SaveData(name, data, room=None): + + # Making folder for server stuff + folder = Settings.get_settings_folder()+"/server/" + if room: folder = folder+str(room)+"/" + try: os.makedirs(folder) + except: pass + + # Saving the json + with open(folder+str(name)+".json", "w") as save: + json.dump(data, save, indent=4) + +def LoadData(name, otherwise=None, room=None): + + try: + folder = Settings.get_settings_folder()+"/server/" + if room: folder = folder+str(room)+"/" + with open(folder+str(name)+".json") as o: + return json.load(o) + except: return otherwise + +def Safe(string): + + # This function stripts strings from any unsafe + # characters. + + good = "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM_" + new = "" + for i in string: + if i in good: new = new + i + else: new = new + "_" + + return new + +def Format(netId, room=True): + + objects = LoadData("objects",{}) + users = LoadData("users",{}) + + if netId in users: + name = users[netId].get("username", "Unknown") + room = users[netId].get("room", "Unknown Room") + ownerId = netId + owner = "" + else: + name = objects.get(netId, {}).get("name", "Unknown Object") + room = objects.get(netId, {}).get("room", "Unknown Room") + ownerId = objects.get(netId, {}).get("ownerId") + owner = users.get(ownerId, {}).get("username", "Unknown") + + string = IDcolor(room)+" "+room+" "+IDcolor(netId)+" "+netId[-4:]+" "+name + if owner and ownerId: + string = string + " " + IDcolor(ownerId) + " by "+ownerId[-4:]+" " + owner + string = string + " " + clr["norm"] + + return string + + + +###### RUNNING THE SERVER ##### + +def ArgumentsHandler(): + + port = 6969 + ipv6 = "-4" + + if "--help" in sys.argv or "-h" in sys.argv: + print(""" +This is the Multiplayer server for Dani's Race. +It is technically an HTTP server that handles +POST requests send by the game. Those requests +are in a JSON format compressed with python's +ZLIB library. There is no webpage, so trying to +access it with a browser will probably spit out +an error. + +"""+clr["bold"]+clr["tdyl"]+"""RUN THIS SERVER FROM THE "Scripts" FOLDER!"""+clr["norm"]+""" + +--help , -h : This Help Text. +--port """+clr["tdyl"]+""""""+clr["norm"]+""" : Sets up a port on which the + server will listen. +--ipv6 , -6 : Use IPv6 connection for global network. + +""") + exit() + + if "--port" in sys.argv: + try: + port = int(sys.argv[ sys.argv.index("--port")+1 ]) + except: + print("Didn't specify port number.\nExample ( for port 8080 ): $ python3 Multiplayer_Server.py --port 8080") + exit() + + if "--ipv6" in sys.argv or "-6" in sys.argv: + ipv6 = "-6" + + return port, ipv6 + +PORT, IPV6 = ArgumentsHandler() + +print(clr["bold"]+"Dani's Race Multiplayer Server!"+clr["norm"]) + +###### CLEANUPS ##### + +def CleanUps(): + + while True: + + time.sleep(3) + + # Cleaning up users + + users = LoadData("users", {}) + delete = [] + for userId in users: + user = users[userId] + room = user.get("room") + name = user.get("username") + + # If user missing for 10 seconds, he is gone. + if user.get("timestamp", 0) < time.time() -10: + + NotifyOthers(userId, name+" left the game.", room) + print(Format(userId), "LEFT GAME!") + delete.append(userId) + + for i in delete: + del users[i] + + SaveData("users", users) + + objects = LoadData("objects", {}) + delete = [] + for netId in objects: + obj = objects[netId] + + # Deleting objects after 5 missing seconds + if obj.get("timestamp", 0) < time.time() -5: + delete.append(netId) + + for i in delete: + del objects[i] + + SaveData("objects", objects) + + +cleanups = threading.Thread(target=CleanUps) +cleanups.daemon = True +cleanups.start() +print("Started cleanups thread.") + + +print(clr["bold"]+"Starting server:", clr["norm"]) + +try: + IP = subprocess.check_output(["hostname", "-I"]).decode("utf-8").split(" ")[0] +except: + try: + # Some versions of hostname just have the -i option that acts like -I in + # other versions. + IP = subprocess.check_output(["hostname", "-i"]).decode("utf-8").split(" ")[0] + except: + IP = clr["tdrd"]+"127.0.0.1"+clr["norm"] + +if not IP or IP == "\n": + IP = clr["tdrd"]+"127.0.0.1"+clr["norm"] +print(" Local IP:", clr["bold"], IP, clr["norm"]) + +try: + GLOBALIP = subprocess.check_output(["curl", "-s", IPV6, "ifconfig.co"]).decode("utf-8")[:-1] + print(" Global IP:", clr["bold"], GLOBALIP, clr["norm"]) +except: + GLOBALIP = None + print(" Global IP:",clr["tdrd"]+clr["bold"], "No connection to Global Network.", clr["norm"]) + +print(" Port:", clr["bold"], PORT, clr["norm"]) +print(" Local Hostname:", clr["bold"], "http://"+IP+":"+str(PORT), clr["norm"]) +if GLOBALIP: + if IPV6 == "-4": + print(" Global Hostname:", clr["bold"], "http://"+GLOBALIP+":"+str(PORT), clr["norm"]) + else: + print(" Global Hostname:", clr["bold"], "http://["+GLOBALIP+"]:"+str(PORT), clr["norm"]) + +print() +serve = HTTPServer(("", PORT), handler) +try: + serve.serve_forever() +except KeyboardInterrupt: + print() + print("Server Exited!") diff --git a/Scripts/Multiplayer_Shared.py b/Scripts/Multiplayer_Shared.py new file mode 100644 index 0000000..40d25e2 --- /dev/null +++ b/Scripts/Multiplayer_Shared.py @@ -0,0 +1,40 @@ +# GPLv3 or later +# ( C ) J.Y.Amihud ( blenderdumbass ) 2024 + +import os +import json +import zlib +import random +import urllib.request +import urllib.parse + +def RandomString(size=16): + + # This will generate random strings, primarily + # for the purpose of recognizing the same objects + # on the network. + + good = "1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM" + + text = "" + for i in range(size): + text = text + random.choice(good) + + return text + +def Send(host, data): + + # Compressing + data = json.dumps(data) + data = data.encode("utf-8") + data = zlib.compress(data) + + # Sending + req = urllib.request.Request(host, data=data) + + # Recieving + data = urllib.request.urlopen(req).read() + data = zlib.decompress(data) + data = json.loads(data) + + return data diff --git a/Scripts/Opt.py b/Scripts/Opt.py new file mode 100644 index 0000000..96040e5 --- /dev/null +++ b/Scripts/Opt.py @@ -0,0 +1,508 @@ +import bge +import math +import numpy +import mathutils + +r = math.radians + +from Scripts import Reuse +from Scripts.Common import * + +# Chunks to store data about parts of the world +chunks = {} +bge.logic.globalDict["Opt.chunks"] = chunks + +camspeed = 0.0 +camposition = mathutils.Vector((0,0,0)) +camspeedframe = 0.0 + +def CameraSpeed(): + + # This function gives the average speed of the + # camera at any given frame. + frame = round(bge.logic.getRealTime(), 2) + + global camspeed + global camposition + global camspeedframe + + if frame != camspeedframe: + camspeedframe = frame + + # Getting velocity of the camera + p = bge.logic.getCurrentScene().active_camera.worldPosition + v = p - camposition + camposition = p.copy() + + # Averaging the velocity + av = 0 + for i in v: + if i > 0: av += i + else : av -= i + av /= 3 + + # Storing the value + camspeed = av + + # Returning the value + return camspeed + + + + +def ToFPS(): + + # Return a fraction that is useful to adjust FPS + # sensitive values like gravity. + + return 60/bge.logic.getAverageFrameRate() + +# Global Variables for GoodFPS function to work. +fpshistory = {} # History of last 200 or so frames FPS +targetFPS = 1 # Target FPS which to aim to preserve +FPStargets = {} # Database of how much a given function effects FPS +testingTarget = False # Fuction that is being tested for how much it effects FPS +testingTargetFrame = 0 # Frame where the test is started +testFPS = 60 # The FPS before the test started +limitframe = 0 # The Frame that is currently being calculated for expected FPS +expectedfps = 60 # Expected FPS with running functions tested effects removed from current FPS + + +def GoodFPS(func="", factor=1, boolean=True, traceback=False, ): + + + + global targetFPS + + # Recording the framerate into buffer + # This buffer will be used to regulate the target FPS + + fps = bge.logic.getAverageFrameRate() + frame = round(bge.logic.getRealTime(), 2) + camspeed = min(CameraSpeed(), 1) + global fpshistory + fpshistory[frame] = fps + + # Limiting execution based on camera speed. + if boolean and numpy.random.choice([True, False], p=[camspeed, 1-camspeed]): + return False + + # Setting the target fps + if frame > 5:# and fps > targetFPS: + targetFPS = max(fpshistory.values()) + targetFPS *= 1.1 + #targetFPS = min(targetFPS, 30) + + # Slicing buffer to contain only last 300 records + newfpshistory = {} + for i in list(fpshistory.keys())[-300:]: + newfpshistory[i] = fpshistory[i] + fpshistory = newfpshistory + + # Calculating the fraction ( of how much are we within the target FPS ) + fraction = ( sum( min(f, targetFPS) for f in fpshistory.values()) / len(fpshistory) ) / targetFPS + + # Testing FPS impacts of various functions to see + # how much to limit them. + global FPStargets + global testFPS + global testingTarget + global testingTargetFrame + + # This runs when the target for testing is already setup + if testingTarget: + + # If we have some decrease in performace, we calculate how much it effects the FPS + if frame > testingTargetFrame and fps+1 < testFPS: + FPStargets[testingTarget] = {"fraction":fps / testFPS, "frame":frame} + testingTarget = False + + # Otherwise we cancel the test + elif frame -2 > testingTargetFrame: + testingTarget = False + + # While we testing, we want to return False, so nothing else will effect the FPS + if boolean: return False + else: return 0.0 + + # This runs to start the test + if ( func and func not in FPStargets and 10 < frame or FPStargets.get(func, {}).get("frame", frame) + 60 < frame ) and fraction > factor: + testFPS = fps + testingTarget = func + testingTargetFrame = frame + + # And we want to return True so the function will get launched. + if boolean: return True + else: return 1.0 + + # Limiting functions based on the tests + global limitframe + global expectedfps + + # Findinf the limiting factor + try: + targetFactor = min(n.get("fraction") for n in FPStargets.values()) * factor + except: + targetFactor = factor + + # Limiting + if expectedfps * FPStargets.get(func, {}).get("fraction", 1) < targetFPS * targetFactor: + if boolean: return False + + # If we allow the function to run + # we want to recalculate the expected FPS + # based on the tests + if limitframe != frame: + + # If the tests data is flawed + if int(expectedfps) > int(fps)+2: + FPStargets = {} + + # Calculating expected FPS + expectedfps = fps*FPStargets.get(func, {}).get("fraction", 1) + limitframe = frame + else: + expectedfps *= FPStargets.get(func, {}).get("fraction", 1) + + # Returning + if boolean: + return True#fraction > factor + else: + return fraction + + +def Address(location, precision): + + # This function will return an adress of any given location + # Which could be used to compare between objects to see if they + # are close enough together. + + ret = "" + for axis in location[:2]: + ret = ret + str(round(axis/precision))+":" + return ret + +def Surround(location, precision, camera=None): + + # This function will give a list of addresses around a certain point. + + ret = [] + ORL = [] + addedtypes = [] + for axis in location[:2]: + ORL.append(round(axis/precision)) + + ret.append(Address(ORL, 1)) + + + + if not camera: + # X + if (location[0]/precision) - round(location[0]/precision) < 0.5: + ret.append(Address([ORL[0]-1, ORL[1]], 1)) + addedtypes.append("-x") + elif (location[0]/precision) - round(location[0]/precision) > 0.5: + ret.append(Address([ORL[0]+1, ORL[1]], 1)) + addedtypes.append("+x") + + # Y + if (location[1]/precision) - round(location[1]/precision) < 0.5: + ret.append(Address([ORL[0], ORL[1]-1], 1)) + addedtypes.append("-y") + elif (location[1]/precision) - round(location[1]/precision) > 0.5: + ret.append(Address([ORL[0], ORL[1]+1], 1)) + addedtypes.append("+y") + + # Diagonals + if "+x" in addedtypes and "+y" in addedtypes: + ret.append(Address([ORL[0]+1, ORL[1]+1], 1)) + elif "-x" in addedtypes and "-y" in addedtypes: + ret.append(Address([ORL[0]-1, ORL[1]-1], 1)) + elif "-x" in addedtypes and "+y" in addedtypes: + ret.append(Address([ORL[0]-1, ORL[1]+1], 1)) + elif "+x" in addedtypes and "-y" in addedtypes: + ret.append(Address([ORL[0]+1, ORL[1]-1], 1)) + + else: + if r(70) >= camera[2] >= r(-70): + ret.append(Address([ORL[0], ORL[1]+1], 1)) + + if r(25) >= camera[2] >= r(-115): + ret.append(Address([ORL[0]+1, ORL[1]+1], 1)) + + if r(-20) >= camera[2] >= r(-160): + ret.append(Address([ORL[0]+1, ORL[1]], 1)) + + if r(-65) >= camera[2] >= r(-180) or r(180) > camera[2] >= r(155): + ret.append(Address([ORL[0]+1, ORL[1]-1], 1)) + + if r(-110) >= camera[2] >= r(-180) or r(180) > camera[2] >= r(110): + ret.append(Address([ORL[0], ORL[1]-1], 1)) + + if r(-155) >= camera[2] >= r(180) or r(180) > camera[2] >= r(65): + ret.append(Address([ORL[0]-1, ORL[1]-1], 1)) + + if r(160) >= camera[2] >= r(20): + ret.append(Address([ORL[0]-1, ORL[1]], 1)) + + if r(115) >= camera[2] >= r(-25): + ret.append(Address([ORL[0]-1, ORL[1]+1], 1)) + + + + return ret + +previousSurround = {} + +def SurroundChanged(key, surround): + + # This returns whether surround was changed between frames + + if key not in previousSurround: + previousSurround[key] = surround.copy() + + changed = surround != previousSurround[key] + + previousSurround[key] = surround.copy() + + return changed + +def RegisterObject(object, precision, delete=True): + + # This will move an object from scene.objects into + # chunks. So that they could be stored without + # effecting the BGE Depsgraph performance. + + # Creating chunk + addr = Address(object.position, precision) + if addr not in chunks: + chunks[addr] = {"loaded":False, + "objects":[]} + + # Adding properties + virtualObject = {} + virtualObject["name"] = object.name + virtualObject["position"] = object.position.copy() + virtualObject["orientation"] = object.orientation.to_euler() + virtualObject["scaling"] = object.scaling.copy() + + for i in object.blenderObject.game.properties: + virtualObject[i.name] = i.value + + + # Adding the object to the chunk + chunks[addr]["objects"].append(virtualObject) + + # Deleting real object + if delete: + object.endObject() + + # Returning + return virtualObject + +def SortByDistance(l, cam, withdistance=False): + + # This sorts a chunk by the distance + distanced = [] + for n, i in enumerate(l): + d = cam.getDistanceTo(i["position"]) + distanced.append([d, n, i]) + + distanced = sorted(distanced) + + if withdistance: + return distanced + + ret = [] + for i in distanced: + ret.append(i[2]) + return ret + +def UpdateScene(camobject, precision, camera=None): + + # This function will update the scene. According to the camera + # view. + + location = camobject.position + + # Getting addresses + addrs = Surround(location, precision, camera) + + # Checking if any of addresses are not up to date. + for addr in chunks: + + # Delete object if address not in view + + # It is done in a separate loop and first, + # because we might need elements from it + # to spawn later. Otherwise we might cache + # way too many objects into memory. + + if addr not in addrs and chunks[addr]["loaded"]: + for object in chunks[addr]["objects"]: + if object.get("object"): + Reuse.Delete(object["object"]) + object["object"] = None + chunks[addr]["loaded"] = False + + # Breaking to skip to next frame and + # re-evaluate whether the FPS is good + return + + lookthroughchunks = chunks + if addrs[0] in chunks: + lookthroughchunks = [chunks[addrs[0]]]+list(chunks.keys()) + + + # Fixing random items at the chunks. + for i in range(5): + addr = random.choice(list(chunks.keys())) + item = random.choice(chunks[addr]["objects"]) + if item.get("broken"): + item["broken"] = False + print(consoleForm(item["name"]), "Fixed") + + + for addr in chunks: + + # Create objects when addres in view + if addr in addrs and not chunks[addr]["loaded"]: + + for object in SortByDistance(chunks[addr]["objects"], camobject): + if not object.get("object") and not object.get("broken"): + object["object"] = Reuse.Create(object["name"]) + object["object"].position = object["position"] + object["object"].orientation = object["orientation"] + object["object"].scaling = object["scaling"] + + # Some objects have a height adjustment value + object["object"].position[2] += object["object"].get("movez", 0) + + # Some effects require a reference point + object["object"]["spawned_by"] = object + + # Some objects need dynamics to be suspended + if object.get("suspendDynamics"): + object["object"].suspendDynamics(True) + + # Some objects might change mesh during game + if object["object"].get("good"): + object["object"].replaceMesh(object["object"]["good"]) + + chunks[addr]["loaded"] = True + + # Breaking to skip to next frame and + # re-evaluate whether the FPS is good + return + + # Level Of Details for spawn objects + + # This is needed because normal LOD system + # with those objects sometimes gives a + # segmentation fault. + + elif addr in addrs and chunks[addr]["loaded"]: + + sortedObjects = SortByDistance(chunks[addr]["objects"], camobject, True) + +# closestUnspawned = [1000000, 0, {}] +# for object in sortedObjects: +# if not object[2].get("object") and object[0] < closestUnspawned[0]: +# closestUnspawned = object +# +# if Reuse.reuse.get(closestUnspawned[2].get("name")): +# closestUnspawned[2]["object"] = Reuse.Create(closestUnspawned[2].get("name")) +# closestUnspawned[2]["object"].position = closestUnspawned[2]["position"] +# closestUnspawned[2]["object"].orientation = closestUnspawned[2]["orientation"] +# closestUnspawned[2]["object"].scaling = closestUnspawned[2]["scaling"] +# +# # Some objects have a height adjustment value +# closestUnspawned[2]["object"].position[2] += closestUnspawned[2]["object"].get("movez", 0) +# +# # Some effects require a reference point +# closestUnspawned[2]["object"]["spawned_by"] = closestUnspawned[2] +# +# # Some objects need dynamics to be suspended +# if closestUnspawned[2].get("suspendDynamics"): +# closestUnspawned[2]["object"].suspendDynamics(True) + + + + for distance, n, object in sortedObjects: +# +# if object["name"] == closestUnspawned[2].get("name") and distance > closestUnspawned[0] and object.get("object"): +# Reuse.Delete(object["object"]) +# object["object"] = None + + if "lods" in object and object.get("object"): + distance = object["object"].getDistanceTo(location) + for lod in object["lods"]: + + lodDistance = object["lods"][lod] + if int(distance) <= lodDistance: + break + + if object["object"].name != lod: + + Reuse.Delete(object["object"]) + object["object"] = Reuse.Create(lod) + object["object"].position = object["position"] + object["object"].orientation = object["orientation"] + object["object"].scaling = object["scaling"] + + # Some objects have a height adjustment value + object["object"].position[2] += object["object"].get("movez", 0) + + # Some effects require a reference point + object["object"]["spawned_by"] = object + + # Some objects need dynamics to be suspended + if object.get("suspendDynamics"): + object["object"].suspendDynamics(True) + +taskschedule = {} + +def ScheduleTask(taskname, FPSfactor, task, *args): + + # This function adds a function to the schedule + + taskObject = {"FPSfactor":FPSfactor, + "task":task, + "args":args} + taskschedule[taskname] = taskObject + +def ExecuteScheduled(): + + # This function executes a scheduled task + +# for taskname in taskschedule: +# print("Scheduled:", taskname) +# print() + + # Trying to execute something + for taskname in taskschedule: + + taskObject = taskschedule[taskname] + + if GoodFPS("[Scheduled] "+taskname, taskObject["FPSfactor"]): + del taskschedule[taskname] + taskObject["task"](*taskObject["args"]) + return + +def Force(value): + + # This function adjusts acceleration + # to fps + + return value * ( ToFPS() ** 2 ) + +def Velocity(value): + + # This function adjusts velocity + # to fps + + return value * ToFPS() + +def PrintTime(): + # This function is used for testing + print(bge.logic.getRealTime()) diff --git a/Scripts/Reuse.py b/Scripts/Reuse.py new file mode 100644 index 0000000..9fb5250 --- /dev/null +++ b/Scripts/Reuse.py @@ -0,0 +1,138 @@ +import bge +from Scripts.Common import * + +# This script is to spawn objects smartly + +# We will need this +scene = bge.logic.getCurrentScene() + +# We need a dictionary to save objects for reuse +if "reuse" not in bge.logic.globalDict: + bge.logic.globalDict["reuse"] = {} + bge.logic.globalDict["reuse-amounts"] = {} + bge.logic.globalDict["self-destruct"] = [] + +# For easy access +reuse = bge.logic.globalDict["reuse"] +amounts = bge.logic.globalDict["reuse-amounts"] +selfDestruct = bge.logic.globalDict["self-destruct"] + +# Function to spawn objects +def Create(object, selfDestructFrames=0, selfDestructInactive=False, visible=True, declarenew=False, frompoint=None): + + # Making a list of those objects in reuse dictionary. + if object not in reuse: + reuse[object] = [] + + # If the list is empty ( we need more objects ) we make a new one. + if not reuse[object]: + if not frompoint: frompoint = object + object = scene.addObject(object, frompoint, 0, False) + new = True + + if object.name not in amounts: + amounts[object.name] = 0 + amounts[object.name] += 1 + + #print(clr["bold"]+clr["tdyl"]+"Reuse New"+clr["norm"]+":", object, amounts[object.name]) + + else: + object = reuse[object].pop(0) + object.restorePhysics() + object.worldLinearVelocity = [0,0,0] + object.worldAngularVelocity = [0,0,0] + new = False + + # If self descructing + if selfDestructFrames: + SelfDestruct(object, selfDestructFrames, selfDestructInactive) + + object.setVisible( visible ) + if declarenew: + return object, new + else: + return object + + + +def Delete(object, inactive=False): + + # To make this callable from logic bricks the next thing is needed. + try: + object = object.owner + except: + pass + + # Making a list of those objects in reuse dictionary. + # Technically if Create() was use to create this object it's + # not needed, but incase other objects will be stored like this. + + if object.name not in reuse: + reuse[object.name] = [] + + # Sometimes just recording that the object is available is enough: + if not inactive: + + # Instead of deleting we are going to store it for later + + object.worldLinearVelocity = [0,0,0] + object.worldAngularVelocity = [0,0,0] + object.scaling = [1,1,1] + object.suspendPhysics() + object.position = [0, 0, -1000] # Putting it outside of the playable area ( in case there are parented parts ) + object.visible = False + + # For some objects + if "Motion" in object.actuators: + object.actuators["Motion"].dLoc = [0,0,0] + + + # Storing the object for later use + if object not in reuse[object.name]: + reuse[object.name].append(object) + + + # Making sure it will not self distract again after it is reused + object["self-destruct"] = 2 + if object in selfDestruct: + selfDestruct.remove(object) + + +def SelfDestruct(object, frames, inactive=False): + + # Function that will make objects ( like particles ) self terminate: + + # We will add the object into a dict + object["self-destruct"] = frames + object["self-destruct-inactive"] = inactive + selfDestruct.append(object) + +def SelfDestructDo(): + + # Happening on update to self destruct objects + + for object in selfDestruct: + object["self-destruct"] -= 1 + if object["self-destruct"] <= 0: + Delete(object, object["self-destruct-inactive"]) + if object in selfDestruct: + selfDestruct.remove(object) + +def EndObject(object): + + # Actually end object + + # Reuding the number of the object in amounts + if object.name in amounts: + amounts[object.name] -= 1 + + print(clr["bold"]+clr["tdrd"]+"Reuse End Object"+clr["norm"]+":", object, amounts[object.name]) + + # Removing the object from reuse if it is in reuse + if object in reuse.get(object.name): + reuse[object.name].remove(object) + + # Removing all object's children, and the object. + for i in object.childrenRecursive: + i.endObject() + object.endObject() \ No newline at end of file diff --git a/Scripts/Script.py b/Scripts/Script.py new file mode 100644 index 0000000..f7b6e4d --- /dev/null +++ b/Scripts/Script.py @@ -0,0 +1,819 @@ +# This file contains all of the scripted events of the game's story. +# And some logic to go with it. + +import bge +import aud +from Scripts import Reuse +from Scripts import Character_Controll +from Scripts import Vehicle +from Scripts import Map +from Scripts import Main_update +from Scripts import Garage + +from Scripts.Common import * + +Story = {"currentEvent":"01_Bring_Neonspeedster_To_Racetrack","passed":[]} +Speakers = {} + +def Run(scene, dani): + + # This function runs the script + + if Story["currentEvent"] in Story: + Story[Story["currentEvent"]](scene, dani) + + + # This needed incase some character started speaking + RemoveSpeakerAnimation() + +def RingPhone(): + + # This function rings the phone. + + bge.logic.globalDict["print"] = "Press P to answer the phone.\nPress H to hang up." + + + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/music/ringtone.ogg" + + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + sound["play"] = device.play(sound["sound"]) + sound["play"].volume = 0.5 + + return sound["play"] + +def AnsweredPhone(): + + # This function answers phone if player presses P + + sound = Story["ringing"] + + # Answer Phone + if keycodes["P"] in bge.logic.globalDict["keys"]: + sound.stop() + return True + + # Hang Up + elif keycodes["H"] in bge.logic.globalDict["keys"]: + sound.stop() + return False + + # Do nothing + else: + return False + +def PlayDialogue(number, character=None): + + # This function plays dialogue from the specific scene. + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/voices/scenes/"+Story["currentEvent"]+"/"+number+".ogg" + + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + sound["play"] = device.play(sound["sound"]) + sound["play"].volume = 2 + + if character: + + sound["play"].location = character.worldPosition + sound["play"].velocity = character.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 0 + sound["play"].volume = 2 + + # Setting speaking animation + character["speaking"] = True + + # Removing the speaking animation + # when the sound file is finished + Speakers[character.name] = {"sound":sound, "character":character} + + + return sound["play"] + +def RemoveSpeakerAnimation(): + + # This function removes speaker animations + # for when characters speak. + + for charactername in Speakers: + speaker = Speakers[charactername] + character = speaker["character"] + sound = speaker["sound"] + + if not sound["play"].status: + character["speaking"] = False + +def emote(dani, data, sound): + + # This function will set off face animations + # based on the data and sound timing. + + for emotion in data: + + start, end = emotion[0] + emotion = emotion[1] + + if start < sound.position < end: + Character_Controll.ApplyAnimation(dani, emotion) + +def StatusText(text, bold=[]): + + # This function prints a status text that is visible all + # the time. + + scene = bge.logic.getCurrentScene() + st = scene.objects["Game_Status_Text"] + + if "status-text" not in bge.logic.globalDict: + bge.logic.globalDict["status-text"] = "" + + if text != bge.logic.globalDict["status-text"]: + + # Updating text + # I'm doing this and not `st["Text"] = text` because + # I want it it update the blender object data now. + # Since next part will be to boldify the text. + + st.blenderObject.data.body = text + + # Storing the text value, so it will not update on every frame. + bge.logic.globalDict["status-text"] = text + bge.logic.globalDict["status-bold"] = "" + + if "status-bold" not in bge.logic.globalDict: + bge.logic.globalDict["status-bold"] = "" + + if bold != bge.logic.globalDict["status-bold"]: + + bge.logic.globalDict["status-bold"] = bold + + # Boldifying the text + for n, i in enumerate(st.blenderObject.data.body_format): + if n in bold: + i.use_bold = True + i.material_index = 0 + else: + i.use_bold = False + i.material_index = 1 + +def StatusUpdate(stages, stage): + + t = "" + bold = [] + for n, s in enumerate(stages): + + if n == stage: + bold = range(len(t), len(t)+len(s)+5+3) + t = t + "-> " + + if stage > n: + t = t + s + " <+>\n" + else: + t = t + s + " < >\n" + + + + try:StatusText(t, bold) + except Exception as e: print("error", e) + + +############################################################################## + +# Mission : Deliver Neonspeedster to Racetrack. +# Challenge: Not to break the car. + +def scene(scene, dani): + + # First we will make the phone called to Dani. + # He needs to pick it up to start the mission itself. + # The phone call should only ring when Dani is close to the house + # since we don't want the player to be near the Racetrack and + # see that Paps isn't there yet. We will spawn him there later. + + stages = [ + "Find Neonspeedster", + "Deliver it to Paps" + ] + + house = scene.objects["TheHouse"] + pointer = scene.objects["PointingArrow"] + targetLocation = [-62.82, 967.5, 4.738] + + # Showing on the map + if not dani.get("race"): + Map.Show(house, icon="Map_Circle", color=[1,0.8,0]) + + if dani.getDistanceTo(house) < 200 and not dani.get("race"): + + # If at least 20 seconds have been passed from the start of the game. + + if bge.logic.getRealTime() > Story.get("lastring", 20): + + Story["ringing"] = RingPhone() + Story["lastring"] = bge.logic.getRealTime() + 100 + + # If the phone is still ringing we can answer it + if "ringing" in Story and Story["ringing"].status and AnsweredPhone(): + + del Story["ringing"] + Story["dialogue01"] = PlayDialogue("brief") + dani["race"] = Story["currentEvent"] + + # Animation of dani holding the phone + if "dialogue01" in Story: + Character_Controll.ApplyAnimation(dani, "AnswerPhone") + + # Emoting to the phone conversation + emotion_data = [ + [[ 3.1 , 3.6 ], "Talk" ], + [[ 8.6 , 8.9 ], "Laugh"], + [[ 18.1 , 19.0 ], "Talk" ] + ] + emote(dani, emotion_data, Story["dialogue01"]) + + + # When we finished listening to it activate the event + if "dialogue01" in Story and not Story["dialogue01"].status: + + del Story["dialogue01"] + bge.logic.globalDict["print"] = "Find the Neonspeedster.\nIt should be in the garage." + Story["stage0"] = True + StatusUpdate(stages, 0) + + # Animation of putting away the phone + Character_Controll.ApplyAnimation(dani, "PutPhoneAway") + + # We we will a neonspeedste at a specific point in the garage for this to work + # relying on the spawn system is stupid, since it might decide that we will not + # get a NeonSpeedster + + NeonSpeedsterPosition = [-754.61, -1043.9, 409.32] + NeonSpeedsterOrientation = [0, 0, -1.79] + + TruckPosition = [-44.62, 960.8, 4.738] + + if "stage1" not in Story and not "NeonSpeedster" in Story and dani.getDistanceTo(NeonSpeedsterPosition) < 50: + + f = False + for c in Garage.Inside(): + if c.name == "NeonSpeedsterBox": + Story["NeonSpeedster"] = c + f = True + + if not f: + # We need properly declare the car, otherwise it will break things + Story["NeonSpeedster"] = Vehicle.Spawn("NeonSpeedsterBox", + NeonSpeedsterPosition, + NeonSpeedsterOrientation) + + # Showing all neonspeedsters on the map + if "stage0" in Story and str(dani.get("drving")) != "NeonSpeedsterBox": + + for car in bge.logic.globalDict["allcars"]: + if car.name == "NeonSpeedsterBox": + Map.Show(car, color=Vehicle.GetColor(car)) + + # As soon as dani found the Neonspeedster, we activate the second stage + if "stage0" in Story and dani["driving"] and dani["driving"].name == "NeonSpeedsterBox": + + del Story["stage0"] + try: del Story["NeonSpeedster"] + except: pass + + bge.logic.globalDict["print"] = "Drive the Neonspeedster to the Racetrack.\nNot a single scratch!" + pointer.visible = True + + # Making a cylinder where the mission ends + Story["stage1"] = Reuse.Create("Starter.Cylinder") + Story["stage1"].position = targetLocation + Story["stage1"].scaling = [8,8,1] + StatusUpdate(stages, 1) + + # Spawning Paps To make sure he is there to recieve + # the car + Reuse.Delete(scene.objects["PapsBox"]) + Story["Paps"] = Reuse.Create("PapsBox") + Story["Paps"].position = [-54.875, 972.66, 4.7383] + + # He spawns in an area where the physics arent enabled for + # the ground yet. + Story["Paps"].suspendDynamics(True) + Story["Paps"]["deactivated"] = True + + + # Same thing but for Moria + Story["Moria"] = Reuse.Create("MoriaBox") + Story["Moria"].position = [-56.875, 972.66, 4.7383] + Story["Moria"].suspendDynamics(True) + + # And Jack + Story["Jack"] = Reuse.Create("JackBox") + Story["Jack"].position = [-52.875, 972.66, 4.7383] + Story["Jack"].suspendDynamics(True) + + + + # Fixing Pap's dynamics and making paps go at Dani + if "Paps" in Story and Story["Paps"].getDistanceTo(dani) < 500: + + if Story["Paps"]["deactivated"]: + Story["Paps"].restoreDynamics() + Story["Moria"].restoreDynamics() + Story["Jack"].restoreDynamics() + Story["Paps"]["deactivated"] = False + + + + # Draw a pointer to the racetrack + if "stage1" in Story: + + # Showing the racetrack on the map + Map.Show(targetLocation, icon="Map_Circle", color=[1,0.8,0]) + + # Point the pointer to the racetrack + tocheck = pointer.getVectTo(targetLocation) + pointer.alignAxisToVect(tocheck[1], 1, 0.1) + pointer.alignAxisToVect( (0,0,1), 2, 1.0 ) + + + # Spawning the truck that paps was talking about near paps + if "Truck" not in Story and dani.getDistanceTo(TruckPosition) < 200: + Story["Truck"] = Vehicle.Spawn("TruckBox", + TruckPosition, + [0,0,0]) + + + # Deciding on the mission + if dani["driving"] and dani["driving"].name == "NeonSpeedsterBox" and dani["driving"].getDistanceTo(targetLocation) < 8: + + StatusUpdate(stages, 2) + + #Character_Controll.ApplyAnimation(Story["Paps"], "Stand") + + Reuse.Delete(Story["stage1"]) + del Story["stage1"] + + if dani["driving"]["health"] >= 0.99: + bge.logic.globalDict["print"] = "Excellent Job!" + Story["dialogue02"] = PlayDialogue("win", Story["Paps"]) + + Story["verdict"] = "won" + Story["Neonspeedster"] = dani["driving"] + + else: + bge.logic.globalDict["print"] = "You Failed! There was a scratch!" + Story["dialogue02"] = PlayDialogue("lose", Story["Paps"]) + + Story["verdict"] = "lost" + + # While the paps is telling you that you won + if "dialogue02" in Story: + + emotion_data = [ + [[ 1.15 , 8 ], "Talk" ] + ] + + emote(Story["Paps"], emotion_data, Story["dialogue02"]) + + + + if 50 > Story["Paps"].getDistanceTo(dani) > 4: + Character_Controll.walkToward(Story["Paps"], dani) + if 50 > Story["Moria"].getDistanceTo(dani) > 4: + Character_Controll.walkToward(Story["Moria"], dani) + if 50 > Story["Jack"].getDistanceTo(dani) > 4: + Character_Controll.walkToward(Story["Jack"], dani) + + + + # When the dialog ende# d. + if not Story["dialogue02"].status: + + # Going for the next story + if Story["verdict"] == "won": + + Story["passed"].append(Story["currentEvent"]) + Story["currentEvent"] = "02_Bring_Truck_To_House" + Story["timer"] = 100 + + #else: + # try: del Story["Truck"] + # except: pass + + # Ending the event + del Story["dialogue02"] + + pointer.visible = False + dani["race"] = "" + StatusText("") + + +Story["01_Bring_Neonspeedster_To_Racetrack"] = scene + +############################################################################## + +def scene(scene, dani): + + house = scene.objects["TheHouse"] + pointer = scene.objects["PointingArrow"] + + # Moria goes to the racetrack and does some racing by herself. + + if "Moria" in Story: + + # Moria goes to the car + if not dani.get("driving") and not Story["Moria"].get("driving"): + Character_Controll.getIntoCar(Story["Moria"], Story["Neonspeedster"]) + + + if Story["Moria"].get("driving"): + + racepos = [-61.7856, 1028, 0] + + # Moria drive the car to the race + + if Story["Moria"]["driving"].getDistanceTo(racepos) > 2 and "Moria-Practicing" not in Story: + Story["Moria"]["driving"]["npc"] = "story" + Story["Moria"]["driving"]["target"] = racepos + Vehicle.TargetedNPC(Story["Moria"]["driving"]) + + # Moria racing the race + + elif "Moria-Practicing" not in Story: + Story["Moria"]["driving"]["race"] = "RacetrackRace" + Story["Moria"]["driving"]["npc"] = "racer" + Story["Moria"]["driving"]["racing"] = True + Story["Moria-Practicing"] = True + + # Optmization Deleting both Moria and the car she drives + # if Dani is far enough away. + + if dani.getDistanceTo(Story["Moria"]) > 500: + if Story["Moria"].get("driving"): + Story["Moria"]["driving"]["racing"] = False + Reuse.Delete(Story["Moria"]["driving"]) + Character_Controll.getOutCar(Story["Moria"]) + Reuse.Delete(Story["Moria"]) + del Story["Moria"] + + # Paps + + if "Paps" in Story: + + # Optimization + if not "dialogue1" in Story and not "dialogue2" in Story: + if dani.getDistanceTo(Story["Paps"]) > 500: + Reuse.Delete(Story["Paps"]) + del Story["Paps"] + + # Jack + + if "Jack" in Story: + + # Optimization + + if dani.getDistanceTo(Story["Jack"]) > 500 and Story.get("stage", 0) < 5: + Reuse.Delete(Story["Jack"]) + + if Story["Jack"].get("driving"): + Character_Controll.getOutCar(Story["Jack"]) + + del Story["Jack"] + dani["race"] = "" + try: del Story["stage"] + except: pass + pointer.visible = False + + # Truck + + if "Truck" in Story: + + # Optimization + + if dani.getDistanceTo(Story["Truck"]) > 500 and Story.get("stage", 0) < 5: + Reuse.Delete(Story["Truck"]) + del Story["Truck"] + dani["race"] = "" + try: + del Story["stage"] + bge.logic.globalDict["print"] = "Mission Failed! You've abandoned it." + except: pass + pointer.visible = False + + + racetrack = [-62.82, 967.5, 4.738] + + # Showing on the map + if not dani.get("race"): + Map.Show(racetrack, icon="Map_Circle", color=[1,0.8,0]) + + # If dani is close to the racetrack and everything has been despawned + # spawned it back in if dani comes. + + if dani.getDistanceTo(racetrack) < 450 and "stage" not in Story and not dani.get("race"): + + + # Restoring truck, Paps and Jack + + if "Truck" not in Story: + + TruckPosition = [-44.62, 960.8, 4.738] + Story["Truck"] = Vehicle.Spawn("TruckBox", + TruckPosition, + [0,0,0]) + + if not Story["Truck"].get("cargo"): + + # There is a car in the truck actually + DarkShadowLocation = [-44.62, 960.8, 10] + Story["DarkShadow"] = Vehicle.Spawn("DarkShadowBox", + DarkShadowLocation, + [0,0,0], + color="pallete") + Vehicle.PackCargo(Story["Truck"], Story["DarkShadow"]) + + if "Paps" not in Story: + Story["Paps"] = Reuse.Create("PapsBox") + Story["Paps"].position = [-54.875, 972.66, 4.7383] + + try: + Reuse.Delete(Story["Jack"]) + del Story["Jack"] + except:pass + + try: + Reuse.Delete(Story["Truck"]) + del Story["Truck"] + except:pass + + if "Jack" not in Story: + Story["Jack"] = Reuse.Create("JackBox") + Story["Jack"].position = [-52.875, 972.66, 4.7383] + + # Paps coming to Dani + if 100 > Story["Paps"].getDistanceTo(dani) > 4: + Character_Controll.walkToward(Story["Paps"], dani) + + # Not restoring Moria. + + if Story.get("timer", 100): Story["timer"] = Story.get("timer", 100) - 1 + else: + + # Paps is telling Jack to go inside the truck. + + if dani.getDistanceTo(Story["Paps"]) < 50 and "dialogue1" not in Story: + Story["dialogue1"] = PlayDialogue("d1", Story["Paps"]) + + # Paps emoting + if "dialogue1" in Story: + + emotion_data = [ + [[ 0.18 , 0.7 ], "Talk" ], + [[ 1.52 , 2.5 ], "Talk" ] + ] + + emote(Story["Paps"], emotion_data, Story["dialogue1"]) + + # When he finished talking Jack goes to the truck. + + if "dialogue1" in Story and not Story["dialogue1"].status: + + if Story["Jack"].get("driving") != Story["Truck"]: + Character_Controll.getIntoCar(Story["Jack"], Story["Truck"], passanger=True) + else: + Story["stage"] = 1 + del Story["dialogue1"] + + # Paps telling you how he wants you to hummiliate jack + if Story.get("stage") == 1: + + # Paps coming to Dani + if "Paps" in Story: + if 100 > Story["Paps"].getDistanceTo(dani) > 4: + Character_Controll.walkToward(Story["Paps"], dani) + + if "dialogue2" not in Story: + Story["dialogue2"] = PlayDialogue("brief", Story["Paps"]) + + # Paps emoting + if "dialogue2" in Story: + + emotion_data = [ + [[ 0.0 , 0.8 ], "Talk" ], + [[ 2.6 , 3.5 ], "Talk" ], + [[ 4.7 , 12.7 ], "Talk" ], + [[ 14.1 , 15.4 ], "Talk" ], + [[ 17.3 , 20.303 ], "Talk" ], + [[ 21.37 , 27 ], "Talk" ], + [[ 28.5 , 34.6 ], "Talk" ], + [[ 36.6 , 37.6 ], "Talk" ], + [[ 38.3 , 40.0 ], "Talk" ], + ] + + emote(Story["Paps"], emotion_data, Story["dialogue2"]) + + if "dialogue2" in Story and not Story["dialogue2"].status: + Story["stage"] = 2 + del Story["dialogue2"] + bge.logic.globalDict["print"] = "Get the truck home!" + + # Now you are faced with the choice to either get the truck or + # continue doing some stupid stuff. + + if Story.get("stage") == 2: + + # Mapping the truck on the map + Map.Show(Story["Truck"], color=[0,0,1]) + + # If you sit in the truck the mission begins. + if dani.get("driving") == Story["Truck"]: + dani["race"] = Story["currentEvent"] + Story["stage"] = 3 + Story["timer"] = 500 + pointer.visible = True + + if Story.get("stage") == 3: + + # Mapping the house + Map.Show(house, icon="Map_Circle", color=[1,0.8,0]) + + # Point to the house + tocheck = pointer.getVectTo(house) + pointer.alignAxisToVect(tocheck[1], 1, 0.1) + pointer.alignAxisToVect( (0,0,1), 2, 1.0 ) + + # If Truck blows up you loose + if Story["Truck"].get("blown"): + bge.logic.globalDict["print"] = "Mission failed! You broke the truck." + dani["race"] = "" + del Story["stage"] + + try: + Reuse.Delete(Story["Paps"]) + del Story["Paps"] + except:pass + + pointer.visible = False + if "dialogue3" in Story: + Story["dialogue3"].stop() + if "dialogue4" in Story: + Story["dialogue4"].stop() + + # Jack telling his side of the story. + if Story["timer"]: Story["timer"] -= 1 + else: + if "dialogue3" not in Story and ( not Story["Jack"].get("saying") or not Story["Jack"]["saying"].status ): + Story["dialogue3"] = PlayDialogue("d2") + Story["Jack"]["saying"] = Story["dialogue3"] + + + # Jack emoting + if "dialogue3" in Story: + + emotion_data = [ + [[ 4.2 , 4.7 ], "Talk" ], + [[ 10 , 10.5 ], "Laugh" ], + ] + + emote(dani, emotion_data, Story["dialogue3"]) + + emotion_data = [ + [[ 0.1 , 0.8 ], "Talk" ], + [[ 1.2 , 3.7 ], "Talk" ], + [[ 6.5 , 7.1 ], "Talk" ], + [[ 8.3 , 10.1 ], "Talk" ], + [[ 11.3 , 14.8 ], "Talk" ], + [[ 15.4 , 17.3 ], "Talk" ], + [[ 18.6 , 22.1 ], "Talk" ], + [[ 23.5 , 25.1 ], "Talk" ], + [[ 26.3 , 30.2 ], "Talk" ], + [[ 30.9 , 31.8 ], "Talk" ], + [[ 32.5 , 33.7 ], "Talk" ], + [[ 34.6 , 38.5 ], "Talk" ], + [[ 39.6 , 41.6 ], "Talk" ], + ] + + emote(Story["Jack"], emotion_data, Story["dialogue3"]) + + # If Jack told his story and we are close enough to the house + if "dialogue3" in Story and not Story["dialogue3"].status: + + # Jack tells changes the mission + if Story["Truck"].getDistanceTo(house) < 450: + if "dialogue4" not in Story and ( not Story["Jack"].get("saying") or not Story["Jack"]["saying"].status ): + Story["dialogue4"] = PlayDialogue("twist") + Story["Jack"]["saying"] = Story["dialogue4"] + + # Jack emoting + if "dialogue4" in Story: + + emotion_data = [ + [[ 3.4 , 3.7 ], "Talk" ], + [[ 9.8 , 10.2 ], "Talk" ], + ] + + emote(dani, emotion_data, Story["dialogue4"]) + + emotion_data = [ + [[ 0.1 , 0.5 ], "Talk" ], + [[ 1.1 , 1.8 ], "Talk" ], + [[ 2.2 , 3.0 ], "Talk" ], + [[ 4.3 , 5.0 ], "Talk" ], + [[ 5.5 , 7.9 ], "Talk" ], + [[ 8.4 , 9.6 ], "Talk" ], + ] + + emote(Story["Jack"], emotion_data, Story["dialogue4"]) + + + + if "dialogue4" in Story and not Story["dialogue4"].status: + + del Story["dialogue3"] + del Story["dialogue4"] + Story["stage"] = 4 + + # Unlocking the race in the middle of this + Story["start_race"] = True + + if Story.get("stage") == 4: + + racepos = [-254.4, -508.1, 189.2] + + # No need to show the race on the map, since it + # will temporarily unlocked and that will make + # the standard system show it on the map. + + # Point to the race + tocheck = pointer.getVectTo(racepos) + pointer.alignAxisToVect(tocheck[1], 1, 0.1) + pointer.alignAxisToVect( (0,0,1), 2, 1.0 ) + + # If Truck blows up you loose + if Story["Truck"].get("blown"): + bge.logic.globalDict["print"] = "Mission failed! You broke the truck." + dani["race"] = "" + del Story["stage"] + + try: + Reuse.Delete(Story["Paps"]) + del Story["Paps"] + except:pass + + pointer.visible = False + + + if Story["Truck"].getDistanceTo(racepos) < 70: + bge.logic.globalDict["print"] = "Press D to unload the car." + + if not Story["DarkShadow"].get("isCargo"): + Story["stage"] = 5 + Character_Controll.getOutCar(Story["Jack"]) + pointer.visible = False + + # Jack goes into the car that you've brought. + if Story.get("stage") == 5: + + if Story["Jack"].get("driving") != Story["DarkShadow"]: + Character_Controll.getIntoCar(Story["Jack"], Story["DarkShadow"], passanger=True) + # else: + # Story["stage"] = 6 + + + +Story["02_Bring_Truck_To_House"] = scene + +# Mission: Deliver the Truck with Jack to the house. +# Twist : Jack will want you to turn away from the challenge. To end up +# on a race not so far away from the house. + +############################################################################## + +# Mission: Race not so far away from the house. +# Twist : Paps notices you race, since he and moria are back from her training. +# He is mad because you drove like an idiot and broke the car. + +############################################################################## + +# Mission : The Mansion Race. +# Challenge: Not to break the car. + +############################################################################## + +# Mission : One of the drivers from the last race invites you to race +# like an idiot ( Pito's Statue Race ). + diff --git a/Scripts/Settings.py b/Scripts/Settings.py new file mode 100644 index 0000000..e49c992 --- /dev/null +++ b/Scripts/Settings.py @@ -0,0 +1,196 @@ +# Gplv3 or any later version +# (C) J.Y.Amihud 2024 + +import os +import json + +# Those modules don't work everywhere +try: + import bge + import bpy + + from Scripts import Main_update + from Scripts import Script + from Scripts import Money + from Scripts import Garage + from Scripts import Character_Controll + from Scripts import Vehicle + + from Scripts.Common import * +except: + pass + +def get_settings_folder(): + + game = "danisrace" + + try: + data_dir = os.environ["XDG_DATA_HOME"] + "/" + game + except: + data_dir = os.path.expanduser("~/.local/share/"+game) + + try: + os.makedirs(data_dir) + except: + pass + + return data_dir + +def load_settings(): + + folder = get_settings_folder() + f = folder+"/config.json" + + try: + with open(f) as o: + return json.load(o) + except: return {} + +def Execute(): + + + # This function will execute the various settings inside of the game + + data = load_settings() + if not data: return + + scene = bge.logic.getCurrentScene() + + # SHADOWS + + for obj in scene.lights: + + # Use Shadow + try: obj.blenderObject.data.use_shadow = data.get("shadows", True) + except: pass + + # Use Contact Shadow + try: obj.blenderObject.use_contact_shadow = data.get("cntctshadows", True) + except: pass + + # Softshadows + bpy.data.scenes["Scene"].eevee.use_soft_shadows = data.get("softshadows", True) + + # Reflections + bpy.data.scenes["Scene"].eevee.use_ssr = data.get("reflections", True) + + + # Samples TODO figure it out + bpy.data.scenes["Scene"].eevee.taa_render_samples = data.get("samples", 1) + + # Volume samples + bpy.data.scenes["Scene"].eevee.volumetric_samples = data.get("volumesamples", 32) + + # Compositor + + if data.get("compositor"): + bpy.context.space_data.shading.use_compositor = 'ALWAYS' + else: + bpy.context.space_data.shading.use_compositor = 'DISABLED' + + # Skin samples + bpy.data.scenes["Scene"].eevee.sss_samples = data.get("skinsamples", 5) + + # Volume shadows + bpy.data.scenes["Scene"].eevee.use_volumetric_shadows = data.get("volumeshadow", False) + + # Volume lights + bpy.data.scenes["Scene"].eevee.use_volumetric_lights = data.get("volumelight", True) + + # Volume shadows samp + bpy.data.scenes["Scene"].eevee.volumetric_shadow_samples = data.get("volshadsampl", 3) + + # Cube Shadow res + bpy.data.scenes["Scene"].eevee.shadow_cube_size = data.get("shadowslamps", "512") + + # Sun shadow res + bpy.data.scenes["Scene"].eevee.shadow_cascade_size = data.get("shadowssun", "1024") + + # Logic Steps + #bge.logic.setMaxLogicFrame(100) + + + # Controlls + + controlmap = { + "veh_forward" :"forward", + "veh_backward" :"backward", + "veh_left" :"left", + "veh_right" :"right", + "veh_drift" :"drift", + "veh_nitro" :"nitro", + "veh_resque" :"resque", + "veh_gear_up" :"gearup", + "veh_gear_down":"geardown", + "veh_auto_gear":"autogear", + "veh_dynamcam" :"dynamcam", + "veh_upload" :"unload", + "chr_forward" :"forward", + "chr_backward" :"backward", + "chr_left" :"left", + "chr_right" :"right", + "chr_jump" :"jump", + "chr_get_car" :"getcar", + "chr_swimdown" :"swimdown" + + } + + for control in controlmap: + if control.startswith("chr"): folder = bge.logic.globalDict["chr_controls"] + else: folder = bge.logic.globalDict["veh_controls"] + + try: folder[controlmap[control]] = keycodes[data[control]] + except Exception as e: + print("Error assigning controls",e) + +def SaveGame(): + + # Let's generate the data to save. + + data = { + "currentEvent" : Script.Story["currentEvent"], + "passedEvents" : Script.Story["passed"] + } + + data["cars"] = Garage.Encode() + data["money"] = Money.Get() + + data["inventory"] = Garage.inventory + + data["time"] = bge.logic.globalDict["time"] + + folder = get_settings_folder() + f = folder+"/save.json" + + try: + with open(f, "w") as save: + json.dump(data, save, indent=4, sort_keys=True) + print(clr["bold"]+clr["tdgr"]+"Game saved!"+clr["norm"], f) + except Exception as e: + print(clr["bold"]+clr["tdrd"]+"Failed to save game:"+clr["norm"], e) + +def LoadGame(): + + # loading game + + folder = get_settings_folder() + f = folder+"/save.json" + + try: + with open(f) as o: + data = json.load(o) + except Exception as e: + print("Couldn't Load Game:", e) + return + + bge.logic.globalDict["garage-saved"] = data.get("cars", []) + Script.Story["currentEvent"] = data.get("currentEvent", "01_Bring_Neonspeedster_To_Racetrack") + Script.Story["passed"] = data.get("passedEvents", []) + + Money.Set(data.get("money", 0.0)) + + Garage.inventory = data.get("inventory", {}) + Garage.UpdateShelfs() + + bge.logic.globalDict["start-time"] = data.get("time") + diff --git a/Scripts/Tools.py b/Scripts/Tools.py new file mode 100644 index 0000000..4605ebc --- /dev/null +++ b/Scripts/Tools.py @@ -0,0 +1,456 @@ +# GPLv3 or any later version +# (C) J.Y.Amihud ( blenderdumbass ) + +# This file will dealwith tools and stuff. + +import bge +import bpy +import aud +import math +import mathutils + +from Scripts import Reuse + +from Scripts.Common import * + +from Scripts import Vehicle +from Scripts import Script +from Scripts import Garage + +def MainLoop(): + + keys = bge.logic.globalDict["keys"] + mouse = bge.logic.globalDict["mouse"] + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + + if dani.get("driving"): + return + + camera = scene.active_camera + hand = scene.objects["Tool_Hand"] + hc = scene.objects["Health_Circle"] + + box = scene.objects["Boxify"] + if box.get("timer"): box["timer"] -= 1 + else: box.position = [0,0,-1000] + + # If dani is using a fix tool + if str(dani.get("tool")) == "FixTool": + + dis = 10000 + car = None + + for thecar in bge.logic.globalDict["allcars"]: + cd = thecar.getDistanceTo(dani) + if cd < dis: + dis = cd + car = thecar + + ray = DaniPoint(dani, camera) + + found = False + + if car: + + # First we gonna create a list of all objects + # that the car can get fixed. + + attachment = dani.get("tool-attachment") + + objs = [] + + if not Vehicle.OnGround(car): + objs = [car] + + for wheel in car["wheels"]: + if ( wheel.visible and not attachment ) or attachment == "Wheels" : + objs.append(wheel) + + for door in car.get("doors", []): + if ( door.visible and not attachment ) or attachment == "Parts" : + objs.append(door) + + if "NitroCanProxy" in car.childrenRecursive and\ + (( not car.get("NitroCan") and attachment == "Nitros") or ( car.get("NitroCan") and not attachment )): + objs.append(car.childrenRecursive["NitroCanProxy"]) + + if "SpoilerProxy" in car.childrenRecursive and\ + ( ( not car.get("Spoiler") and "Spoiler" in str(attachment) ) or ( car.get("Spoiler") and not attachment )): + objs.append(car.childrenRecursive["SpoilerProxy"]) + + if ray[1]: + + point = ray[1] + d = 10 + c = None + for obj in objs: + od = obj.getDistanceTo(point) + if od < d: + d = od + c = obj + + if c: + found = True + + if c not in car["wheels"]: + center = Boxify(c) + else: + center = Boxify(c, True) + + text = "" + if c in car["wheels"]: + text = "Wheel" + + elif str(c) == "NitroCanProxy": + text = "Nitros" + + elif "Spoiler" in str(c): + text = "Spoiler" + + elif c == car: + text = "Engine and Chassis" + + elif c in car["doors"]: + text = "Bodywork" + + orange = range(len(text)) + text = text + "\n" + str(int(round(c.get("health", 1.0)*100)))+"%" + + Script.StatusText(text, orange) + + # Activate the tool + if mousecodes["LMB"] in mouse: + + FixToolSound() + + if str(c) == "NitroCanProxy": + Vehicle.AddNitroCan(car) + dani["tool-attachment"] = None + car["nitro"] = 10.0 + + if str(c) == "SpoilerProxy" and attachment: + spoilertype = Garage.shop.get(attachment, {}).get("usemodel") + Vehicle.AddSpoiler(car, spoilertype) + dani["tool-attachment"] = None + + c["health"] = c.get("health", 1.0) + 0.01 + if c["health"] >= 1.0: + c["health"] = 1.0 + + # Replacing mesh, to looks nice. + if c.get("Good"): + c.replaceMesh(c.get("Good")) + + # Doors + if c in car["doors"]: + c["locked"] = True + + rot = door.orientation.to_euler() + for i in range(3): + if i == door.get("axis",2): + rot[i] = door.get("lockat", 0) + else: + rot[i] = 0 + c.orientation = rot + c.suspendPhysics() + + door.orientation = rot + + if c == car: + Vehicle.ChangeBody(car, good=True) + + if c == car: + car["blown"] = False + + if c["health"] > 0.1: + + if not c.visible: + dani["tool-attachment"] = None + c.visible = True + + # Removing parts + if mousecodes["RMB"] in mouse: + + FixToolSound() + + # Removing spoilers + if str(c) == "SpoilerProxy" and not attachment: + spoilertype = Vehicle.RemoveSpoiler(car) + for name in Garage.shop: + item = Garage.shop[name] + if item.get("usemodel") == spoilertype: + dani["tool-attachment"] = name + + # Removing nitros + if str(c) == "NitroCanProxy" and not attachment: + Vehicle.RemoveNitroCan(car) + dani["tool-attachment"] = "Nitros" + + # Removing bodywork + if c in car["doors"] and not attachment: + c["health"] = 0.0 + c.visible = False + dani["tool-attachment"] = "Parts" + + # Removing wheels + elif c in car["wheels"] and not attachment: + c["health"] = 0.0 + c.visible = False + dani["tool-attachment"] = "Wheels" + + + # Shelf + + if not found: + + tool = dani["tool"] + + closest = 5 + theitem = "" + themodel = None + + for name in Garage.inventory: + + amount = Garage.inventory[name] + item = Garage.shop.get(name, {}) + model = item.get('model') + if model: model= scene.objects[model] + + if ( amount or name == dani.get("tool-attachment") ) and model: + + d = dani.getDistanceTo(model) + if d < closest: + closest = d + theitem = name + themodel = model + + if themodel: + + name = theitem + amount = Garage.inventory[name] + item = Garage.shop.get(name, {}) + printname = item.get("name", name) + + + Boxify(themodel) + Script.StatusText(printname+"\nIn Stock: "+str(amount), range(len(printname))) + + if mousecodes["LMB"] in mouse and not tool.get("active-timer"): + tool["active-timer"] = 30 + + print("pressed", dani.get("tool-attachment")) + + if not dani.get("tool-attachment"): + + dani["tool-attachment"] = name + Garage.Use(name) + + else: + + name = dani["tool-attachment"] + + if name not in Garage.inventory: Garage.inventory[name] = 1 + else: Garage.inventory[name] += 1 + Garage.UpdateShelfs() + + dani["tool-attachment"] = None + + + + + if tool.get("active-timer"): tool["active-timer"] -= 1 + + # Tool attachment + if dani.get("tool-attachment"): + + name = dani["tool-attachment"] + item = Garage.shop.get(name, {}) + usemodel = item.get("usemodel") + + if not dani.get("tool-attachment-model") and usemodel: + + dani["tool-attachment-model"] = Reuse.Create(usemodel) + dani["tool-attachment-model"].suspendPhysics() + dani["tool-attachment-model"].blenderObject["MainColor"] = [1.000000, 0.008796, 0.000000] + dani["tool-attachment-model"].blenderObject["SecondaryColor"] = [1.000000, 0.500000, 0.000000] + + s = item.get("usescale", 1) + dani["tool-attachment-model"].scaling = [s,s,s] + + + else: + + usemodel = dani["tool-attachment-model"] + usemodel.position = Vehicle.RelativePoint(dani, (-0.5,-0.5,0.5)) + usemodel.applyRotation((0.03,-0.01,0.05),False) + + elif dani.get("tool-attachment-model"): + Reuse.Delete(dani["tool-attachment-model"]) + dani["tool-attachment-model"] = None + + + # If dani is using a paint tool + elif str(dani.get("tool")) == "PaintTool": + + ray = DaniPoint(dani, camera) + tool = dani["tool"] + + if ray[0] in bge.logic.globalDict["allcars"]: + + car = ray[0] + Boxify(car) + if mousecodes["LMB"] in mouse: + + PaintToolSound() + + if not tool.get("active-timer"): + Vehicle.SmartColor(car, "pallete") + tool["active-timer"] = 30 + + elif mousecodes["RMB"] in mouse: + + PaintToolSound() + + if not tool.get("active-timer"): + Vehicle.SmartColor(car) + tool["active-timer"] = 30 + + if tool.get("active-timer"): tool["active-timer"] -= 1 + +def UpdateTool(tool): + + + tool = tool.owner + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + hand = scene.objects["Tool_Hand"] + box = scene.objects["Boxify"] + icon = scene.objects["Dani_Tool_Icon"] + + if "taken" not in tool: + tool["taken"] = None + tool["wasat"] = tool.position.copy() + + if not tool["taken"]: + + if dani.getDistanceTo(tool["wasat"]) < 2 and not tool.get("timer") and not dani.get("tool"): + tool["taken"] = dani + tool["timer"] = 100 + dani["tool"] = tool + + tool.position = hand.position + tool.orientation = hand.orientation + tool.setParent(hand, True) + + icon.blenderObject["raw"] = tool.get("icon_raw", 1) + icon.blenderObject["column"] = tool.get("icon_column", 0) + + tool.applyRotation([0,0,0.1], False) + + elif dani.getDistanceTo(tool["wasat"]) < 2 and not tool.get("timer"): + + tool["taken"] = None + tool["timer"] = 100 + dani["tool"] = None + box.position = [0,0,-1000] + + tool.removeParent() + + tool.position = tool["wasat"] + tool.orientation = [0,0,0] + + Script.StatusText(" ") + + icon.blenderObject["raw"] = 1 + icon.blenderObject["column"] = 0 + + if tool.get("timer"): tool["timer"] -= 1 + +def Boxify(obj, orcenter=False): + + # This function will draw a box around the object + + scene = bge.logic.getCurrentScene() + box = scene.objects["Boxify"] + box["timer"] = 10 + + if not orcenter: + + # Finding bounding box + bbox = obj.blenderObject.bound_box + + # finding the center of the box + center = 0.125 * sum((mathutils.Vector(b) for b in bbox), mathutils.Vector()) + + center.rotate(obj.orientation) + center * obj.scaling + center = obj.position + center + box.position = center + + + else: + box.position = obj.position + center = obj.position + box.orientation = obj.orientation + box.scaling = obj.blenderObject.dimensions + + + return center + +def DaniPoint(dani, camera): + + # Then we want to see if any of them are pointed at + fro = dani.position.copy() + fro.z += 1 + + to = fro.copy() + to.z -= 1 + to -= fro + to.rotate(camera.worldOrientation.to_euler()) + to += fro + + ray = Vehicle.BeautyRayCast(dani, "tool", to, fro, dist=5) + + return ray + + +#### SOUNDS ##### + +def FixToolSound(): + + # Engine Sound + + device = bge.logic.globalDict["SoundDevice"] + + code = "//sfx/fixtool.ogg" + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(code)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + sound["play"].volume = 1 + +def PaintToolSound(): + + # Engine Sound + + device = bge.logic.globalDict["SoundDevice"] + + code = "//sfx/painttool.ogg" + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(code)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + sound["play"].volume = 1 + diff --git a/Scripts/Vehicle.py b/Scripts/Vehicle.py new file mode 100644 index 0000000..d7c25a3 --- /dev/null +++ b/Scripts/Vehicle.py @@ -0,0 +1,4356 @@ +# THIS MODULE IS TO REPLACE THE OLD Car_controll.py MODULE +# WHICH ENDED TOO LARGE AND TOO HARD TO MAINTAIN. + +import os +import bge +import time +import math +import json +import bpy +import random +import numpy +import aud +import mathutils + + +from Scripts import Reuse +from Scripts import Destruction +from Scripts import Opt +from Scripts import Map +from Scripts import Script +from Scripts import Tools +from Scripts import Money +from Scripts import Garage +from Scripts import Mouse + +from Scripts import Multiplayer_Shared +from Scripts import Multiplayer_Client + +from Scripts import Character_Controll + +from Scripts.Common import * + +controls = { + + "forward" :keycodes["UpArrow"], + "backward":keycodes["DownArrow"], + "left" :keycodes["LeftArrow"], + "right" :keycodes["RightArrow"], + "drift" :keycodes["LCtrl"], + "nitro" :keycodes["Z"], + "resque" :keycodes["BackSpace"], + "gearup" :keycodes["W"], + "geardown":keycodes["S"], + "autogear":keycodes["Q"], + "dynamcam":keycodes["C"], + + # Truck + "unload" :keycodes["D"] +} + +if "veh_controls" not in bge.logic.globalDict: + bge.logic.globalDict["veh_controls"] = controls +controls = bge.logic.globalDict["veh_controls"] + +# Data about various cars in the game + +def LoadCarData(): + + # Loading Json animation data files. + + folder = bge.logic.expandPath("//cardata/") + + if "carData" not in bge.logic.globalDict: + + carData = {} + + for f in os.listdir(folder): + if f.endswith(".json"): + print("Reading", f, "...") + with open(folder+f) as fl: + data = json.load(fl) + + name = f.replace(".json", "") + + carData[name] = data + bge.logic.globalDict["carData"] = carData + + return bge.logic.globalDict["carData"] + +carData = LoadCarData() + +def AssingProbablity(carData): + + if "NPC_cars" not in bge.logic.globalDict: + + NPC_cars = list(carData.keys()) + NPC_type_cars = {} + + for carname in carData: + car = carData[carname] + t = car.get("type", "race-car") + if t not in NPC_type_cars: + NPC_type_cars[t] = [] + NPC_type_cars[t].append(carname) + + npccp = [] + for carname in carData: + car = carData[carname] + npccp.append(car.get("probability",1)) + NPC_cars_probability = [] + for i in npccp: + NPC_cars_probability.append( 1 / sum(npccp) * i) + bge.logic.globalDict["NPC_cars"] = NPC_cars + bge.logic.globalDict["NPC_type_cars"] = NPC_type_cars + bge.logic.globalDict["NPC_cars_probability"] = NPC_cars_probability + + return bge.logic.globalDict["NPC_cars"], bge.logic.globalDict["NPC_type_cars"], bge.logic.globalDict["NPC_cars_probability"] + +NPC_cars, NPC_type_cars, NPC_cars_probability = AssingProbablity(carData) + +# Position of the garage fix point. +fixGaragePosition = [-754.61, -1043.9, 409.32] + +def UpdateCar(car): + + # This function runs on every frame for each car, + # making the car interactable in the game. + + + # Clear the car and get the scene + scene, car = GetSceneAndCar(car) + + + + # Mapping all cars on the minimap ( for debuging ) + #Map.Show(car, color=GetColor(car)) + + # Performace optimization method. + if car.get("racing") or car.get("active") or InView(car): + + if not car.get("inview"): + if not car.get("isCargo"): + car.restorePhysics() + car["inview"] = True + + + # Make sure this car is not deleted when we see it + if CloseToCamera(car, 200): + if car in Reuse.reuse.get(car.name): + Reuse.reuse[car.name].remove(car) + print(consoleForm(car),clr["bold"]+clr["tdyl"],"Removed from Reuse.", clr["norm"]) + car["nodespawn"] = 100 + if car not in bge.logic.globalDict["allcars"]: + bge.logic.globalDict["allcars"].append(car) + print(consoleForm(car),clr["bold"]+clr["tdyl"],"Restored.", clr["norm"]) + car["nodespawn"] = 100 + + # Updating the car physics parameters + PhysicsUpdate(car) + + # Truck updates + if "Truck" in str(car): + UpdateCargo(car) + + # Car Control + if car.get("active"): + UserControl(car) + + + elif car.get("npc") == "npc": + NormalNPC(car) + + elif car.get("npc") == "pursuit": + AngryPursuit(car) + + elif car.get("npc") == "story": + pass # If it is controlled from story + + + elif car.get("racing"): + RacingAI(car) + + # If nobody controls the car. + else: + IdleBraking(car) + + + # Apply Turn + ApplyTurn(car) + + # Automatic Transmission + if car.get("autogear", True): + AutomaticTransmission(car) + + # Forces ( Drag Force, Down Force ) + x = 0 + y = DragForce(car) + z = -DownForce(car) + car.applyForce([x, y, 0], True) + car.applyForce([0, 0, z], True) + if not car.get("underwater"): + car.applyTorque([-z/2*(car.localLinearVelocity[1] < 0) + , 0,0], True) + + # Give car the sound + EngineSound(car) + DriftSound(car) + + # Smoke and fire + SmokeAndFire(car) + GroundPuffsOfSmoke(car) + + # Manual fixing. + # Moved to Tools.py + + else: + if car.get("inview", True): + car.suspendPhysics() + car["inview"] = False + + if car.get("nodespawn", 0): car["nodespawn"] -= 1 + +def GetSceneAndCar(car): + + # This module parses the inputs of other modules. + # And gives back bge.scene and the car object. + + # We don't know if what we get here is the controller + # where the script is running, or the object itself. + + if type(car) == bge.types.SCA_PythonController: + car = car.owner + + scene = bge.logic.getCurrentScene() + + return scene, car + +def PhysicsUpdate(car): + + # Updating the car physics to + # work with a more realistic + # effect, while cheating at some + # things. + + scene, car = GetSceneAndCar(car) + toFPS = Opt.ToFPS() + + # If car has no physics yet, give it physics + if not getCarPhysics(car): + InitCarPhysics(car) + + # Doors Swining + GetPhysicsForces(car) + SwingDoors(car) + + for n, wheel in enumerate(car["wheels"]): + + wheelObject = car["wheels"][n] + health = wheelObject["health"] ** 2 + + # Damping + suspension = wheel["suspension"] * health + k = car["specs"]["suspentionDamping"] + suspensionDamping = 2 * k * math.sqrt(suspension) + + + + getCarPhysics(car).setSuspensionStiffness(suspension, n) + getCarPhysics(car).setSuspensionDamping(suspensionDamping, n) + getCarPhysics(car).setTyreFriction(car["specs"]["grip"]*math.sqrt(car.mass), n) + getCarPhysics(car).setRollInfluence(car["specs"]["roll"], n) + + # Applying the engine acceleration + # to the wheels. + ApplyEngine(car) + + # Making sure to clear the acceleration + # for the next frame. + + if not car.get("braking"): + car["driftturn"] = 0.0 + car["accelerating"] = False + car["braking"] = False + +def getCarPhysics(car): + + # Getting car physics. Car physics + # is referring to the Vehicle Constraint + # from the bullet physics. Not the rigid + # body of the main part of the car. + + try: + return bge.constraints.getVehicleConstraint(car["VehicleConstraintId"]) + except: + return None + +def InitCarPhysics(car): + + # This part of code runs once per car. It generates the + # Bullet Physics Vehicle Constraint setup. + + # Clear the car and get the scene + scene, car = GetSceneAndCar(car) + + # Get the data about this car + car["specs"] = GetCarSpecs(car.get("carData", car)).copy() + + # Create a constraint + carPhysics = bge.constraints.createVehicle(car.getPhysicsId()) + car["cid"] = carPhysics.getConstraintId() + car["VehicleConstraintId"] = carPhysics.getConstraintId() + + + # Adding wheels + for n, wheel in enumerate(car["specs"]["wheels"]): + AddWheelToCar(car, wheel) + car["wheels"][n].position = carPhysics.getWheelPosition(n) + + + # Connect doors and other hinged objects + car["doors"] = [] + + + + for obj in car.childrenRecursive: + if obj.get("door"): + car["doors"].append(obj) + obj.suspendPhysics() + + + + # Init Various things we might need + + # Engine things + car["rpm"] = float(0.0) + car["health"] = float(1.0) + car["nitro"] = float(0.0) + + car["shake"] = float(0.0) + + # Set the mass of the car. We divide the mass + # by 10, since there is a limit of how strong + # can be car's suspension. UPBGE does not provide + # a way ( yet ) to override this limit. + car.mass = car["specs"]["mass"] / 10 + + # Create a callback for collisions + def OnCollisionDo(obj, point, normal, points): + + # For readability reason, I put the code + # for the task itself in a separate funtion + # below. + + if car["active"] or (InView(car) and Opt.GoodFPS("Collision"),0.6): + OnCollision(car, obj, point, normal, points) + + # I would love to be able to do a Scheduling instead of simple + # FPS checking for non player cars. But this results in Seg-Faults + # because apperately the script tries to read "points" that are + # freed after the simulation step of the collision is over. + + car.collisionCallbacks.append(OnCollisionDo) + + # For trucks + UnloadCargo(car) + + + print(consoleForm(car),clr["bold"]+clr["tdrd"],"Physics Initilized.", clr["norm"]) + +def GetCarSpecs(car): + + # This function returns the car specs + # for a given car. + + # Clearing the key + try: key = car["carData"] + except: + try: key = car.name + except: key = str(car) + + # Return the data, or some data if there is none. + if key in carData: + return carData[key] + else: + return carData[list(carData.keys())[0]] + +def AddWheelToCar(car, wheel): + + # This function adds a new wheel. + + # Getting the object of the wheel + wheelname = car.get("wheelname", car.name.replace("Box", "Wheel"))+wheel["object"] + wheelObject = car.children[wheelname] + + + + # Add suspension data to the wheel + wheelObject["suspension"] = car["specs"]["suspention"] + + # Add health data to the wheel + wheelObject["health"] = 1.0 + + # Adding this wheel in to the car data for later + # use. + + if "wheels" not in car: + car["wheels"] = [] + + car["wheels"].append(wheelObject) + + # Removing the parent of the wheel ( it can mess things up if we don't ) + wheelObject.removeParent() + wheelObject.suspendPhysics() + + # Adding the wheel to the car physics + getCarPhysics(car).addWheel(wheelObject, + wheel["xyz"], + wheel["down"], + wheel["axel"], + wheel["suspensionRest"], + wheel["radius"], + wheel["front"]) + +def UserControl(car): + + # This function makes user control + scene, car = GetSceneAndCar(car) + + keys = bge.logic.globalDict["keys"] + settings = bge.logic.globalDict["settings"] + + if settings.get("veh_mouse"): + + MouseControl(car) + + else: + # Acceleration + + if controls["forward"] in keys: + Accelerate(car) + + if controls["backward"] in keys: + Accelerate(car, -1) + + if controls["backward"] not in keys and controls["forward"] not in keys: + + # Making sure you can actually stop the car + v = -car.localLinearVelocity[1] + if v < 0: v *= -1 + if v > 5: + ReleaseBreakes(car) + else: + IdleBraking(car) + + # Turning + + if controls["right"] in keys: + TurnRight(car) + + elif controls["left"] in keys: + TurnLeft(car) + + + if controls["nitro"] in keys: + StartNitro(car) + else: + StopNitro(car) + + + + if controls["drift"]in keys: + HandBrake(car) + + # Racing + if controls["resque"]in keys: + car["abort-resque"] = 0 + ResqueRacer(car) + + # Gears + + if controls["gearup"] in keys and not car.get("geartoggletimer"): + GearUp(car) + car["geartoggletimer"] = 10 + + elif controls["geardown"] in keys and not car.get("geartoggletimer"): + GearDown(car) + car["geartoggletimer"] = 10 + + + # Toggling automatic transmission on and off with Q + if controls["autogear"] in keys and not car.get("geartoggletimer"): + car["autogear"] = not car.get("autogear", True) + car["geartoggletimer"] = 10 + + # Counting down the timer + if car.get("geartoggletimer"): + car["geartoggletimer"] -= 1 + + + #Toggling truck stuff + + if controls["unload"] in keys and not car.get("cargocountdown"): + ChangeCargo(car) + car["cargocountdown"] = 100 + + if car.get("cargocountdown"): + car["cargocountdown"] -= 1 + + # Toggling dynamic camera + if controls["dynamcam"] in keys and not car.get("camtoggletimer"): + car["dynamiccam"] = not car.get("dynamiccam", True) + car["shake"] = 0.0 + car["camtoggletimer"] = 10 + + if car.get("camtoggletimer"): + car["camtoggletimer"] -= 1 + + + + # User UI + if car.get("dynamiccam", True): + DynamicCamera(car) + else: + Mouse.MouseLookActual(car.children["CarCamTarget"], True) + + + UpdateTachometer(car) + UpdateSpeedometer(car) + UpdateGearUI(car) + UpdateRedline(car) + UpdateHealthGauge(car) + UpdateNitroMeter(car) + UpdateFixMap(car) + + # Various things that happen only to the user's car + CloseCall(car) + + # Grabbing control when on multiplayer + if settings.get("multiplayer"): + Multiplayer_Client.GrabOwnership(car) + +def MouseControl(car): + + # This fucntion will control the car with a mouse. + + + scene, car = GetSceneAndCar(car) + + # Getting mouse position on the screen. + mouse = bge.logic.mouse + pos = list(mouse.position) + + + # Making sure that the mouse doesn't escape the window + if pos[0] > 0.9: + bge.render.setMousePosition(int(bge.render.getWindowWidth()*0.9), + int(bge.render.getWindowHeight() * pos[1])) + elif pos[0] < 0.1: + bge.render.setMousePosition(int(bge.render.getWindowWidth() * 0.1), + int(bge.render.getWindowHeight() * pos[1])) + + if pos[1] > 0.9: + bge.render.setMousePosition(int(bge.render.getWindowWidth() * pos[0]), + int(bge.render.getWindowHeight()*0.9)) + elif pos[1] < 0.1: + bge.render.setMousePosition(int(bge.render.getWindowWidth() * pos[0]), + int(bge.render.getWindowHeight() * 0.1)) + + pos[0] -= 0.5 + pos[1] -= 0.5 + pos[0] *= 2.2 + pos[1] *= 2.2 + + # Blind spot + bs = 0.1 + + if pos[0] > 0: p0 = 1 + else: p0 = -1 + if pos[1] > 0: p1 = 1 + else: p1 = -1 + + # if pos[0] > bs or pos[0] < -bs: + # p = pos[0] + # pos[0] = (( (pos[0] * p0) * (1+bs) ) - bs) * p0 + + # else: pos[0] = 0.0 + + a = pos[1] + t = pos[0] + + if pos[1] > bs or pos[1] < -bs: + + pos[1] = (( (pos[1] * p1) * (1+bs) ) - bs) * p1 + pos[1] = ( ( ( pos[1] * p1 ) ** 3 ) * p1) + Accelerate(car, -pos[1] , mouse=True) + else: + pos[1] = 0.0 + v = -car.localLinearVelocity[1] + if v < 0: v *= -1 + if v > 5: + ReleaseBreakes(car) + else: + IdleBraking(car) + + + car["turn"] = -( ( ( pos[0] * p0 ) ** 3 ) * p0) + + # UI indication + turnpiece = scene.objects["Mouse_Turn_UI_Piece"] + turnframe = scene.objects["Mouse_Turn_UI"] + m = 0.062012 * turnpiece.worldScale[0] + turnpiece.worldPosition = RelativePoint(turnframe, + (m*t,0,0) + ) + accelpiece = scene.objects["Mouse_Accelation_UI_Piece"] + accelframe = scene.objects["Mouse_Accelation_UI"] + + m = 0.062012 * accelpiece.worldScale[0] + accelpiece.worldPosition = RelativePoint(accelframe, + (m*a,0,0) + ) + +def Accelerate(car, factor=1, mouse=False): + + # This function accelerates the car + + # When changing direction we want it to act like + # brakes. + v = -car.localLinearVelocity[1] + if v * factor < -0.5: + + # Activating breaks + ActivateBreaks(car, factor, True) + + else: + + # Reverting to NOT breaking. + ReleaseBreakes(car) + + # Reving the engine, and telling the code that + # we are accelerating now. + + Rev(car, factor, mouse) + car["accelerating"] = True + +def ActivateBreaks(car, factor=1, stable=False): + + # Activating brakes. + + Abs = ABS(car) + turn = Turning(car) + for n, wheel in enumerate(car["specs"]["wheels"]): + + wheelObject = car["wheels"][n] + health = wheelObject["health"] ** 5 + + # If the car moves forward stopping only with back + # wheels so it will not fly into the air. + if ( factor < 0 and not wheel["front"] ) or ( factor > 0 and wheel["front"] ): + getCarPhysics(car).applyBraking(Abs*health, n) + else: + getCarPhysics(car).applyBraking(0, n) + + # Stablizing the brakes + if stable: + r = car.localAngularVelocity[2] + car.applyTorque([0,0,-r*car.mass*20], True) + + car["braking"] = True + +def ReleaseBreakes(car): + + for n, wheel in enumerate(car["specs"]["wheels"]): + getCarPhysics(car).applyBraking(0, n) + + +def StartNitro(car): + + scene, car = GetSceneAndCar(car) + + # Skipping + if not car.get("active") and not Opt.GoodFPS("Nitro", 0.9) or not car.get("NitroCan") : + return + + # Nitro Ended + if car.get("nitro", 0) < 0.04: + StopNitro(car) + RemoveNitroCan(car) + car["nitro"] = 0.0 + return + + + for nitroObject in car.children: + if "nitro" in nitroObject.name: + if not nitroObject.get("nitroCone"): + nitroCone = Reuse.Create("NitroCone") + nitroObject["nitroCone"] = nitroCone + nitroCone.position = nitroObject.worldPosition + nitroCone.orientation = nitroObject.worldOrientation + nitroCone.setParent(nitroObject) + + if car.get("active"): + cam = scene.active_camera + if cam.lens > 15: + cam.lens -= 0.3 + + NitroSound(car) + ReduceNitro(car, 0.015) + + + # Fake push on the car forward + car.applyForce([0,-30*car.mass, 0], True) + + car["nitroing"] = True + +def StopNitro(car): + + scene, car = GetSceneAndCar(car) + + for nitroObject in car.children: + if "nitro" in nitroObject.name: + if nitroObject.get("nitroCone"): + nitroObject["nitroCone"].removeParent() + Reuse.Delete(nitroObject["nitroCone"]) + nitroObject["nitroCone"] = None + + if car.get("active"): + cam = scene.active_camera + if cam.lens < 20: + cam.lens += 0.3 + + NitroSoundStop(car) + + car["nitroing"] = False + +def AddNitro(car, amount): + + # Deprecated + return + if amount < 0: amount *= -1 + car["nitro"] = max(0, car.get("nitro", 0) + amount) + +def ReduceNitro(car, amount): + + if amount < 0: amount *= -1 + car["nitro"] = min(car.get("nitro", 0) - amount, 10) + +def AddNitroCan(car): + + if "NitroCanProxy" not in car.childrenRecursive: + return + + + nitrocan = Reuse.Create("NitroCan") + nitroproxy = car.childrenRecursive["NitroCanProxy"] + + nitrocan.position = nitroproxy.position + nitrocan.worldOrientation = nitroproxy.worldOrientation + nitrocan.setParent(nitroproxy) + car["NitroCan"] = True + car["NitroCanObject"] = nitrocan + +def RemoveNitroCan(car): + + if not car.get("NitroCan"): + return + + + nitrocan = car["NitroCanObject"] + nitrocan.removeParent() + Reuse.Delete(nitrocan) + car["nitro"] = 0.0 + car["NitroCan"] = False + + +def AddSpoiler(car, spoilertype=None): + + if "SpoilerProxy" not in car.childrenRecursive: + return + + if not spoilertype: + spoilers = [] + for name in Garage.shop: + usemodel = Garage.shop[name].get("usemodel") + if "Spoiler" in name and usemodel: + spoilers.append(usemodel) + spoilertype = random.choice(spoilers) + + + + spoiler = Reuse.Create(spoilertype) + spoilerproxy = car.childrenRecursive["SpoilerProxy"] + + spoiler.position = spoilerproxy.position + spoiler.worldOrientation = spoilerproxy.worldOrientation + spoiler.scaling = spoilerproxy.scaling + spoiler.setParent(spoilerproxy) + ColorPart(car, spoiler) + car["Spoiler"] = spoilertype + car["SpoilerObject"] = spoiler + +def RemoveSpoiler(car): + + if not car.get("Spoiler"): + return + + spoilertype = car["Spoiler"] + spoiler = car["SpoilerObject"] + spoiler.removeParent() + Reuse.Delete(spoiler) + car["Spoiler"] = None + car["SpoilerObject"] = None + return spoilertype + +def GetTunnedEffects(car): + + # This function will return a sum total of all effects added + # to the car. + + effects = {} + + names = [] + if car.get("Spoiler"): + for name in Garage.shop: + item = Garage.shop[name] + if item.get("usemodel") == car["Spoiler"]: + names.append(name) + + for name in names: + item = Garage.shop.get(name, {}) + effe = item.get("effects", {}) + for e in effe: + effect = effe[e] + effects[e] = effects.get(e,1) * effect + + return effects + +def UpdateNitroMeter(car): + + # This function shows nitro amount + # for the user. + + scene, car = GetSceneAndCar(car) + + nitrometer = scene.objects["Gage_Nitro"] + nitrovalue = car.get("nitro", 0) / 10 * 100 + nitrometer["Nitro"] = nitrovalue + + +def ABS(car): + + # This function implements a basic + # Anti-lock Braking System. And + # returns a braking value, to be + # used in the Bullet's Car's Braking + # function. + + # Due to it being a game and not a + # perfect simulation of car dynamics + # it will not be your typical ABS + # but rather something that acts similarly. + + brakes = car["specs"]["brakes"] + cb = car.mass * brakes + + # Here is the magic. The strong the "abs" value + # for a specific car, the more damping will any + # sudden burst of rotation on Z axis recieve. + # Basically, higher "abs" = lower drifting. + + strength = car["specs"]["abs"] + car.localAngularVelocity[2] *= Turning(car) ** strength + + return cb + +def IdleBraking(car): + + # When car is just standing still + # and nobody is driving it, we + # want to car to keep standing where + # it is standing and not roll down hills + # and stuff. For this we activate breaks. + + Abs = ABS(car) + for n, wheel in enumerate(car["specs"]["wheels"]): + getCarPhysics(car).applyBraking(Abs, n) + +def GearUp(car): + + # First we gear up. + oldgear = car.get("gear", 0) + gears = car["specs"]["gears"] + car["gear"] = min( len( gears ) - 1 , car.get("gear", 0) + 1 ) + + # Then we calculate how much to change the RPM + car["rpm"] *= gears[car["gear"]] / gears[oldgear] + GearShiftSound(car) + +def GearDown(car): + + # Same as GearUP() but in reverse. + + oldgear = car.get("gear", 0) + gears = car["specs"]["gears"] + car["gear"] = max( 0 , car.get("gear", 0) - 1 ) + car["rpm"] *= gears[car["gear"]] / gears[oldgear] + GearShiftSound(car) + +def GetGearRatio(car): + + # Gets a ratio between engine + # speed and wheel speed based on + # the current selected gear. + + gear = car.get("gear", 0) + gearRatio = car["specs"]["gears"][gear] + return gearRatio + +def AutomaticTransmission(car): + + # This function automatically shifts + # gears. + + idle = car["specs"]["idle"] + redline = car["specs"]["redline"] + gears = car["specs"]["gears"] + + # Gearing up when reaching the redline + # We add a bit to the rpm to avoid damage + # to the engine. + if car["rpm"]*1.1 >= redline and car.get("gear",0)+1 != len( gears ): + GearUp(car) + + # Gearing down when reaching idle + elif car["rpm"] < idle and car.get("gear"): + GearDown(car) + + # Reverse gear is gear 1 and if we have too much resistance + # also gear 1. + if car["rpm"] <= 0 or EngineResistance(car) < 0.1: + car["gear"] = 0 + +def Rev(car, factor=1, mouse=False): + + # This function simulates the acceleration of + # the internals of the engine recorded in + # car["rpm"]. + + torque = Torque(car) + radius = car["specs"]["wheels"][0]["radius"] + mass = car["specs"]["mass"] + maxrpm = car["specs"]["maxrpm"] + + # Get how many RPM there will be in 1 second + force = torque / radius + + resistance = EngineResistance(car) + + accel = Opt.Force(force) / max( 0.1, ( mass / GetGearRatio(car) ) ) * resistance + addrot = accel / ( math.pi * 2 ) + addRPM = addrot * 60 + + # Get how many RPM there will be in 1 frame + addRPM /= bge.logic.getAverageFrameRate() + + # Add idle RPM. Starting engine from the idle + # instead of from 0. + withIdle = RPMWithIdle(car, factor) + + # Calculating everything. + if withIdle < 0: flip = -1 + else : flip = 1 + + + if not mouse: + car["rpm"] = ( ( math.sqrt( withIdle * flip) * flip ) + (addRPM * factor) ) ** 2 * factor + elif factor < 0 and car["rpm"] > factor * maxrpm: + car["rpm"] = ( ( math.sqrt( withIdle * flip) * flip ) - (addRPM) ) ** 2 *-1 + elif factor > 0 and car["rpm"] < factor * maxrpm: + car["rpm"] = ( ( math.sqrt( withIdle * flip) * flip ) + (addRPM) ) ** 2 + + + # Making sure not to exceed the maximum RPM + car["rpm"] = min( maxrpm, car["rpm"]) + car["rpm"] = max(-maxrpm, car["rpm"]) + + # If revs are above the redline add damage to the engine + redline = car["specs"]["redline"] + factor = ( car["rpm"] - redline ) / ( maxrpm - redline ) * int( car["rpm"] > redline ) + car["health"] = max(0, car.get("health", 1) - ( factor / 1000 )) + if factor: + RedlineSound(car, volume=min(3, factor*10)) + + # Making sure to set 0th gear at negative RPM + if car["rpm"] < 0: car["gear"] = 0 + +def EngineResistance(car): + + # This function simulates the resistance + # ground and various obsticles on the engine + # of the car. If you push against the wall, + # this function will prevent the engine from + # reving hard. Also it is used to make it + # nearly impossible to start the car from a + # high gear. + + gear = GetGearRatio(car) + if not car.get("gear"): + return 1 + + # First we need to calculate expected speed + radius = car["specs"]["wheels"][0]["radius"] + ev = car["rpm"] / gear / 60 * radius + + # Then we get the real value + v = -car.localLinearVelocity[1] + + # Then return the fraction + try : return min(1, v / ev ) + except: return 0 # If the expected velocity is 0, we return 0. + +def RPMWithIdle(car, factor=None, normalize=False): + + # Returns RPM of the car with Idle RPM present. + + # Sometimes the factor should be specified manually. + if factor == None: + if car["rpm"] < 0: factor = -1 + else : factor = 1 + + # Making the idle + idle = car["specs"]["idle"] * factor + if factor < 0: withIdle = min(idle, car["rpm"]) + else : withIdle = max(idle, car["rpm"]) + + # Sometimes we want to normalize it ( like for sound ). + if normalize and withIdle < 0: withIdle *= -1 + + return withIdle + +def HorsePower(car): + + # This function calculates estimated + # horse power given the current RPM + # of the engine. + + # The approximation is done using a Sinewave + # from values of 0 to 3 forths of Pi. + + rpm = car["rpm"] + maxrpm = car["specs"]["maxrpm"] + horses = car["specs"]["hp"] + + # Nitro + if car.get("nitroing"): + return horses * 10 + + return math.sin(rpm/maxrpm*(math.pi/4*3)) * horses + +def Torque(car): + + # This function calculates torque of + # the car. Based on an estimate of + # a torque curve. + + rpm = car["rpm"] + maxrpm = car["specs"]["maxrpm"] + torque = car["specs"]["torque"] + health = car.get("health", 1) + + # Nitro + if car.get("nitroing"): + return torque * 10 + + return math.sin((rpm/maxrpm*(math.pi/4*3))+math.pi/4) * torque * health + +def HorsePowerToWatts(value): + return value * 745.6998715823 + +def AirDencity(car, ignoreWater=False): + + # Air density function will return + # the density of air at various elevations + # including the dencity of water under water. + + # Water is a bit below the center of the game map. + waterlevel = -9.5 + + if car.position[2] > waterlevel or not car.get("underwater") or ignoreWater: + + # We will aproximate the air dencity due to elevation + # with a simple sine wave. Since the graph looks a bit + # like the sine way between the values of pi and pi + half a pi. + # Or in the case of this code, flipped sine from 0 to half a pi. + + maxelevation = 10000 # Roughly where the air is least dense + mindensity = 0.023 # Roughly the density at mount everest + maxdensity = 1.2 # Roughly density of air at sea level + + heightproxy = 1 - ( ( car.position[2] - waterlevel ) / maxelevation ) + density = math.sin( math.pi / 2 * heightproxy ) * maxdensity + density = min(maxdensity, density) + + # Making sure to check that the car is not underwater anymore + car["underwater"] = False + + return density + + else: + + # Completerly faking water density. + + return max(2, min( 20, ( car.position[2] - waterlevel ) * -10 )) + +def WaterBoyancy(car): + + # This function moves car upward, + # if the car is underwater. + # See DownForce() for where it is + # running. + + g = -9.8 + m = car.mass + v = 0.9 # Less then 1 so car could sink, but slowly. + + return g * m * v - DragForce(car) + +def DragForce(car): + + effects = GetTunnedEffects(car) + cd = car["specs"]["drag"] + cd = cd * effects.get("drag", 1) + + p = AirDencity(car) + v = -car.localLinearVelocity[1] + A = 1.5 # Frontal area of the car. I estimate it here. + + if v > 0: flip = 1 + else : flip = -1 + + return (cd / 2 * p * ( v ** 2 ) * A ) * flip + +def DownForce(car): + + if not car.get("underwater"): + + effects = GetTunnedEffects(car) + cl = car["specs"]["downforce"] + cl = cl * effects.get("downforce", 1) + + p = AirDencity(car, ignoreWater=True) + v = -car.localLinearVelocity[1] + A = 0.1 # Based on nothing + + df = cl * p * ( v ** 2 ) * A + + # making sure that the car will not derail + + # Distance to the ground when suspension is holding it in place + wp = car["specs"]["wheels"][0]["xyz"][2] + wr = car["specs"]["wheels"][0]["radius"] + dtg = wp-wr-0.2 + + down = RelativePoint(car, [0,0,dtg]) + hit = car.rayCastTo(down) + + # is the car is on the ground, limit downforce + # to the force of the suspension. + if hit: + df = min(car["specs"]["suspention"]/2, df) + + return df + + else: + return WaterBoyancy(car) + +def ApplyEngine(car): + + toFPS = Opt.ToFPS() + + hp = HorsePower(car) + force = HorsePowerToWatts(hp) / 100 + grip = car["specs"]["grip"] + + # Here is a little cheat to help the car start + resistance = EngineResistance(car) + if car.get("gear",0) == 0: + resistance = 1 + + for n, wheel in enumerate(car["specs"]["wheels"]): + + wheelObject = car["wheels"][n] + health = wheelObject["health"] ** 2 + + rightwheel = ( wheel["front"] and force > 1 ) or ( not wheel["front"] and force < 1 ) + + if rightwheel and not car.get("braking"): + getCarPhysics(car).applyEngineForce(force * health * resistance / GetGearRatio(car) / grip, n) + else: + getCarPhysics(car).applyEngineForce(0, n) + + + # Decrease RPMS + if not car.get("accelerating"): + car["rpm"] *= 0.98 * EngineResistance(car) + +def ApplyTurn(car): + + # This function applies turn + + toFPS = Opt.ToFPS() + + # Making sure the car is not turning more than it is possible. + + maxturn = car["specs"]["maxturn"] + car["turn"] = max(-maxturn, + min(maxturn, + car["turn"] + ) + ) + + for n, wheel in enumerate(car["specs"]["wheels"]): + + wheelObject = car["wheels"][n] + health = wheelObject["health"] ** 2 + + if wheel["front"]: + getCarPhysics(car).setSteeringValue(car["turn"]*health, n) + else: + getCarPhysics(car).setSteeringValue(0, n) + + # Fake turn Aid + vo = car.localLinearVelocity[1] + v = vo + if v < 0: v *= -1 + + if v > 0.01 and vo < 0: + car.applyTorque([0,0,car["turn"] * 20 * car.mass],True) + elif v > 0.01 and vo > 0: + car.applyTorque([0,0,car["turn"] * -20 * car.mass],True) + + + # Auto going straiter + if not car.get("turning"): + if car["turn"] < 0: + car["turn"] += 0.05 * toFPS + if car["turn"] > 0: + car["turn"] = 0 + else: + car["turn"] -= 0.05 * toFPS + if car["turn"] < 0: + car["turn"] = 0 + + car["turning"] = False + +def TurnRight(car): + + toFPS = Opt.ToFPS() + + if car["turn"] > 0: car["turn"] *= -0.5 + car["turn"] -= 0.005 * toFPS + car["turning"]= True + +def TurnLeft(car): + + toFPS = Opt.ToFPS() + + if car["turn"] < 0: car["turn"] *= -0.5 + car["turn"] += 0.005 * toFPS + car["turning"]= True + +def HandBrake(car): + + # This is an implementation of + # a fake handbrake. It is fake + # since normal drifting could + # be technically simulated using + # the physics engine. But it is + # a very delicate thing that results + # in borderline undrivable cars. + # So instead there is this function + # that is used instead to approximate + # the behaviour of the drift. + + factor = car.localLinearVelocity[1] + ActivateBreaks(car, factor) + + + # Recording the turn at which the drift started. + # And using it to add a fake torque to the car. + # which will simulate drifting experience a bit + # more accurately. + + dt = car.get("driftturn",0) + if dt < 0: dt *= -1 + + # If turn increased in the same direction as the drift turn, we increase drift turn + if dt > Turning(car) and ( ( car.get("driftturn",0 ) < 0 ) == ( car["turn"] < 0 ) ): + car["driftturn"] = float(car["turn"]) + + # Adding fake turn to the car + car.applyTorque([-car.mass*5, + 0, + car["driftturn"]*car.mass*10 + ], True) + +def Turning(car): + + # This funtion returns the fraction + # of how far the front wheels have + # been turned for turning the car. + + t = car["turn"] + if t < 0: t *= -1 + maxturn = car["specs"]["maxturn"] + + return t / maxturn + +def UpSideDown(car): + + rot = car.localOrientation.to_euler() + upsideDown = not ( -2.5 < rot[0] < 2.5 and -2.5 < rot[1] < 2.5 ) + return upsideDown + +def DynamicCamera(car): + + # This is a function that moves the camera + # in a dynamic way. + + scene, car = GetSceneAndCar(car) + + + toFPS = Opt.ToFPS() + + cam = scene.active_camera + camtarget = car.children["CarCamTarget"] + + onground = OnGround(car) + someanim = bge.logic.globalDict.get("CameraTargetChange") + + + + + spin = GetSpin(car) + + dontseecar = cam.rayCastTo(car) not in [car]+list(car.childrenRecursive) + + # The effect of showing the car if + # the car went up side down. + + upsidedown = UpSideDown(car) + + # if upsidedown and dontseecar: + # camrot = camtarget.localOrientation.to_euler() + # camrot[0] -= 0.05 + # camtarget.localOrientation= camrot + + #if upsidedown and spin < 1: + # return # To cancel the rest of the effect + + ########### AIR CAMERA DISCONNECT EFFECT ########## + + if not onground and not someanim: + Character_Controll.ChangeCameraTarget(camtarget, + 0, + rotate=False) + car["return-cam"] = True + + + if onground and car.get("return-cam"): + + car["return-cam"] = False + + Character_Controll.ChangeCameraTarget(camtarget, + 30, + rotate=True) + car["zoom"] = 2 + return + + ####################################################### + + + # The effect is zooming in when something + # covers up the car. + + # if dontseecar and camtarget.scaling[0] > 0.1: + # camtarget.scaling *= 0.97 + + # elif camtarget.scaling[0] <= 0.8 and scene.objects["SecondaryCamSensor"].rayCastTo(car) in [car]+list(car.childrenRecursive): + # camtarget.scaling *= 1.1 + + # if camtarget.scaling[0] < 0.1: + # camtarget.scaling = [0.1, 0.1, 0.1] + # if camtarget.scaling[0] > 2: + # camtarget.scaling = [2, 2, 2] + + # Then we ar going to rotate the camera around + # the car ( using a parenting point in the middle + # of the car ) based on various momentums of + # the car. + + camrot = mathutils.Vector(camtarget.localOrientation.to_euler()) + + reference = car.getVectTo(car.position + car.getLinearVelocity())[2] + rotref = car.localAngularVelocity + + backorfoth = ((reference[1]+1)/2) + if backorfoth > 0.6: backorforth = 1 + else: backorforth = 0 + + if camtarget["back"] and backorforth: backorforth = 0 + elif camtarget["back"]: backorforth = 1 + + camnewrot = mathutils.Vector( [ 0.3 - max(-0.1, min(0.2, rotref[0]/2)),# - (car["engine"]*0.005), + 0 - (rotref[2]/10) - (rotref[1]/6), + ( math.pi * backorforth ) - (rotref[2]/4)] ) + + # If the car spins too hard, we want to offset it. + camnewrot -= rotref / 10 * min(1,spin) + + if backorforth and camrot[2] < 0: + camnewrot[2] -= math.pi * 2 + + camnewrot = mathutils.Vector(camnewrot) + camrot = camrot + (( camnewrot - camrot ) / 10 ) + camtarget.localOrientation = camrot + + + # Shake ( shift ) from speed and collisions + v = car.localLinearVelocity[1] + if v < 0: v *= -1 + v = max(0, v - 15) + v = v / 20000 + + if car.get("shake", 0) > 0.01: + v += car["shake"] / 60 + car["shake"] *= 0.92 + + cam.shift_x = random.uniform(0,v) + cam.shift_y = random.uniform(0,v) + +def UpdateTachometer(car): + + # This function shows engine RPMs + # for the user. + + scene, car = GetSceneAndCar(car) + + tachometer = scene.objects["Gage_Tachometer"] + rpmvalue = RPMWithIdle(car, normalize=True) / car["specs"]["maxrpm"] * 100 + tachometer["Health"] = rpmvalue + +def UpdateHealthGauge(car): + + # This function shows engine RPMs + # for the user. + + scene, car = GetSceneAndCar(car) + + tachometer = scene.objects["Gage_Health"] + tachometer["Health"] = car.get("health", 1) * 100 + +def UpdateSpeedometer(car): + + # This function shows car speed + # to the user. + + scene, car = GetSceneAndCar(car) + + kmh = GetSpeed(car) + + speedometer = scene.objects["Gage_Arrow"] + + # Speedometer in game shows between 0 and 400 kmh + # and the whole rotation takes -245 degrees or + # -4.276 radians. + + whole = -4.276 + + # And with that we can apply the rotation of the gauage. + rotation = kmh / 400 * whole + speedometer.orientation = [0,0, rotation] + +def UpdateRedline(car): + + # This function updates redline. + + scene, car = GetSceneAndCar(car) + + + redline = car["specs"]["redline"] + arrow = scene.objects["Gage_Redline"] + maxrpm = car["specs"]["maxrpm"] + whole = -4.276 # See UpdateSpeedometer + + rotation = redline / maxrpm * whole + arrow.orientation = [0,0,rotation] + +def UpdateGearUI(car): + + # This function shows to the user + # in what gear the car is right now. + + scene, car = GetSceneAndCar(car) + + # Getting gear number + gear = car.get("gear",0) + 1 # First gear is 0 in the system + + # Reverse gear + if car["rpm"] < 0: + gear = "R" + + scene.objects["Gage_Gear"]["Text"] = str(gear) + +def UpdateFixMap(car): + + # This shows the fix icon on the map if + # the car is broken + + if car.get("health", 1.0) < 1.0: + + Map.Show(fixGaragePosition, icon="Map_Fix", color=[0.8,0.8,0.8]) + +def GetSpeed(car, real=False): + + # When car moves forward, it is moving + # backwards in the engine + v = -car.localLinearVelocity[1] + + # Now we can find kmh + kmh = v / 3.6 + + # But in the actuall game this real value feels + # too small compared to the mayhem that is happening + # in the game. So we quadrupple the speed, thus + # 25 KM/H will show up as 100 KM/H to the user. + + kmh *= 4 + + # Sometimes we need the real speed ( as in + # when we go backwards, the speed will be < 0 ). + # But sometimes we just want to know the speed + # no matter the direction. + + if not real and kmh < 0: + kmh *= -1 + + return kmh + +def GetSpin(car): + + # This function returns average + # normalized angular velocity. + # Basically, how hard does the car + # spin in any direction. + + # Getting angular velocity + xyz = car.getAngularVelocity() + + # Normalizing it + for n, i in enumerate(xyz): + if i < 0: + xyz[n] *= -1 + + # Averaging it + av = sum(xyz) / 3 + + # Returning it + return av + +def RelativePoint(car, point, rotation=None): + + # This will return any relative point + # in relation to the car in 3D space. + + # First we want to convert the point to + # a vector. + point = mathutils.Vector(point) + + # And get the car's rotation in eulers. + if not rotation: + rotation = car.orientation.to_euler() + + # Then we rotate the point by the car's rotation + # using, the blessing, that is the mathutils + # module. + point.rotate(rotation) + + # And finally we move the point to the car's + # world location. + point = point + car.worldPosition + + # Returning + return point + +def OnGround(car): + + # Returns whether the car + # in on the ground or not. + + # Getting the relative position of the ground + down = RelativePoint(car, [0,0,-1]) + + # See if there is grown under the car + ray = car.rayCast(down, poly=True) + + # If nothing is sensed, we return None + if not ray[1]: + return None + + # Else we return the material of the object underneith + # Useful to see on what kind of ground the car is. + else: + try: return ray[3].material + # If there is no material data it will game the name + # of the object + except: return ray[0].name + +def GroundPuffsOfSmoke(car): + + # This will puff smoke from wheels + # if driving on anything with "ground" in + # it's name. + + if "ground" in str(OnGround(car)).lower(): + speed = min(1, GetSpeed(car) / 30) + for wheel in car["wheels"]: + pos = wheel.position.copy() + Destruction.Dust(pos, 0.05*speed) + +def CloseToCamera(car, d=50): + + cam = bge.logic.getCurrentScene().active_camera + distance = car.getDistanceTo(cam) + + return distance < d + +def OnCollision(car, obj, point, normal, points): + + # This function is responsible for + # breaking the car when it hits things. + + #print("--------", car, obj) + + if not CloseToCamera(car): + return + + # We have a set of objects called separators + # for bots to see the middle of the road. + # we want to ignore those for this calculation. + if "Separation" in str(obj) or "Dani" in str(obj): + return + + if car.get("abort-resque", 0) > 10: + car["abort-resque"] -= 10 + + + # To find whether we want to do sparkles or + # dust, we need to find which material we + # collide with. Unfortunately there is no + # simple way to do it. But rather we will + # need to cast a ray toward the point. + + # First we fire the ray + ray = car.rayCast(point, # Adding a bit to the distance + dist=car.getDistanceTo(point)+0.01, # to insure that + poly=True) # it will actually hit something. + + # And then we extract the material from this operation. + if ray[3]: material = ray[3].material + else : material = "" + + + # For when we collide with water, we want to + # make it so the car will behave underwaterly. + if "water" in str(material).lower(): + + # Setting car as underwater + if not car.get("underwater"): + car["underwater"] = True + if car.get("driver"): + car["driver"]["underwater"] = True + + # Splashing + if not car.get("splashtimer"): + z = car.getLinearVelocity()[2] + y = car.localLinearVelocity[2] + if z < 0: z *= -1 + if y < 0: y *= -1 + force = z + y + Destruction.WaterSplash(car.position.copy(), + 1, + car.orientation.to_euler()[2], + force) + car["splashtimer"] = 10 + else: + car["splashtimer"] -= 1 + return + + + # If the car already blew up we don't want + # to continue breaking it + if car.get("blown"): + return + + v = -car.localLinearVelocity[1] + + # Trying to optimize the colisions a bit + if not car.get("active") and not obj.get("active") and not Opt.GoodFPS("NPC Collision", 0.8): + return + elif not Opt.GoodFPS("Active Collision", 0.5) and v < 20: + return + + + + # Making sure not to count close calls if you + # actually hit something. + + bge.logic.globalDict["closeCallIgnore"] = True + + # Now for every detected collision at the moment. + for point in [points[0]]: + + force = point.appliedImpulse / car.mass + + if UpSideDown(car) and not OnGround(car): + force = 3 + + # Under a low enough value we ignore everything + if force < 0.1: + + force = 0.5 + + # Camera shake + car["shake"] = max(car.get("shake",0), force) + + + # Nitro + #AddNitro(car, force/20) + + return + else: + + # If we colide with ground, do dust + if "ground" in str(material).lower(): + Destruction.Dust(point.worldPoint, + 0.05 * min(1, force/20)) + else: # do particles. + Destruction.particles("Sparkle", point.worldPoint, + despawn = random.randint(30, 100), + amount = random.randint(0, 3), + max = 50, + spread = 0, + normal = 0.04 * min(1, force), + velocity = car.getLinearVelocity() * 0.015) + + car["health"] = max(0, car["health"] - ( force / 100 )) + + + # Scrape sound + if force < 2: + ScrapeSound(car, position=point.worldPoint, volume=max(1,min(3,force))) + else: + HitSound(car, position=point.worldPoint, volume=min(3,force/20)) + + # For each wheel, we are going to specify health as well + # so that the car to loose handling as well as engine power + # due to breaking. + + for n, wheel in enumerate(car["specs"]["wheels"]): + + wheelObject = car["wheels"][n] + if ( wheel["xyz"][0] < 0 ) == ( point.localPointA[0] < 0 ) \ + and ( wheel["xyz"][1] < 0 ) == ( point.localPointA[1] < 0 ) : + wheelObject["health"] = max(0, wheelObject["health"] - ( force / 100 )) + + # Popping the wheel. + + # Disconneting whe wheel from the car is impossible with the + # current implementation of the Vehicle constraint. But we + # can do the next best thing. Hiding it and spawing a new + # wheel in it's stead. + + if wheelObject["health"] < 0.1 and wheelObject.visible: + + wheelObject.visible = False + + newWheel = Reuse.Create(wheelObject.name, 500) + newWheel.position = wheelObject.position + newWheel.orientation = wheelObject.orientation + newWheel.scaling = wheelObject.scaling + ColorPart(car, newWheel) + + # Now a little physics cheat to make this pop + # more exciting! Let the wheel have the same + # initial velocity as the car. + + newWheel.worldLinearVelocity = car.worldLinearVelocity + + # And of course a Poping sound + + WheelPopSound(car, newWheel.position) + + # Opening and destroying doors + if force > 0.5: + closestDistance = 100 + for door in car["doors"]: + distance = door.getDistanceTo(point.worldPoint) + if distance < closestDistance: + closestDistance = distance + thedoor = door + + # Opening the door + try: + thedoor["locked"] = False + thedoor["health"] = max(0, thedoor["health"] - ( force / 20 )) + if "Borked" in thedoor: + thedoor.replaceMesh(thedoor["Borked"]) + GlassSound(car, position=car.position, volume=3) + except: + pass + + + # Consequences to the car + if car["health"] < 0.3: + ChangeBody(car, good=False) + RemoveSpoiler(car) + + # Explosion + if car["health"] < 0.03 and not car.get("blown"): + Destruction.Explosion(point.worldPoint, mass=50, size=30) + car["blown"] = True + + RemoveNitroCan(car) + RemoveFireBall(car) + + # Push everybody out of the car + if car.get("driver"): + character = car["driver"] + Character_Controll.getOutCar(character) + car.worldLinearVelocity = [0,0,10] + car.localAngularVelocity = [10,0,0] + + + for p in car.get("passangers", []): + Character_Controll.getOutCar(p) + + # Detatch all wheels. + for n, wheel in enumerate(car["specs"]["wheels"]): + # Detatching all wheels: + wheelObject = car["wheels"][n] + wheelObject.visible = False + wheelObject["health"] = 0 + newWheel = Reuse.Create(wheelObject.name, 500) + newWheel.position = wheelObject.position + newWheel.orientation = wheelObject.orientation + newWheel.scaling = wheelObject.scaling + ColorPart(car, newWheel) + WheelPopSound(car, newWheel.position) + + # Detach all doors + for door in car.get("doors",[]): + door["health"] = 0 + + # Camera shake + car["shake"] = car.get("shake",0) + force + car["crash"] = force + + # Nitro + #AddNitro(car, force/20) + + # Angry Pursuit + if car.get("npc") == "npc" and obj in bge.logic.globalDict["allcars"]\ + and obj.get("target") != car: + car["target"] = obj + car["npc"] = "pursuit" + + # Voice + StartEnemySounds(car) + + elif car.get("npc") == "pursuit" and obj == car.get("target"): + + HitEnemySound(car) + +def Material(car, value=(0,1,0), attribute="MainColor"): + + # This function edits car's attributes in the blender space + # mainly to edit the parameters of materials. + + if "colors" not in car: + car["colors"] = {} + + car["colors"][attribute] = value + + # Getting objects + for obj in car.childrenRecursive: + ColorPart(car, obj) + + for wheel in car["wheels"]: + ColorPart(car, wheel) + + # Neon + if attribute == "SecondaryColor": + + if "Neon" not in car: + light = Reuse.Create("NeonLamp") + light.blenderObject.data = light.blenderObject.data.copy() + light.position = car.position + light.setParent(car) + car["Neon"] = light + + light = car["Neon"] + light.blenderObject.data.color = value + light.blenderObject.data.use_shadow = False + +def SmartColor(car, color=None): + + # Coloring the car. This is not easy + # since we have multiple possible attributes. + + # If there is nothing passed into the color. + # we choose the default color for the car. + if color == None: + for attribute in car["specs"].get("material", []): + value = car["specs"]["material"][attribute] + Material(car, value, attribute) + + elif color == "random": + for attribute in car["specs"].get("material", []): + value = (random.random(), + random.random(), + random.random()) + Material(car, value, attribute) + + elif color == "pallete": + + # This is a bit more comlex to make the colors + # look good with one another. But this option + # is trying to do just that. + + maincol = mathutils.Color((0.5,0.5,0.5)) + secondcol = mathutils.Color((0.5,0.5,0.5)) + + # We gonna select whether it is low or high + # saturation first. + + maincol.s = numpy.random.choice([0, 1], p=[0.6,0.4]) + + # Let's deal with gray scale first + if not maincol.s: + + # We don't ever want the car to be white + maincol.v = random.choice([0.001,0.65]) + + # If it is closer to black we want to select a + # strong color for the secondary. Otherwise secondary + # is white. + if maincol.v < 0.2: + secondcol.s = 1 + secondcol.v = 1 + secondcol.h = random.random() + else: + secondcol.s = 2 + secondcol.v = 1 + secondcol.h = random.random() + + + else: + + maincol.h = random.random() + maincol.v = random.random() + + + # For this the secondary color is complex. + # Since it is very specific and not everything + # looks good together. + + if maincol.h < 0.16: # Red and orange. + secondcol.s = 1 + secondcol.v = 1 + secondcol.h = random.uniform(maincol.h, 0.16) + + elif maincol.h < 0.333: # Green to yellowish + secondcol.s = 1 + secondcol.v = 1 + secondcol.h = random.uniform(0.16, 0.3) + + elif maincol.h < 0.6: # Green-Blue to desaturated. + secondcol.s = random.uniform(0,0.7) + secondcol.v = 1 + secondcol.h = maincol.h + + elif maincol.h < 0.67: # Blue to Green-Blueish. + secondcol.s = 1 + secondcol.v = 1 + secondcol.h = random.uniform(0.444, 0.67) + + else: # Magenta and pink to desaturated. + secondcol.s = random.uniform(0,0.7) + secondcol.v = 1 + secondcol.h = maincol.h + + color = {"MainColor":maincol, "SecondaryColor":secondcol} + + for attribute in color: + value = color[attribute] + Material(car, value, attribute) + +def ColorPart(car, part): + + # This function colors a specific part + # of the car, into the colors of the car. + + for attribute in car.get("colors", []): + value = car["colors"][attribute] + part.blenderObject[attribute] = value + +def GetColor(car): + + try: return car["colors"]["MainColor"] + except: [1,0,0] + +def ChangeBody(car, good=False): + + # This function changes the car body. + + # Getting objects + #rig = car.children[car.get("rig","RIG")] + body = car.childrenRecursive["Car_body"] + + # If we are breakingt he car, record what + # we had before so we could fix it later. + if not good: + if "good" not in body: + body["good"] = body.meshes[0].name + + # And then execute the change of mesh + body.replaceMesh(car.get("borked", "Borked")) + GlassSound(car, position=car.position, volume=3) + + if "Neon" in car: + car["Neon"].blenderObject.data.energy = 0 + + # Otherwise restore the car + else: + try: + body.replaceMesh(body["good"]) + except: + pass + + RemoveFireBall(car) + + + if "Neon" in car: + car["Neon"].blenderObject.data.energy = 1000 + +def SmokeAndFire(car): + + # This function drawns smoke and fire + if not car.get("underwater"): + health = car.get("health", 1) + smokeCoefitient = max(0, 1-health*2)*0.1 + fireCoefitient = max(0, 1-health*4)*0.1 + smokeemiter = car.children["SmokeEmmiter"] + Destruction.Smoke(smokeemiter.position, smokeCoefitient) + Destruction.Fire(smokeemiter.position, fireCoefitient) + + if "FireBall" not in car and health < 0.3: + car["FireBall"] = Destruction.AttatchFireBall(smokeemiter.position, + smokeemiter, + 1.5) + else: + RemoveFireBall(car) + +def RemoveFireBall(car): + + if "FireBall" in car: + Destruction.DeleteFireBall(car["FireBall"]) + del car["FireBall"] + +def Fix(car): + + # Fixing the car. Restoring + # the car to the unbroken, + # 100% health, state. + + # Restoring health values + car["health"] = 1 + car["blown"] = False + for wheel in car.get("wheels", []): + wheel["health"] = 1 + wheel.visible = True + + # Restoring the doors. + for door in car.get("doors",[]): + + # Restoring rotation of the door + rot = door.orientation.to_euler() + for i in range(3): + if i == door.get("axis",2): + rot[i] = door.get("lockat", 0) + else: + rot[i] = 0 + + door.orientation = rot + door["locked"] = True + + # Restoring the shape + door.visible = True + if "Good" in door: + door.replaceMesh(door["Good"]) + door.suspendPhysics() + + door["health"] = 1.0 + + # Restoring the body work. + ChangeBody(car, good=True) + + # Flipping it over + + rot = car.orientation.to_euler() + rot.y = 0 + rot.x = 0 + car.orientation = rot + +def InView(car): + + # This function will determen if the + # car is visible from the perspective + # of the active camera. + + scene, car = GetSceneAndCar(car) + + camera = scene.active_camera + deleteDistance = 500 + + # To avoid false positives, any car very + # close to the camera will be flagged as + # visible. Even if it is not. + + if car.getDistanceTo(camera) < 100: + return True + + # Then we check whether the car is farther + # then the delete distance ( in which case + # it will soon despawn anyway ). + + if car.getDistanceTo(camera) > deleteDistance: + return False + + # Next we check if the car is toward the direction + # of the camera. + + if not camera.pointInsideFrustum(car.worldPosition) == camera.INSIDE: + return False + + # And finally we check if any visible object + # is obscuring the car. + + try: + obj = camera.rayCastTo(car) + if obj not in [car]+list(car.childrenRecursive) and obj.visible: + return False + except: + pass + + # If all tests are passed, the car is visible. + return True + +def GetPhysicsForces(car): + + # It would be great if UPBGE had a way + # to measure forces on the car. But + # there isn't such a way, so I will make + # my stupid attemt at appriximating them. + + # To do it I will be comparing linear velocity + # of the car, to linear velocity car used + # to have a frame ago. This multiplying by the + # FPS will result in a crude approximation of + # the forces that are acting on the car. + + v = car.localLinearVelocity + pv = car.get("prevLocalLinearVelocity", mathutils.Vector((0,0,0))) + + car["localLinearForces"] = ( v - pv ) * bge.logic.getAverageFrameRate() + + car["prevLocalLinearVelocity"] = v.copy() + +def SwingDoors(car): + + # Doors and other hinged objects might + # be swang around by the momentum of + # the car. + + reference = ( car["localLinearForces"] + car.localLinearVelocity ) / 2 + + + for door in car.get("doors",[]): + + # If locked, skip + if door.get("locked", True) or not door.visible: + continue + + # Getting door data + minrot = door.get("minrot", 0) + maxrot = door.get("maxrot", math.pi/2) + lockat = door.get("lockat", 0) + breakat= door.get("breakat", math.pi/2) + axis = door.get("axis", 2) + factor = door.get("factor", 1.0) + offset = door.get("offset", math.pi) + + # Rotating door + rot = door.localOrientation.to_euler() + rot[axis] = (math.atan2(*(i for n, i in enumerate(reference) if n != axis )) + offset ) * factor + for n in range(3): + if n != axis: + rot[n] = 0 + + # Gravity assisted autolocking. + # For hoods and things like that. + # Where not moving means that gravity pushes + # the door down. + + if door.get("gravity", axis != 2): + + if axis == 1: gravityaxis = 2 + else : gravityaxis = 1 + + + gravityfactor = reference[gravityaxis] + if gravityfactor < 0: gravityfactor *= -1 + gravityfactor = min(30, gravityfactor)/30 + gravityfactor = 1 - gravityfactor + + rot[axis] = rot[axis]+((lockat - rot[axis])*gravityfactor) + + + # Rotation clamping. + maxrot -= minrot + rot[axis] -= minrot + if rot[axis] < 0: rot[axis] += math.pi*2 + + if rot[axis] > (maxrot/2)+math.pi: + rot[axis] = 0 + + + elif rot[axis] > maxrot: + rot[axis] = maxrot + + + rot[axis] += minrot + + # Damping the effect a little. + oldrot = door.localOrientation.to_euler()[axis] + diff = ( rot[axis] - oldrot ) + if diff > math.pi: diff -= math.pi*2 + elif diff < -math.pi: diff += math.pi*2 + rot[axis] = oldrot + ( diff / 5 ) + + # Applying rotation. + door.localOrientation = rot + + # Locking door + if door["health"] > 0.5 and round(rot[axis],2) == round(lockat, 2): + door["locked"] = True + rot[axis] = lockat + + # Detaching the door + if round(rot[axis],3) == round(breakat, 3): + door["health"] -= 0.003 + + if door["health"] < 0.1: + + door.visible = False + + newdoor = Reuse.Create(door.name, 500) + if "Borked" in door: + newdoor.replaceMesh(door["Borked"]) + newdoor.position = door.position + newdoor.orientation = door.orientation + newdoor.worldLinearVelocity = car.worldLinearVelocity / 1.5 + ColorPart(car, newdoor) + +def CloseCall(car): + + # This function will measure close calls + # of almost colisions to other cars. And + # give rewards to the player based on them. + + allcars = bge.logic.globalDict["allcars"] + + + + for othercar in allcars: + if othercar != car: + distance = car.getDistanceTo(othercar) + + if distance < 7: + + # Ignoring if actually hit something, see OnCollision() + if bge.logic.globalDict.get("closeCallIgnore"): + bge.logic.globalDict["closeCallScore"] = 0 + bge.logic.globalDict["closeCallCar"] = None + break + + # Calculating score + v = -car.localLinearVelocity[1] + if v < 0: v *= -1 + score = ( -( distance - 7 ) * v ) / 100 + + if score > bge.logic.globalDict.get("closeCallScore", 0): + bge.logic.globalDict["closeCallScore"] = score + bge.logic.globalDict["closeCallCar"] = othercar + + + elif bge.logic.globalDict.get("closeCallCar") == othercar: + + score = bge.logic.globalDict["closeCallScore"] + bge.logic.globalDict["closeCallScore"] = 0 + bge.logic.globalDict["closeCallCar"] = None + + if score > 1: + bge.logic.globalDict["print"] = "Oh god"+("!"*int(round(score))) + DaniOhGodSound(car) + Money.Recieve(score*25) + + car["shake"] = score + car["near"] = score + + + + else: + bge.logic.globalDict["closeCallIgnore"] = False + + +##### PAPSES TRUCK SPECIFIC FUNCTIONS ### + +def ChangeCargo(car): + + # Making sure it is a truck + if "Truck" not in car.name: + return + + # Telling the Updater function whether the + # truck should be unloaded or not. + car["be_unloaded"] = not car.get("be_unloaded", False) + +def UnloadCargo(car): + + # This function unloads cargo if the car + # is the truck. + + # Making sure it is a truck + if "Truck" not in car.name: + return + + rig = car.children[car.get("rig","RIG")] + anim = "Papses_Truck_RigAction" + + + cargoray = car.childrenRecursive["TruckCargoRay"] + cargomover = car.childrenRecursive["TruckCargoMover"] + # 1.28613 m + + if car.get("unloaded"): + rig.playAction(anim, 0, 50) + + + else: + rig.playAction(anim, 50, 101) + +def UpdateCargo(car): + + # This runs on trucks. + # The function updates the cargo area. + + # Getting objects related to the operation + rig = car.children[car.get("rig","RIG")] + anim = "Papses_Truck_RigAction" + cargoray = car.childrenRecursive["TruckCargoRay"] + cargomover = rig.channels["Down_Part"] + chain = car.childrenRecursive["Chain"] + backdoor = car.childrenRecursive["BackDoor"] + bdt = -(math.pi / 3 * 2) + + # Getting states of the operation + beUnloaded = car.get("be_unloaded", False) + isUnloaded = car.get("is_unloaded", False) + isExtended = rig.getActionFrame(0) == 50 + isShrunk = rig.getActionFrame(0) in [0,100] + + + + # Extending the opertations + if not isExtended and beUnloaded: + rig.playAction(anim, 0, 50) + + if not isUnloaded and not isShrunk and not beUnloaded: + rig.playAction(anim, 50, 100) + + if isExtended and not isUnloaded and beUnloaded: + + # Finding the ground + to = RelativePoint(cargoray, (0,0,-1) ) + fro = cargoray.position + ray = BeautyRayCast(car, "left", to, fro, dist=100) + if ray[1]: d = cargoray.getDistanceTo(ray[1]) + else: d = 100 + + # Moving the bone down + loc = cargomover.location + if d > 0.5 and d != 100: + loc.x += 0.05 + cargomover.location = loc + + # Extending chain + chain.blenderObject.modifiers["Array"].fit_length += 0.05 + + # Unrolling the door + backdoor["locked"] = True + if loc.x > 0.4: + r = backdoor.localOrientation.to_euler() + if r.y > bdt: r.y -= 0.1 + else: r.y = bdt + backdoor.localOrientation = r + + # Telling the system that it's done. + else: + + # Cargo + if car.get("cargo"): + c = car["cargo"] + c.removeParent() + c.restorePhysics() + c["isCargo"] = False + car["cargo"] = False + + car.mass -= c.mass + + c.position = cargoray.position + c.position.z += 1.5 + c.orientation = car.orientation + + for wheel in c["wheels"]: + wheel.visible = True + + car["is_unloaded"] = True + + r = backdoor.localOrientation.to_euler() + r.y = bdt + backdoor.localOrientation = r + + if isExtended and isUnloaded and not beUnloaded: + + # Getting a cargo + if not car.get("cargo"): + + # Looking through all cars. + for c in bge.logic.globalDict["allcars"]: + if c.getDistanceTo(cargoray) < 2: + PackCargo(car, c) + + + # Moving the bone up + loc = cargomover.location + if loc.x > 0: + loc.x -= 0.05 + cargomover.location = loc + + # Retracting chain + chain.blenderObject.modifiers["Array"].fit_length -= 0.05 + + # Closing the door + backdoor["locked"] = True + r = backdoor.localOrientation.to_euler() + if r.y < 0: r.y += 0.1 + else: r.y = 0 + backdoor.localOrientation = r + + # Telling the system that it's done. + else: + + + + loc.x = 0 + cargomover.location = loc + chain.blenderObject.modifiers["Array"].fit_length = 0 + car["is_unloaded"] = False + + r = backdoor.localOrientation.to_euler() + r.y = 0 + backdoor.localOrientation = r + +def PackCargo(car, c): + + cargoray = car.childrenRecursive["TruckCargoRay"] + + c["isCargo"] = True + car["cargo"] = c + car.mass += c.mass + + c.position = cargoray.position + c.orientation = car.orientation + + c.suspendPhysics() + c.setParent(cargoray, False, False) + + for wheel in c["wheels"]: + wheel.visible = False + + print("Packed", c, "into", car) + + +##### SPAWNING AND UNSPAWNING OF CARS ### + +def SpawnLogic(camSurroundCars): + + # Logic of autospanning cars. + # Challenges: + # Have enough cars. + # Have enough performace. + # Have enough variety. + + settings = bge.logic.globalDict["settings"] + + + spawnedCarModels = bge.logic.globalDict["spawnedCarModels"] + spawnedCars = len( bge.logic.globalDict["allcars"] ) + + maxCars = settings.get("maxcars", 4) + cam = bge.logic.getCurrentScene().active_camera + dani = bge.logic.getCurrentScene().objects["Dani_Box"] + + # First lets see how many cars are on screen + # right now. + # notinframe = [] + # inframe = [] + # for car in bge.logic.globalDict["allcars"]: + # if cam.pointInsideFrustum(car.position) == cam.INSIDE: + # inframe.append(car) + # else: + # notinframe.append(car) + + # Getting all of the points that are close enough. + spawns = [] + for i in camSurroundCars: + spawns += bge.logic.globalDict["spawns"].get(i, []) + + # For testing + if "selected" in bge.logic.globalDict["spawns"]: + spawns = bge.logic.globalDict["spawns"]["selected"] + break + + + # Itterating the spawn points + spawnSorted = [] + + for spawn in spawns: + + # If the camera is pointed ( roughly ) at it + vect = cam.getVectTo(spawn["position"]) + vectfactor = vect[0] / 300 + score = vect[0] + spawnSorted.append((score, spawn)) + + spawnSorted = sorted(spawnSorted) + + # Model of the car to spawn. + carModel = str(numpy.random.choice(NPC_cars, p=NPC_cars_probability)) + + + for n, spawn in enumerate(spawnSorted): + + distance, spawn = spawn + + # If it's way too close, abort + if len(spawnSorted) > n+1 and distance < 100 \ + and spawn.get("npc") != "racer": + continue + + # Spawn timer ( to avoid, over spawning ) + if spawn.get("to_spawn_timer", 0) > 0: + spawn["to_spawn_timer"] -= 1 + continue + spawn["to_spawn_timer"] = 100 + + + # Checking race + if spawn.get("race"): + + # Getting the race data + + racename = spawn.get("race") + race = bge.logic.globalDict["races"][racename] + after = race.get("after") + during = race.get("during") + duringcheck = dani.get("race") == after and Script.Story.get(during) + aftercheck = after in Script.Story["passed"] or not after or duringcheck + + racetype = race.get("type", "race-car") + try: + carModel = random.choice(NPC_type_cars[racetype]) + except Exception as e: + print(e) + pass + + else: + aftercheck = False + + # For NPC cars + if ( spawn.get("npc") == "npc" )\ + or ( spawn.get("npc") == "racer" and aftercheck + and spawn.get("to_spawn") and ( not dani.get("race") or duringcheck )): + + # Spawning the car. + + car = Spawn( + carModel, + spawn["position"], + spawn["orientation"], + True, + "pallete", + spawnanyway=False, + spawn=spawn) + + if not car and (spawnedCars < maxCars or spawn.get("npc") == "racer"): + + car = Spawn( + carModel, + spawn["position"], + spawn["orientation"], + True, + "pallete", + spawnanyway=True, + spawn=spawn) + + + if not car: return + + try: + if spawn.get("race") and dani.get("driving", {}).get("NitroCan"): + AddNitroCan(car) + car["nitro"] = 10 + except:pass + + try: + if spawn.get("race") and dani.get("driving", {}).get("Spoiler"): + AddSpoiler(car) + except:pass + + + car.setLinearVelocity([0,-15,0], True) + spawn["to_spawn"] = False + car["spawn"] = spawn + + return + +def Spawn(carname, position, orientation, + fix=True, # Whether to fix the car + color=None, # What color to assign to the car + spawnanyway=True, # Spawn it no matter what. + anything=False, # Spawn anything at all. + spawn=None): # Spawn data from SpawnLogic() + + # This function is simplify spawning + # of the car. + + # If we recieved a name of a car and not an object. + if type(carname) == str: + + # Trying to get the car from already spawned + car = SafeDespawn(bge.logic.globalDict["allcars"], + carname, + anything=anything) + + spawnedmessage = clr["tdgr"]+" Spawned Reused!" + + # Making the car anyway. + if spawnanyway and not car: + car = Reuse.Create(carname) + + spawnedmessage = clr["tdyl"]+" Spawned New!"+clr["norm"]+" ( "+str(len(bge.logic.globalDict["allcars"])+1)+" )" + + # Otherwice return nothing. + elif not car: + return False + + else: + car = carname + + spawnedmessage = clr["tdbu"]+" Spawned by object input!" + + if car.get("active"): + return car + + if "spawn" in car: + car["spawn"]["to_spawn"] = True + + car.position = position + car.orientation = orientation + car.worldLinearVelocity = [0,0,0] + car.worldAngularVelocity = [0,0,0] + + # Putting the car in the list of all cars + if car not in bge.logic.globalDict["allcars"]: + bge.logic.globalDict["allcars"].append(car) + if car not in bge.logic.globalDict["cars"]: + bge.logic.globalDict["cars"].append(car) + + car["nodespawn"] = 100 + car["stuck"] = 300 + + # Removing the car from races if it used to be in one + for racename in bge.logic.globalDict["races"]: + race = bge.logic.globalDict["races"][racename] + try: + race["racers"].remove(car) + except: + pass + + + # If a spawn point is given + if spawn: + car["npc"] = spawn.get("npc") + car["anger"] = spawn.get("anger", random.random()) + if spawn.get("race"): + car["race"] = spawn.get("race") + car["racing"]= False + race = bge.logic.globalDict["races"][spawn.get("race")] + if car not in race["racers"]: + race["racers"].append(car) + else: + car["npc"] = "" + car["race"] = "" + car["racing"] = False + + # Removing stuff on new cars. + + RemoveNetId(car) + + try:RemoveNitroCan(car) + except: pass + car["nitro"] = 0.0 + + try: RemoveSpoiler(car) + except: pass + + print(consoleForm(car),clr["bold"], spawnedmessage, clr["norm"]) + + + # Updating the car, to make sure everything + # is configured. + + + try: + PhysicsUpdate(car) + SmartColor(car, color) + if fix: Fix(car) + except: + pass + + + + return car + +def SafeDespawn(cars, model=None, anything=False): + + # This function returns a despawned car from a list + # of input cars. + + for car in cars: + + # If we need a specific model skip anything else + if model and car.name != model: + continue + + # Logic to determen whether its safe to despawn. + + if ( not car.get("racing") # Not a racing car + and not car.get("active") # Not one Dani's drivin + and not InView(car) # Not on screen + and car.get("nodespawn", 0) < 1 # Not just spawned + and car not in Script.Story.values() # Not a story related car + ): + + + # Deleting the car in an inactive way. + Reuse.Delete(car, inactive=True) + + # Removing it from the list of all cars. + if car in bge.logic.globalDict["allcars"]: + bge.logic.globalDict["allcars"].remove(car) + + # Declaring that the car is despawned. + print(consoleForm(car),clr["bold"]+clr["tdgr"],"Safely Despawned!", clr["norm"]) + + # Returning the result + return car + + # If we need anything at all, but the model car + # was not found. We try again, without the model + # in the arguments. + + if anything: + return SafeDespawn(cars) + + # If nothing found, returning False. + return False + +def DespawnLogic(car): + + # This function will despans the car when the car + # is outside of a visible range, for respawning it + # in a different place. + + scene, car = GetSceneAndCar(car) + + # No despawn timer + if car.get("nodespawn", 0) > 0: + car["nodespawn"] -= 1 + return + + dani = scene.objects["Dani_Box"] + camera = scene.active_camera + deleteDistance = 400 + + if car.getDistanceTo(camera) > deleteDistance \ + and not car.get("racing") \ + and car in bge.logic.globalDict["allcars"] : + + + # This function will be scheduled in Opt + def RemovingTheCar(car, dani): + + + + # If car is waiting for a race to start, we don't delete it + if car.get("npc") == "racer": + racename = car["race"] + race = bge.logic.globalDict["races"][racename] + starter = race["starters"][0] + if dani.getDistanceTo(starter["location"]) < deleteDistance or car["racing"]: + return + + # if "spawn" in car: + # car["spawn"]["to_spawn"] = True + + print("Removing Car:", car) + + # Making sure it can be respawned + if car.get("spawnPoint"): + car["spawnPoint"]["to_spawn"] = True + + # Removing the car from races + for racename in bge.logic.globalDict["races"]: + race = bge.logic.globalDict["races"][racename] + try: + race["racers"].remove(car) + except: + pass + + if car in bge.logic.globalDict["allcars"]: + bge.logic.globalDict["allcars"].remove(car) + + # Removing from cache if too many cars in cache + + # TODO make this removing of cars actually work and + # not sig-fault + + #if Reuse.reuse.get(car.name) or len(bge.logic.globalDict["allcars"]) > bge.logic.globalDict["maxCars"]: + # for i in car["wheels"]: + # i.endObject() + # car.suspendPhysics(True) + # Reuse.EndObject(car) + # Otherwise storing it for later. + #else: + + Reuse.Delete(car) + + # Scheduling + RemovingTheCar(car, dani) + +def UnspawnLogic(): + + for car in bge.logic.globalDict["allcars"]: + + if car in Reuse.reuse: + bge.logic.globalDict["allcars"].remove(car) + continue + + try: + car.position + except: + bge.logic.globalDict["allcars"].remove(car) + + +##### AI NPCS AND RACERS FUNCTIONS #### + +def NormalNPC(car): + + # Redouing the NPC shannanigans + vturn, mindist = VisionTurn(car, True) + vturn *= 2#max(2,v) + + + t = vturn + if t < 0: t *= -1 + anger = car.get("anger", 0.2) + v = -car.localLinearVelocity[1] + + + maxv = mindist / (t+1) * 1.5 + + # Stopping in front of Dani, making the cars more stealable. + dani = bge.logic.getCurrentScene().objects["Dani_Box"] + if not dani.get("driving"): + danivect = car.getVectTo(dani) + if danivect[0] < 10 and danivect[2][1] < -0.3: + maxv = 0 + + #The unstuck protocol + if v < 1 and car.get("stuck",0) < 1: + car["unstuck"] = 100 + car["stuck"] = 300 + car["stuckturn"] = -vturn * 100 + + elif car.get("stuck",0) > 0: + car["stuck"] -= 1 + + # If everything is okay moving forward + nitro = False + if maxv > v / 3 and car.get("unstuck", 0) < 1: + Accelerate(car) + nitro = True + + # If turing or needing to go back + else: + Accelerate(car, -1) + if car.get("unstuck", 0) > 0: + car["unstuck"] -= 1 + + if car.get("unstuck", 0) < 1: + car["turn"] = vturn + #SoftTurn(car, vturn) + else: + car["turn"] = -vturn * 100 + #SoftTurn(car, -vturn * 100) + + + # if nitro: + # StartNitro(car) + # else: + # StopNitro(car) + + # PURSUIT CHEAT + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + + if bge.logic.globalDict["pursuit-cheat"] and dani.get("driving"): + + car["target"] = dani["driving"] + car["npc"] = "pursuit" + +def AngryPursuit(car): + + # This is the code for NPC that are pursuing + # you. + + # Getting the pursuit target + try: + target = car["target"] + + # Deactivate Argy Pursuit if the target car is blown + if target.get("blown"): + car["npc"] = "npc" + + if car.get("blown"): + car["npc"] = "" + + except: + car["npc"] = "npc" + return + + # Showing on the map + scene, car = GetSceneAndCar(car) + dani = scene.objects["Dani_Box"] + if target == dani.get("driving"): + Map.Show(car) + + # Vector to the target + vect = car.getVectTo(target) + distance = vect[0] + ontarget = -vect[2][1] + + # If following Dani, don't despwant as much + if vect[0] < 300 and target == dani.get("driving"): + car["nodespawn"] = 300 + + # Turn + pturn = vect[2][0] * 4 + vturn, mindist = VisionTurn(car, True) + if vect[0] > 50: + turn = (pturn*(1-ontarget)) + (vturn*ontarget) + else: + turn = pturn + + nitro = False + if ontarget > -0.3: + Accelerate(car, 1) + #car["turn"] = turn + SoftTurn(car, turn) + if OnGround(car): + nitro = True + + else: + Accelerate(car, -1) + SoftTurn(car, -turn) + #car["turn"] = -turn + + if nitro: + v = -car.localLinearVelocity[1] + tv = -car["target"].localLinearVelocity[1] + if v < tv or vect[0] < 50: + car["nitro"] = 10.0 + StartNitro(car) + else: + StopNitro(car) + +def RacingAI(car): + + # This is the code for NPC that are pursuing + # you. + + scene, car = GetSceneAndCar(car) + dani = scene.objects["Dani_Box"] + cam = scene.active_camera + + + + onground = OnGround(car) + + + # Getting race info + try: + racename = car["race"] + except: + car["npc"] = "npc" + return + + race = bge.logic.globalDict["races"][racename] + + # Race position + try: pos = race["positions"].index(car) + except: pos = 0 + + # Making sure car has lap andcheckpoint info + if "lap" not in car: + car["lap"] = 0 + if "checkpoint" not in car: + car["checkpoint"] = 0 + + # Getting next checkpoint and the one after that + checknumber = car["checkpoint"] + + checkpoint1 = race["checkpoints"][checknumber] + try: checkpoint2 = race["checkpoints"][ checknumber + 1 ] + except: checkpoint2 = race["checkpoints"][ 0 ] + + target = checkpoint1["location"] + target2 = checkpoint2["location"] + + # Vector to the first checkpoint + vect = car.getVectTo(target) + distance = vect[0] + ontarget = -vect[2][1] + + # Vector to the second checkpoint + vect2 = car.getVectTo(target2) + distance2 = vect2[0] + ontarget2 = -vect2[2][1] + + # Getting recording data + targets = GetRacingTargets(car, racename, checknumber) + pretargets = GetRacingTargets(car, racename, checknumber - 1) + + # Going to the next checkpoint ( when car is close enough to + # the current one ). + if vect[0] < checkpoint1["radius"]: + car["checkpoint"] += 1 + + if checkpoint1.get("OnLoop"): + car["abort-resque"] = 200 + + # Testing how well the car is doing + if targets.get("time"): + currentRaceTime = bge.logic.getRealTime() - race.get("start-time", 0) + timediff = targets["time"] - currentRaceTime + car["racetd"] = timediff - car.get("timediff",0) + car["timediff"] = timediff + + ptdiff = str(round(car["racetd"], 1)) + if car["racetd"] > 0: + ptdiff = "+"+ptdiff + + ActivePrint(car, + round(currentRaceTime, 1), + round(targets["time"], 1), + ptdiff) + + + # Same thing by for laps too. + if car["checkpoint"] == len(race["checkpoints"]): + car["checkpoint"] = 0 + car["lap"] += 1 + + # Velocity of the car + v = -car.localLinearVelocity[1] + + # Turn target based on checkpoint vectors + pturn = vect[2][0] * 5 + pturn2 = vect2[2][0] * 5 + + # Distance to the first checkpoint ( 0 to 1 ) + # This one is used for turning calclations + closeFrac = -(min(1, distance/max(0.1,v))-1) + + # Distance to the first checkpoint from the last one. + # This one to calculate target speed. + try: + prevcheck = race["checkpoints"][ checknumber - 1 ] + distbetween = math.dist( target, prevcheck["location"] ) - (checkpoint1["radius"] + prevcheck["radius"]) + speedFrac = min(1, max(0, 1 - ( (vect[0]-checkpoint1["radius"]) / distbetween ))) + except: + speedFrac = closeFrac + + + + + + # In certain cases we want to update the turning fraction + # closeFrac to act in strange ways for a better racing line. + orcloseFrac = closeFrac + if ( (pturn > 0) != (pturn2 > 0) ): + closeFrac = TargetingCurve(speedFrac) + + # Rotation targeting ( from the recording data ) + # if targets.get("rot"): + # trz = targets["rot"][2] + # rz = car.orientation.to_euler()[2] + # frac, diff = RotDiff(trz, rz) + # pturn = (pturn*(1-orcloseFrac)) + (diff*orcloseFrac) + + # Vision based rotation data + vturn, mindist = VisionTurn(car, True) + vclose = -(min(1, mindist/10)-1) + + # Calulating rotation between checkpoints + tturn = (pturn*(1-closeFrac)) + (pturn2*closeFrac) + + # When there are elevations ( such on the loops in the + # Racetrack ) it messes up the vision a bit, and car + # go crazy. It's better for those to just use the checkpoints + # for navigation. + if not checkpoint1.get("OnLoop") and not checkpoint1.get("IgnoreVision"): + turn = (tturn*(1-vclose)) + (vturn*vclose) + ontarget = ontarget ** 2 + else: + turn = pturn + + # Anti-tumble turning ( if the car rotates sideways, it + # counteracts this by turing the other way to return the + # car back to the wheels ). + tumble = car.localOrientation.to_euler()[1] + turn += tumble / 2 + + # Getting how much the car is turning + t = turn + if t < 0: t *= -1 + + # Calculating target acceleration + if not pretargets.get("speed"): + accel = 20 + else: + accel = pretargets["speed"] + + + # Calculating target speed by interpelating target speed + # between two sets of data. Of previous and next checkpoints. + + if targets.get("time"): + + # but first we need to find the original acceleration between + # this to next checkpoint so the time matches. + + try: + pt = pretargets.get("time", 0) + nt = targets.get("time", 0) + timeIs = nt - pt + ( car.get("racetd", 0) * 2 ) + + + accel = max( distbetween / timeIs, accel ) + except: + pass + + + if targets.get("speed"): + + accel = (accel*(1-(speedFrac))) + ( targets["speed"] * (speedFrac) ) + if ontarget > 0.9: + accel = max( accel , targets["speed"] ) + + + + + # Targeting the timing of the race. + # Accelerating faster if not on time with the + # recording. + #if car.get("racetd",0) < 0 and ontarget > 0.9: + # accel *= min(1-(max(0,car.get("racetd",0))/5), 3) + + # If the car is not on target, slow it down accordingly. + accel *= ontarget + + # Draw beam + RacerBeam(car) + + # If the car is stuck, resque it. + if 1 > v or ( UpSideDown(car) and not onground and + not checkpoint1.get("OnLoop") ) or (ontarget < 0.3 + and not checkpoint1.get("OnLoop")): + if ResqueRacer(car): + #car.localLinearVelocity[1] = -accel + car.setLinearVelocity([0,-pretargets.get("speed", accel), 0], True) + car["nitro"] = 10.0 + + # Cheating resque, so that to give more challenge to + # the player. If the player is too far ahead, the cars + # will resque not too far away from him. + # if pos != 0 and race["positions"].index(dani) == pos - 1\ + # and dani.get("checkpoint") not in [checknumber, checknumber + 1]: + + # # But we don't want it to be noticeable, so more checks. + # tc = race["checkpoints"][dani.get("checkpoint", 0) - 1] + # if cam.pointInsideFrustum(tc["location"]) != cam.INSIDE \ + # and cam.pointInsideFrustum(car.position) != cam.INSIDE \ + # and not tc.get("OnLoop")\ + # and dani.getDistanceTo(tc["location"]) > tc["radius"]*2: + + # # The resquing itself. + # car["checkpoint"] = dani.get("checkpoint", 0) + # car["lap"] = dani.get("lap",0) + # car["abort-resque"] = 0 + # ResqueRacer(car, visible=False) + # daniv = dani["driving"].localLinearVelocity[1] + # car.localLinearVelocity[1] = daniv + + # Showing on the map + #if pos < race["positions"].index(dani): + Map.Show(car, color=GetColor(car)) + + + # If there is a nessesity, use handbrake. + if t > 0.3 and v > 7 and onground: + HandBrake(car) + + # Applying acceleration. ( also ignore the accel if you see that the road is straight ) + if v < accel or (ontarget > 0.8 + and ( ontarget2 > 0.9 or vect[0] < 100) + and not checkpoint1.get("OnLoop")): + Accelerate(car, 1) + + else: + Accelerate(car, -1) + + + SoftTurn(car, turn) + + + # Nitro + + # A little bit of a cheat. If the AI racer is not first + # it gets free nitro. + if pos != 0: + car["nitro"] = 10.0 + + # Nitro logic. + if (v < max(accel, targets.get("speed", 0)) and ontarget > 0.5 )\ + and onground: + StartNitro(car) + else: + StopNitro(car) + +def RotDiff(rot1, rot2): + + # This function compares 2 rotations values + + # 360 degress in radiance is 2 pi + turn = 2 * math.pi + diff = rot1 - rot2 + if diff > math.pi: + diff -= math.pi * 2 + elif diff < -math.pi: + diff += math.pi * 2 + + frac = diff + if frac < 0: frac *= -1 + frac = 1 - ( frac / ( math.pi * 2 ) ) + + return frac, diff + +def TargetingCurve(value): + + # To make the car direct it's momentum + # properly toward the checkpoint, it needs + # to first turn away from the next checkpoint + # and the at it. To alight the checkpoint with + # the next checkpoint. + + return -math.sin(1.2*((value)*math.pi)) * ( value**5 ) * 1 + +def RacerBeam(car): + + scene = bge.logic.getCurrentScene() + dani = scene.objects["Dani_Box"] + cam = scene.active_camera + + if not car.get("beam"): + return + + # Moving the indicator + car["beam"].position = car.position + car["beam"].position[2] += 1 + tocam = car["beam"].getVectTo(cam) + car["beam"].alignAxisToVect(tocam[1], 1, 1.0) + car["beam"].alignAxisToVect( (0,0,1), 2, 1.0 ) + beamscale = max( 0.2, car.getDistanceTo(dani) / 100 ) + car["beam"].scaling = [beamscale, beamscale, beamscale] + +def ResqueRacer(car, visible=True): + + # Resque system for racing cars. + + # Getting race data + + try: + racename = car["driver"]["race"] + + except: + try: racename = car["race"] + except: return False + + + # Abort system for not overresqueing + if "abort-resque" not in car: + car["abort-resque"] = 50 + if car["abort-resque"] > 0: + car["abort-resque"] -= 1 + return False + + car["abort-resque"] = 50 + + + race = bge.logic.globalDict["races"][racename] + + # Getting checkpoints + try: checknumber = car["checkpoint"] + except: checknumber = car["driver"]["checkpoint"] + + try: checkpoint1 = race["checkpoints"][ checknumber - 1 ] + except: checkpoint1 = race["checkpoints"][ 0 ] + + while checkpoint1.get("OnLoop"): + print(car, checkpoint1.get("OnLoop"), checknumber) + checknumber -= 1 + try: checkpoint1 = race["checkpoints"][ checknumber - 1 ] + except: break + + if not car.get("active"): + #car["driver"]["checkpoint"] = checknumber + car["checkpoint"] = checknumber + + checkpoint2 = race["checkpoints"][ checknumber ] + + # Fixing the car ( but not fully ) + car["health"] = max( car["health"], 0.5 ) # Only upto 50% health restored + car["blown"] = False # Making car blowup-able + car["underwater"] = False + + # Fixing wheels + for wheel in car.get("wheels", []): + wheel["health"] = max(wheel["health"], 0.7) + wheel.visible = True + + # Nothing is done to restore the look of the car, so the user will be + # reminded that the car is broken. + + if visible: + # Sparkles on disappearing position + Destruction.particles("Sparkle", car.position.copy(), + despawn = random.randint(30, 100), + amount = 30, + max = 30, + spread = 2, + normal = 0.05, + scale = 7) + + car.position = checkpoint1["location"] + car.position.z += 1 + + car.setLinearVelocity([0,0,0], True) + + # Rotation + # targets = GetRacingTargets(car, racename, checknumber) + + # if targets.get("rot"): + + # car.orientation = targets.get("rot") + + # else: + + tocheck = car.getVectTo(checkpoint2["location"]) + car.alignAxisToVect( (0,0,1), 2, 1.0 ) + car.alignAxisToVect(-tocheck[1], 1, 1.0) + #if UpSideDown(car): + + + if visible: + # Sparkles on the new position + Destruction.particles("Sparkle", car.position, + despawn = random.randint(30, 100), + amount = 50, + max = 100, + spread = 2, + normal = 0.05, + scale = 5) + return True + +def GetRacingTargets(car, racename, checkpoint): + + # This function gives racing targets + # such as target speed and such. + + race = bge.logic.globalDict["races"][racename] + + + # Choosing the recoring + if "race-recording" not in car: + try: + car["race-recording"] = random.choice(list(race.get("raceData", {}).keys())) + except: + return {} + + rr = car["race-recording"] + + answer = {} + + + + # Speed + try: answer["speed"] = race["raceData"][rr]["speed"][checkpoint] + except: pass + + # Rotation + try: answer["rot"] = race["raceData"][rr]["rot"][checkpoint] + except: pass + + # Time + try: answer["time"] = race["raceData"][rr]["time"][checkpoint] + except: pass + + return answer + +def TargetedNPC(car): + + # Making sure that the NPC is + # going somewhere + + if not car.get("target"): + RandomTarget(car) + + if not car.get("path"): + car["path"] = FindPathTo(car, car["target"]) + + + # Getting the path + path = car.get("path", []) + + # Pointing the car at the next node on the path + # or at the target. + if len(path) > 1: + nexttarget = path[-2] + else: + nexttarget = car["target"] + + # Finiding vector to the nexttarget + vect = car.getVectTo(nexttarget) + distance = vect[0] + ontarget = -vect[2][1] + + # Finding a path to the target. + if distance < 15: + car["path"] = FindPathTo(car, car["target"]) + + + # Turning calculation + pturn = vect[2][0] + vturn, mindist = VisionTurn(car, True) + vturn *= 2 + + maxv = 10 + + if vect[0] > 50: + turn = (pturn*(1-ontarget)) + (vturn*ontarget) + else: + turn = pturn + + # Cant's velocity + v = -car.localLinearVelocity[1] + + maxv *= max(0.2, ontarget) + + if maxv > v and ontarget > -0.3: + Accelerate(car, 1) + car["turn"] = turn + #SoftTurn(car, turn) + if OnGround(car): + nitro = True + + else: + Accelerate(car, -1) + #SoftTurn(car, -turn) + car["turn"] = -turn + + + # else: + # car["stuck"] = True + + # if car.get("stuck") and ontarget < 0: + # if 0.5 > -v: + # Accelerate(car, -1) + # SoftTurn(car, -turn*10) + + # else: + # car["stuck"] = False + + # Is the car already at the target place? + #finished = closeFrac > 0.5 and car["target"] == nexttarget + + #if finished: IdleBraking(car) + + # Termination of NPC logic + if car.get("underwater")\ + or car.getDistanceTo(car.get("target")) < 5: + car["npc"] = False + car["target"] = [] + +def RandomTarget(car): + + Navigation = bge.logic.globalDict["Navigation"] + car["target"] = random.choice(Navigation["parking"])["position"] + +def VisionTurn(car, return_min=False): + + # Calculating turn depending on what the + # car car see. + + + # There is this object used for separating + # the road sides. + sep = "Separation" + + # There is also a material for the road which + # we will ignore + road = "ROAD" + + vo = car.get("visionOffset", 0) + + # Making sure we scan a wider range + height = -3#random.uniform(-6, -3) + width = -10#random.uniform(-15, -10) + + # Getting the rays + toright = RelativePoint(car, (-5, width, height)) + fromright = RelativePoint(car, (-0.8,0+vo,0.5)) + rightray = BeautyRayCast(car, "right", toright, fromright, dist=50, poly=True) + try: rightm = str(rightray[3].material).upper() + except:rightm = "" + + # And for the left too + toleft = RelativePoint(car, (5, width, height)) + fromleft = RelativePoint(car, (0.8,0+vo,0.5)) + leftray = BeautyRayCast(car, "left", toleft, fromleft, dist=50, poly=True) + try: leftm = str(leftray[3].material).upper() + except:leftm = "" + + # Upper Forward rays + + toupright = RelativePoint(car, (-5, width, 0.5)) + fromupright = RelativePoint(car, (-0.8,0+vo,0.5)) + uprightray = BeautyRayCast(car, "upright", toupright, fromupright, dist=50, poly=True) + try: uprightm = str(uprightray[3].material).upper() + except:uprightm = "" + + + toupleft = RelativePoint(car, (5, width, 0.5)) + fromupleft = RelativePoint(car, (0.8,0+vo,0.5)) + upleftray = BeautyRayCast(car, "upleft", toupleft, fromupleft, dist=50, poly=True) + try: upleftm = str(upleftray[3].material).upper() + except:upleftm = "" + + + + + # Forward ray + #if return_min: + toforward = RelativePoint(car, (0, -10, -1)) + fromforward = RelativePoint(car, (0,-2+vo,0.5)) + forwardray = BeautyRayCast(car, "forward", toforward, fromforward, dist=50, poly=True) + try: forwardm = str(forwardray[3].material).upper() + except:forwardm = "" + + toupforward = RelativePoint(car, (0, -10, 0.5)) + fromupforward = RelativePoint(car, (0,-2+vo,0.5)) + upforwardray = BeautyRayCast(car, "upforward", toupforward, fromupforward, dist=50, poly=True) + try: upforwardm = str(upforwardray[3].material).upper() + except:upforwardm = "" + + + right = 50 + left = 50 + upright = 50 + upleft = 50 + forward = 50 + upforward = 50 * 4 + + LINE = "MATRACK_LIGHT.002" + + + if forwardray[1] and road not in forwardm: + forward = car.getDistanceTo(forwardray[1]) + + if upforwardray[1] and road not in upforwardm: + upforward = car.getDistanceTo(upforwardray[1]) + + if leftray[1] and road not in leftm: + left = car.getDistanceTo(leftray[1]) + + if rightray[1] and road not in rightm: + right = car.getDistanceTo(rightray[1]) + + if upleftray[1] and road not in upleftm: + upleft = car.getDistanceTo(upleftray[1]) + + if uprightray[1] and road not in uprightm: + upright = car.getDistanceTo(uprightray[1]) + + left = min(left, upleft) + right = min(right, upright) + forward = min(forward, upforward / 4) + + # Anti britishing department + if uprightm == LINE and not car.get("race"): + left *= 0.9 + right = max(left*1.01, right) + + # Race ignoring line + if car.get("racing"): + if uprightm == LINE or rightm == LINE: + right = 10 + if upleftm == LINE or rightm == LINE: + left = 10 + + forward /= 2 + mindist = min(left, right, forward, upright, upleft) + #maxdist = max(left, right, forward, upright, upleft) + + st = 5 + turn = max(-0.8, min(0.8, (( max(0, min(1, ( right / st ))) - max(0, min(1, ( left / st ))) ) *-2 ))) + + # Another version of the turn calculation + + # minturn = min(left, right) + # maxturn = max(left, right) + + # turn = ( ( left / maxturn ) - ( right / maxturn ) ) * ( maxturn / minturn / 4 ) + + # Avoiding cars and stuff + if upforwardray[0]: + obj = upforwardray[0] + if "speed" in car and obj != car.get("target"): + car["avoid"] = obj + + if car.get("avoid"): + obj = car["avoid"] + vect = car.getVectTo(obj) + v = -car.localLinearVelocity[1] + closeFrac = -(min(1, vect[0]/(v*5))-1) + if vect[2][0] > 0: pturn = -1 + else: pturn = 1 + turn = (turn*(1-closeFrac)) + (pturn*closeFrac) + + onobj = -vect[2][1] + if onobj < 1-closeFrac: + car["avoid"] = None + + if not return_min: + return turn + else: + return turn, mindist + +def SoftTurn(car, turn): + + if turn < 0 and car["turn"] > turn: + TurnRight(car) + elif turn > 0 and car["turn"] < turn: + TurnLeft(car) + +def FindPathTo(car, location): + + # Since this could be an object, + # we will try to unlcoking the location + + try: location = location.position + except: pass + location = mathutils.Vector(location) + + path = [] + + Navigation = bge.logic.globalDict["Navigation"] + + def findClosest(car, location, points, exclude): + + + cd = 1000 + nd = None + + for node in points: + + if location == None: location = node + distance = math.dist(location, node ) + distance += car.getDistanceTo(node) / 4 + + if distance < cd and node not in exclude: + cd = distance + nd = node + + return nd + + + points = list(n["position"] for n in Navigation["road"]) + [car.position] + + while not path or path[-1] != car.position: + + if not path: + path.append(findClosest(car, location, points, path)) + else: + path.append(findClosest(car, path[-1], points, path)) + + for n, node in enumerate(path): + if n == 0: + DrawLine(car, "Main", node, location) + else: + DrawLine(car, n, node, path[n-1]) + + return path + +def StraightLine(car, point1, point2): + + ray = car.rayCast(point1, point2) + if ray[1]: + cd = math.dist(point1, point2) + ad = math.dist(point2, ray[1]) + if round(cd, 1) == round(ad, 1): + return True + else: + return False + return True + + +##### SOUND RELATED FUNCTIONS #### + +def EngineSound(car): + + # Engine Sound + + device = bge.logic.globalDict["SoundDevice"] + + s = car.get("engine_sound","//sfx/engines/neonspeedster.ogg") + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + + pitch = RPMWithIdle(car, normalize=True) / car["specs"]["maxrpm"] + + sound["play"].pitch = pitch * 3 + sound["play"].volume = car.get("health", 1) + + # Grind sound + v = -car.localLinearVelocity[1] + if v < 0: v *= -1 + + s = "//sfx/grind.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + if v > 0.01: + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + + + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = max(0.3, car.get("health", 1)) * 2 + sound["play"].volume = (1-car.get("health", 1)) * min(1.5, v *8) * 1 + + else: + + try: + bge.logic.globalDict["sounds"][code]["play"].stop() + except: + pass + +def GearShiftSound(car): + + # This is the sound of the gear + # being shifted. + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/gear_shit.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].volume = 3 + +def DriftSound(car): + + # This is a sound of rubber skidding. + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/drift.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + slide = car.localLinearVelocity[0] + + # Adding nitro + AddNitro(car, slide / 5000) + + if car.get("braking"): + slide = car.localLinearVelocity[1] + if slide < 0: + slide *= -1 + + sound = bge.logic.globalDict["sounds"][code] + if slide > 1 and OnGround(car): + + #car["nitro"] += 2 + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + sound["play"].location = car.worldPosition + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + #sound["play"].pitch = 0.8 + sound["play"].volume = min(2, slide / 5) / 5 + + + else: + + try: + bge.logic.globalDict["sounds"][code]["play"].stop() + except: + pass + +def ScrapeSound(car, position=None, volume=3): + + # This function produces + # a sound of scraping metal. + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/scrape.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + if not position: + sound["play"].location = car.worldPosition + else: + sound["play"].location = position + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = volume + +def RedlineSound(car, position=None, volume=3): + + # This function produces + # a sound of scraping metal. + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/redline.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + if not position: + sound["play"].location = car.worldPosition + else: + sound["play"].location = position + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + #sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = volume + +def HitSound(car, position=None, volume=3): + + # This function produces + # a sound of scraping metal. + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/hit.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + if not position: + sound["play"].location = car.worldPosition + else: + sound["play"].location = position + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = volume + +def GlassSound(car, position=None, volume=3): + + # This function produces + # a sound of scraping metal. + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/glass.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + if not position: + sound["play"].location = car.worldPosition + else: + sound["play"].location = position + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = volume + +def WheelPopSound(car, position=None, volume=3): + + # This function produces + # a sound of scraping metal. + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/tire_pop.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + if not position: + sound["play"].location = car.worldPosition + else: + sound["play"].location = position + sound["play"].velocity = car.getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 200 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(0.6, 1.4) + sound["play"].volume = volume + +def NitroSound(car): + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/nitro.ogg" + code = s+str(car["cid"]) + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.position + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = 1 + sound["play"].volume = 5 + +def NitroSoundStop(car): + + device = bge.logic.globalDict["SoundDevice"] + + s = "//sfx/nitro.ogg" + code = s+str(car["cid"]) + if code in bge.logic.globalDict["sounds"]: + sound = bge.logic.globalDict["sounds"][code] + try: + if sound["play"].volume > 0: + sound["play"].volume -= 0.5 + else: + sound["play"].stop() + except: + pass + +def DaniOhGodSound(car): + + # Dani saying, oh no! + + scene, car = GetSceneAndCar(car) + device = bge.logic.globalDict["SoundDevice"] + dani = scene.objects["Dani_Box"] + + # Dani animation of opening mouth + Character_Controll.ApplyAnimation(dani, "Talk") + + # The sound itself. + device.play(bge.logic.globalDict["sounds"]["dani_ohgod"]["sound"]) + +def StartEnemySounds(car): + + # Angry voice when player hits somebody + + device = bge.logic.globalDict["SoundDevice"] + + sound = random.choice(bge.logic.globalDict["sounds_angry_start"]) + sound = bge.logic.globalDict["sounds"][sound] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.position + sound["play"].relative = False + sound["play"].distance_maximum = 50 + sound["play"].distance_reference = 5 + sound["play"].attenuation = 1 + sound["play"].volume = 1.5 + + + car["enemysound"] = sound["play"] + +def HitEnemySound(car): + + # Angy voice when the enemy hits player + + device = bge.logic.globalDict["SoundDevice"] + + if not car.get("enemysound") or not car["enemysound"].status: + + sound = random.choice(bge.logic.globalDict["sounds_angry_hit"]) + sound = bge.logic.globalDict["sounds"][sound] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = car.position + sound["play"].relative = False + sound["play"].distance_maximum = 50 + sound["play"].distance_reference = 5 + sound["play"].attenuation = 1 + sound["play"].volume = 1.5 + + car["enemysound"] = sound["play"] + + +#### SAVE / MULTIPLAYER FUNCTIONS ##### + +def Encode(car): + + # This function will generate a copy of the car's + # data that is possible to save into a Json file. + + # General data + cardata = { + "ID" : id(car), + "type" : "veh", + "name" : car.name, + "cid" : int(car["cid"]), + "netId" : car.get("netId"), + "position" : list(car.position), + "orientation" : list(car.orientation.to_euler()) + } + + # Health data + cardata["health"] = car.get("health" , 1.0) + cardata["nitro"] = car.get("nitro" , 0.0) + cardata["NitroCan"] = car.get("NitroCan" , False) + cardata["blown"] = car.get("blown" , False) + cardata["Spoiler"] = car.get("Spoiler", False) + + cardata["doors"] = [] + for door in car.get("doors",[]): + doordata = { + "health" : door["health"], + "locked" : door["locked"], + "visible" : door.visible + } + cardata["doors"].append(doordata) + + cardata["wheels"] = [] + for wheel in car.get("wheels", []): + wheeldata = { + "health" : wheel["health"], + "visible" : wheel.visible + } + cardata["wheels"].append(wheeldata) + + # Color data + cardata["colors"] = car.get("colors", {}).copy() + for colorname in cardata["colors"]: + color = cardata["colors"][colorname] + color = list(color) + cardata["colors"][colorname] = color + + if not car.get("ownerId"): + car["ownerId"] = bge.logic.globalDict.get("userId") + + netObjects = bge.logic.globalDict["netObjects"] + if cardata["ID"] not in netObjects["pythonId"]: + netObjects["pythonId"][cardata["ID"]] = car + + return cardata + +def Decode(cardata, new=False, network=False): + + # This function will restore the car + # from the encoded state. + + # Making the car + carModel = cardata.get("name" , "NeonSpeedsterBox") + position = cardata.get("position" , [0,0,0]) + orientation = cardata.get("orientation", [0,0,0]) + + # Network related recognizing + netId = cardata.get("netId") + + netObjects = bge.logic.globalDict["netObjects"] + + if netId and netId not in netObjects["netId"] or new: + + car = Spawn(carModel, position, orientation) + UpdateCar(car) + + print("Spawned cause", netId, "netId isn't in", list(netObjects["netId"].keys())) + + netObjects["netId"][netId] = car + + + + car = netObjects["netId"][netId] + car["netId"] = netId + car["ownerId"] = cardata.get("ownerId") + + if math.dist(car.position, position) > 0.1: + car.position = position + if math.dist(car.orientation.to_euler(), orientation) > 0.1: + car.orientation = orientation + + + # Health stuff + + car["health"] = cardata.get("health", 1.0) + car["nitro"] = cardata.get("nitro", 0.0) + + if cardata.get("NitroCan" , False): + AddNitroCan(car) + + if cardata.get("Spoiler" , False): + AddSpoiler(car, cardata["Spoiler"]) + + car["blown"] = cardata.get("blown", False) + if car["health"] < 0.3: + ChangeBody(car, good=False) + + for n, door in enumerate(car.get("doors",[])): + + # Getting door data + try: + doordata = cardata.get("doors",[])[n] + except Exception as e: + print("Failed to load door",n,"for", car, ":", e) + break + + # Restoring the door + door["health"] = doordata.get("health", 1.0) + door["locked"] = doordata.get("locked", True) + door.visible = doordata.get("visible", True) + + if door["health"] < 1.0: + if "Borked" in door and door.get("mesh", "Good") != "Borked": + door.replaceMesh(door["Borked"]) + door["mesh"] = "Borked" + + for n, wheel in enumerate(car.get("wheels", [])): + + # Getting wheel data + try: + wheeldata = cardata.get("wheels",[])[n] + except Exception as e: + print("Failed to load wheel",n,"for", car, ":", e) + break + + wheel["health"] = wheeldata.get("health", 1.0) + wheel.visible = wheeldata.get("visible", True) + + # Colors + for attribute in cardata.get("colors", {}): + value = cardata["colors"][attribute] + Material(car, value, attribute) + +def RemoveNetId(car): + + # Removing NetId from the car + # for when the play has taken it + # or when it's despawned. + + if "netId" in car and "netCars" in bge.logic.globalDict: + + netCars = bge.logic.globalDict["netCars"] + netId = car["netId"] + + if netId in netCars: + del car["netId"] + del netCars[netId] + + +#### DEBUGGIN FUNCTIONS ###### + +def DrawLine(car, name, point1, point2): + # To deactivate this function. + settings = bge.logic.globalDict["settings"] + if not settings.get("dev-rays"): return + + if "lines" not in car: + car["lines"] = {} + if name not in car["lines"]: + car["lines"][name] = Reuse.Create("Redline") + + line = car["lines"][name] + line.position = point1 + vect = line.getVectTo(point2) + line.alignAxisToVect(vect[1], 2, 1) + line.scaling.z = vect[0] + + scale = 2 + line.scaling.x = scale + line.scaling.y = scale + +def BeautyRayCast(car, name, to, fro, *args, **kwargs): + + # This will make a KX_Object.rayCast() function be more + # pretty utilizing DrawLine() function. + + ray = car.rayCast(to, fro, *args, **kwargs) + + if ray[1]: + DrawLine(car, name, fro, ray[1]) + else: + DrawLine(car, name, fro, to) + + return ray + +def ActivePrint(car, *text): + + # Prints something only from the car + # that is driven by the player ( or + # one that dani is in the driver's + # seat of, in the case of AI debugging ). + + scene, car = GetSceneAndCar(car) + dani = scene.objects["Dani_Box"] + + if car.get("active") or dani.get("driving") == car: + print(*text) + diff --git a/SettingUI/banner.jpg b/SettingUI/banner.jpg new file mode 100644 index 0000000..b29ef94 Binary files /dev/null and b/SettingUI/banner.jpg differ diff --git a/SettingUI/banner.png b/SettingUI/banner.png new file mode 100644 index 0000000..2db3fbb Binary files /dev/null and b/SettingUI/banner.png differ diff --git a/SettingUI/icon.png b/SettingUI/icon.png new file mode 100644 index 0000000..e5981ed Binary files /dev/null and b/SettingUI/icon.png differ diff --git a/animdata/chr/Dani_Box.json b/animdata/chr/Dani_Box.json new file mode 100644 index 0000000..eccaa9f --- /dev/null +++ b/animdata/chr/Dani_Box.json @@ -0,0 +1,61 @@ +{ + + "Talk":{ + "Action":"DaniEmotions", + "Frames":[0, 10], + "kwargs":{"layer":3} + }, + + "Laugh":{ + "Action":"DaniEmotions", + "Frames":[10, 20], + "kwargs":{"layer":3} + }, + + "Stand":{ + "Frames":[0, 290] + }, + + "Walk":{ + "Frames":[301, 313] + }, + + "Drive":{ + "Frames":[330,330] + }, + + "Swim":{ + "Frames":[340,353] + }, + + "Fly":{ + "Frames":[358,371], + "kwargs":{"priority":-1, + "blendin":5} + }, + + "Hang":{ + "Frames":[390,390], + "kwargs":{"priority":-1} + }, + + "Land":{ + "Frames":[371,384], + "kwargs":{"priority":-1} + + }, + + + "AnswerPhone":{ + "Action":"DanisPhone", + "Frames":[32, 100], + "kwargs":{"layer":2}, + "Keep":true + }, + + "PutPhoneAway":{ + "Action":"DanisPhone", + "Frames":[0, 32], + "kwargs":{"layer":2} + } +} diff --git a/animdata/chr/JackBox.json b/animdata/chr/JackBox.json new file mode 100644 index 0000000..07655f2 --- /dev/null +++ b/animdata/chr/JackBox.json @@ -0,0 +1,21 @@ +{ + + "Stand":{ + "Frames":[0,300] + }, + + "Walk":{ + "Frames":[330, 350] + }, + + "Drive":{ + "Frames":[360,361] + }, + + "Talk":{ + "Action":"JackTalk", + "Frames":[0, 20], + "kwargs":{"layer":3} + } + +} diff --git a/animdata/chr/MoriaBox.json b/animdata/chr/MoriaBox.json new file mode 100644 index 0000000..fc6781e --- /dev/null +++ b/animdata/chr/MoriaBox.json @@ -0,0 +1,14 @@ +{ + + "Stand":{ + "Frames":[0,276] + }, + + "Walk":{ + "Frames":[300, 320] + }, + + "Drive":{ + "Frames":[325,325] + } +} diff --git a/animdata/chr/PapsBox.json b/animdata/chr/PapsBox.json new file mode 100644 index 0000000..c2bc6a4 --- /dev/null +++ b/animdata/chr/PapsBox.json @@ -0,0 +1,17 @@ +{ + "Walk":{ + "Frames":[301, 330] + }, + + "Stand":{ + "Frames":[0, 290] + }, + + "Talk":{ + "Action":"PapsTalk", + "Frames":[0, 10], + "kwargs":{"layer":3} + } + + +} diff --git a/assembly.blend b/assembly.blend new file mode 100644 index 0000000..4af125c Binary files /dev/null and b/assembly.blend differ diff --git a/ast/loc/City_source.blend b/ast/loc/City_source.blend new file mode 100644 index 0000000..a4e590d Binary files /dev/null and b/ast/loc/City_source.blend differ diff --git a/ast/loc/city.blend b/ast/loc/city.blend new file mode 100644 index 0000000..d23f3a0 Binary files /dev/null and b/ast/loc/city.blend differ diff --git a/ast/loc/textures/Ground.jpg b/ast/loc/textures/Ground.jpg new file mode 100644 index 0000000..7606fca Binary files /dev/null and b/ast/loc/textures/Ground.jpg differ diff --git a/ast/loc/textures/ads.png b/ast/loc/textures/ads.png new file mode 100644 index 0000000..817d551 Binary files /dev/null and b/ast/loc/textures/ads.png differ diff --git a/ast/loc/textures/bush.png b/ast/loc/textures/bush.png new file mode 100644 index 0000000..45889e3 Binary files /dev/null and b/ast/loc/textures/bush.png differ diff --git a/ast/loc/textures/cliff.jpg b/ast/loc/textures/cliff.jpg new file mode 100644 index 0000000..f69090c Binary files /dev/null and b/ast/loc/textures/cliff.jpg differ diff --git a/ast/loc/textures/dirt.jpg b/ast/loc/textures/dirt.jpg new file mode 100644 index 0000000..b40e52d Binary files /dev/null and b/ast/loc/textures/dirt.jpg differ diff --git a/ast/loc/textures/sings.png b/ast/loc/textures/sings.png new file mode 100644 index 0000000..49811fb Binary files /dev/null and b/ast/loc/textures/sings.png differ diff --git a/ast/veh/DarkShadow.blend b/ast/veh/DarkShadow.blend new file mode 100644 index 0000000..2eed836 Binary files /dev/null and b/ast/veh/DarkShadow.blend differ diff --git a/ast/veh/HatchBack01.blend b/ast/veh/HatchBack01.blend new file mode 100644 index 0000000..a2d08ee Binary files /dev/null and b/ast/veh/HatchBack01.blend differ diff --git a/ast/veh/NeonSpeedster.blend b/ast/veh/NeonSpeedster.blend new file mode 100644 index 0000000..7917eaa Binary files /dev/null and b/ast/veh/NeonSpeedster.blend differ diff --git a/ast/veh/RedKiss.blend b/ast/veh/RedKiss.blend new file mode 100644 index 0000000..9c44620 Binary files /dev/null and b/ast/veh/RedKiss.blend differ diff --git a/ast/veh/Truck.blend b/ast/veh/Truck.blend new file mode 100644 index 0000000..dd5d2c8 Binary files /dev/null and b/ast/veh/Truck.blend differ diff --git a/ast/veh/bgelogic/NLNodeTree.py b/ast/veh/bgelogic/NLNodeTree.py new file mode 100644 index 0000000..a3889bd --- /dev/null +++ b/ast/veh/bgelogic/NLNodeTree.py @@ -0,0 +1,62 @@ +# MACHINE GENERATED +import bge, bpy, sys, importlib +import mathutils +from uplogic import nodes +import math + +def _initialize(owner): + network = nodes.LogicNetwork() + CON0000 = nodes.ConditionOnce() + ACT0001 = nodes.VehicleSetAttributes() + ACT0002 = nodes.ActionCreateVehicleFromParent() + ACT0003 = nodes.VehicleApplyForce() + CON0004 = nodes.ConditionKeyPressed() + CON0000.input_condition = False + CON0000.repeat = False + CON0000.reset_time = 0.5 + ACT0001.condition = ACT0002.OUT + ACT0001.constraint = ACT0002.VEHICLE + ACT0001.wheelcount = 4 + ACT0001.set_suspension_compression = False + ACT0001.suspension_compression = 0.0 + ACT0001.set_suspension_stiffness = False + ACT0001.suspension_stiffness = 0.0 + ACT0001.set_suspension_damping = False + ACT0001.suspension_damping = 0.0 + ACT0001.set_tyre_friction = False + ACT0001.tyre_friction = 0.0 + ACT0001.value_type = 'REAR' + ACT0002.condition = CON0000 + ACT0002.game_object = "NLO:RedKissBox" + ACT0002.suspension = 0.05999999865889549 + ACT0002.stiffness = 50.0 + ACT0002.damping = 5.0 + ACT0002.friction = 2.0 + ACT0002.wheel_size = 1.0 + ACT0003.condition = CON0004 + ACT0003.vehicle = "NLO:RedKissBox" + ACT0003.wheelcount = 2 + ACT0003.power = 1.0 + ACT0003.value_type = 'REAR' + CON0004.key_code = bge.events.WKEY + CON0004.pulse = True + network.add_cell(CON0000) + network.add_cell(ACT0002) + network.add_cell(CON0004) + network.add_cell(ACT0001) + network.add_cell(ACT0003) + owner["IGNLTree_NodeTree"] = network + network._owner = owner + network.setup() + network.stopped = not owner.get('NL__NodeTree') + return network + +def pulse_network(controller): + owner = controller.owner + network = owner.get("IGNLTree_NodeTree") + if network is None: + network = _initialize(owner) + if network.stopped: return + shutdown = network.evaluate() + if shutdown is True: + controller.sensors[0].repeat = False diff --git a/ast/veh/bgelogic/__init__.py b/ast/veh/bgelogic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/ast/veh/textures/Front_lights.png b/ast/veh/textures/Front_lights.png new file mode 100644 index 0000000..aef723b Binary files /dev/null and b/ast/veh/textures/Front_lights.png differ diff --git a/ast/veh/textures/LightLInes.png b/ast/veh/textures/LightLInes.png new file mode 100644 index 0000000..049e0f3 Binary files /dev/null and b/ast/veh/textures/LightLInes.png differ diff --git a/ast/veh/textures/Light_dots.png b/ast/veh/textures/Light_dots.png new file mode 100644 index 0000000..205bffe Binary files /dev/null and b/ast/veh/textures/Light_dots.png differ diff --git a/ast/veh/textures/Paps_Truck_Texture.png b/ast/veh/textures/Paps_Truck_Texture.png new file mode 100644 index 0000000..467a220 Binary files /dev/null and b/ast/veh/textures/Paps_Truck_Texture.png differ diff --git a/ast/veh/textures/Paps_Truck_Texture_TEXT.png b/ast/veh/textures/Paps_Truck_Texture_TEXT.png new file mode 100644 index 0000000..fee7a1b Binary files /dev/null and b/ast/veh/textures/Paps_Truck_Texture_TEXT.png differ diff --git a/ast/veh/textures/Paps_Truck_Texture_TEXT2.png b/ast/veh/textures/Paps_Truck_Texture_TEXT2.png new file mode 100644 index 0000000..7e0f031 Binary files /dev/null and b/ast/veh/textures/Paps_Truck_Texture_TEXT2.png differ diff --git a/ast/veh/textures/Red_kiss_neon.png b/ast/veh/textures/Red_kiss_neon.png new file mode 100644 index 0000000..527fdb2 Binary files /dev/null and b/ast/veh/textures/Red_kiss_neon.png differ diff --git a/ast/veh/textures/blue_kiss.jpg b/ast/veh/textures/blue_kiss.jpg new file mode 100644 index 0000000..d7c11cb Binary files /dev/null and b/ast/veh/textures/blue_kiss.jpg differ diff --git a/ast/veh/textures/front_light.png b/ast/veh/textures/front_light.png new file mode 100644 index 0000000..ae5d459 Binary files /dev/null and b/ast/veh/textures/front_light.png differ diff --git a/ast/veh/textures/front_part.png b/ast/veh/textures/front_part.png new file mode 100644 index 0000000..79d07f8 Binary files /dev/null and b/ast/veh/textures/front_part.png differ diff --git a/ast/veh/textures/gear_lever.png b/ast/veh/textures/gear_lever.png new file mode 100644 index 0000000..98c9402 Binary files /dev/null and b/ast/veh/textures/gear_lever.png differ diff --git a/ast/veh/textures/gear_mechanism.png b/ast/veh/textures/gear_mechanism.png new file mode 100644 index 0000000..8fafde5 Binary files /dev/null and b/ast/veh/textures/gear_mechanism.png differ diff --git a/ast/veh/textures/leather.jpg b/ast/veh/textures/leather.jpg new file mode 100644 index 0000000..23667a4 Binary files /dev/null and b/ast/veh/textures/leather.jpg differ diff --git a/ast/veh/textures/lightdots.png b/ast/veh/textures/lightdots.png new file mode 100644 index 0000000..298c07d Binary files /dev/null and b/ast/veh/textures/lightdots.png differ diff --git a/ast/veh/textures/map.png b/ast/veh/textures/map.png new file mode 100644 index 0000000..faade66 Binary files /dev/null and b/ast/veh/textures/map.png differ diff --git a/ast/veh/textures/map_UI_Over.png b/ast/veh/textures/map_UI_Over.png new file mode 100644 index 0000000..41dcca3 Binary files /dev/null and b/ast/veh/textures/map_UI_Over.png differ diff --git a/ast/veh/textures/screen.png b/ast/veh/textures/screen.png new file mode 100644 index 0000000..1839204 Binary files /dev/null and b/ast/veh/textures/screen.png differ diff --git a/ast/veh/textures/tire_text.png b/ast/veh/textures/tire_text.png new file mode 100644 index 0000000..677938f Binary files /dev/null and b/ast/veh/textures/tire_text.png differ diff --git a/bgelogic/NLNodeTree.py b/bgelogic/NLNodeTree.py new file mode 100644 index 0000000..a3889bd --- /dev/null +++ b/bgelogic/NLNodeTree.py @@ -0,0 +1,62 @@ +# MACHINE GENERATED +import bge, bpy, sys, importlib +import mathutils +from uplogic import nodes +import math + +def _initialize(owner): + network = nodes.LogicNetwork() + CON0000 = nodes.ConditionOnce() + ACT0001 = nodes.VehicleSetAttributes() + ACT0002 = nodes.ActionCreateVehicleFromParent() + ACT0003 = nodes.VehicleApplyForce() + CON0004 = nodes.ConditionKeyPressed() + CON0000.input_condition = False + CON0000.repeat = False + CON0000.reset_time = 0.5 + ACT0001.condition = ACT0002.OUT + ACT0001.constraint = ACT0002.VEHICLE + ACT0001.wheelcount = 4 + ACT0001.set_suspension_compression = False + ACT0001.suspension_compression = 0.0 + ACT0001.set_suspension_stiffness = False + ACT0001.suspension_stiffness = 0.0 + ACT0001.set_suspension_damping = False + ACT0001.suspension_damping = 0.0 + ACT0001.set_tyre_friction = False + ACT0001.tyre_friction = 0.0 + ACT0001.value_type = 'REAR' + ACT0002.condition = CON0000 + ACT0002.game_object = "NLO:RedKissBox" + ACT0002.suspension = 0.05999999865889549 + ACT0002.stiffness = 50.0 + ACT0002.damping = 5.0 + ACT0002.friction = 2.0 + ACT0002.wheel_size = 1.0 + ACT0003.condition = CON0004 + ACT0003.vehicle = "NLO:RedKissBox" + ACT0003.wheelcount = 2 + ACT0003.power = 1.0 + ACT0003.value_type = 'REAR' + CON0004.key_code = bge.events.WKEY + CON0004.pulse = True + network.add_cell(CON0000) + network.add_cell(ACT0002) + network.add_cell(CON0004) + network.add_cell(ACT0001) + network.add_cell(ACT0003) + owner["IGNLTree_NodeTree"] = network + network._owner = owner + network.setup() + network.stopped = not owner.get('NL__NodeTree') + return network + +def pulse_network(controller): + owner = controller.owner + network = owner.get("IGNLTree_NodeTree") + if network is None: + network = _initialize(owner) + if network.stopped: return + shutdown = network.evaluate() + if shutdown is True: + controller.sensors[0].repeat = False diff --git a/bgelogic/__init__.py b/bgelogic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/cardata/DarkShadowBox.json b/cardata/DarkShadowBox.json new file mode 100644 index 0000000..6bd4a7b --- /dev/null +++ b/cardata/DarkShadowBox.json @@ -0,0 +1,43 @@ +{ + "name":"Dark Shadow", + "probability":1, + "type":"race-car", + + "material":{"MainColor" :[0.033096, 0.033096, 0.033096], + "SecondaryColor":[1.000000, 0.147027, 0.000000]}, + + "wheels":[ + { "radius":0.425, "xyz":[ 0.915, -1.427, -0.150], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":""}, + { "radius":0.425, "xyz":[-0.915, -1.427, -0.150], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":".001"}, + { "radius":0.425, "xyz":[-0.915, 1.184, -0.150], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".003"}, + { "radius":0.425, "xyz":[ 0.915, 1.184, -0.150], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".002"} + ], + + "mass" : 1993 , + "hp" : 610 , + "torque" : 100 , + "drag" : 0.5 , + "roll" : -0.4 , + "grip" : 0.3 , + "maxturn" : 0.8 , + "brakes" : 2.0 , + "abs" : 0.01 , + "downforce" : 2.0 , + "suspention" : 100.0 , + "suspentionDamping" : 0.8 , + "maxrpm" : 8000 , + "idle" : 1100 , + "redline" : 6000 , + + "gears":[ + 5.00, + 3.00, + 2.00, + 1.00, + 0.75, + 0.50, + 0.25, + 0.20, + 0.15 + ] + } diff --git a/cardata/HatchBack01Box.json b/cardata/HatchBack01Box.json new file mode 100644 index 0000000..6370f43 --- /dev/null +++ b/cardata/HatchBack01Box.json @@ -0,0 +1,35 @@ +{ + "name":"Hatchback Model 1", + "probability":10, + "type":"street-car", + + "wheels":[ + { "radius":0.383, "xyz":[ 0.919, -1.519, -0.200], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":""}, + { "radius":0.383, "xyz":[-0.919, -1.519, -0.200], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":".001"}, + { "radius":0.383, "xyz":[-0.919, 1.522, -0.200], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".003"}, + { "radius":0.383, "xyz":[ 0.919, 1.522, -0.200], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".002"} + ], + + "mass" : 1993 , + "hp" : 50 , + "torque" : 50 , + "drag" : 1.5 , + "roll" : 0.4 , + "grip" : 0.1 , + "maxturn" : 0.8 , + "brakes" : 1.0 , + "abs" : 0.1 , + "downforce" : 0.5 , + "suspention" : 50.0 , + "suspentionDamping" : 0.8 , + "maxrpm" : 6000 , + "idle" : 1100 , + "redline" : 5000 , + + "gears":[ + 5.00, + 2.00, + 1.00, + 0.50 + ] + } diff --git a/cardata/NeonSpeedsterBox.json b/cardata/NeonSpeedsterBox.json new file mode 100644 index 0000000..893ff1e --- /dev/null +++ b/cardata/NeonSpeedsterBox.json @@ -0,0 +1,43 @@ +{ + "name":"Neonspeedster", + "probability":1, + "type":"race-car", + + "material":{"MainColor" :[0.051525, 0.514757, 1.000000], + "SecondaryColor":[0.051525, 0.514757, 1.000000]}, + + "wheels":[ + { "radius":0.42, "xyz":[ 0.89, -1.25, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":""}, + { "radius":0.42, "xyz":[-0.89, -1.25, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":".001"}, + { "radius":0.42, "xyz":[-0.89, 1.65, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".003"}, + { "radius":0.42, "xyz":[ 0.89, 1.65, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".002"} + ], + + "mass" : 1800 , + "hp" : 600 , + "torque" : 120 , + "drag" : 0.25 , + "roll" : -0.4 , + "grip" : 0.3 , + "maxturn" : 0.8 , + "brakes" : 2.0 , + "abs" : 0.01 , + "downforce" : 2.0 , + "suspention" : 200.0 , + "suspentionDamping" : 0.6 , + "maxrpm" : 8000 , + "idle" : 1100 , + "redline" : 6000 , + + "gears":[ + 5.00, + 3.00, + 2.00, + 1.00, + 0.75, + 0.50, + 0.25, + 0.20, + 0.15 + ] + } diff --git a/cardata/RedKissBox.json b/cardata/RedKissBox.json new file mode 100644 index 0000000..db16a49 --- /dev/null +++ b/cardata/RedKissBox.json @@ -0,0 +1,43 @@ +{ + "name":"Red Kiss", + "probability":1, + "type":"race-car", + + "material":{"MainColor" :[1.000000, 0.008796, 0.000000], + "SecondaryColor":[1.000000, 0.500000, 0.000000]}, + + "wheels":[ + { "radius":0.469, "xyz":[ 0.959, -1.333, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":""}, + { "radius":0.469, "xyz":[-0.959, -1.333, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":".001"}, + { "radius":0.469, "xyz":[-0.959, 1.538, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".003"}, + { "radius":0.469, "xyz":[ 0.959, 1.538, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".002"} + ], + + "mass" : 1800 , + "hp" : 580 , + "torque" : 120 , + "drag" : 0.25 , + "roll" : -0.4 , + "grip" : 0.2 , + "maxturn" : 0.8 , + "brakes" : 2.0 , + "abs" : 0.01 , + "downforce" : 2.0 , + "suspention" : 200.0 , + "suspentionDamping" : 0.6 , + "maxrpm" : 8000 , + "idle" : 1100 , + "redline" : 6000 , + + "gears":[ + 5.00, + 3.00, + 2.00, + 1.00, + 0.75, + 0.50, + 0.25, + 0.20, + 0.15 + ] + } diff --git a/cardata/TruckBox.json b/cardata/TruckBox.json new file mode 100644 index 0000000..55a4ba8 --- /dev/null +++ b/cardata/TruckBox.json @@ -0,0 +1,53 @@ +{ + "name":"Truck", + "probability":0, + "type":"truck", + + "wheels":[ + { "radius":0.565, "xyz":[ 1.19, -3.36, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":""}, + { "radius":0.565, "xyz":[-1.19, -3.36, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":true , "object":".001"}, + { "radius":0.565, "xyz":[-1.69, 3.03, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".003"}, + { "radius":0.565, "xyz":[ 1.69, 3.03, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".002"}, + { "radius":0.565, "xyz":[-1.69, 4.22, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".004"}, + { "radius":0.565, "xyz":[ 1.69, 4.22, -0.100], "down":[0,0,-1], "axel":[-1,0,0], "suspensionRest": 0.2, "front":false, "object":".005"} + ], + + "mass" : 5000 , + "hp" : 800 , + "torque" : 50 , + "drag" : 0.9 , + "roll" : 0.4 , + "grip" : 0.4 , + "maxturn" : 0.8 , + "brakes" : 2.0 , + "abs" : 1.0 , + "downforce" : 1.0 , + "suspention" : 50.0 , + "suspentionDamping" : 0.8 , + "maxrpm" : 4000 , + "idle" : 500 , + "redline" : 3000 , + + "visionOffset" : -2 , + "cameraZoom" : 5 , + + "gears":[ + 5.00, + 4.00, + 3.00, + 2.50, + 2.00, + 1.50, + 1.00, + 0.85, + 0.75, + 0.65, + 0.50, + 0.35, + 0.25, + 0.23, + 0.20, + 0.15, + 0.10 + ] + } diff --git a/checklist.org b/checklist.org new file mode 100644 index 0000000..c91e5fa --- /dev/null +++ b/checklist.org @@ -0,0 +1,38 @@ +* Cars +** AI +*** Spawn System [4/4] +**** DONE Cars spawn all over the place +**** DONE Not enough cars +**** DONE Performace optimization +**** DONE Spawn Points Corrections [3/5] +- [X] Loop Around +- [X] To Racetrack +- [X] Through Sand +- [X] Deep Down +- [X] Small Round +*** AI NPC [6/6] +**** DONE Stupid driving +**** DONE Forward vision +**** DONE Avoid Going Into Walls +**** DONE Do not british +**** DONE Angry Pursuit +**** DONE Racing AI +** Controlls +*** Breaking system [1/2] +**** DONE Fix car tombling from breaking +**** TODO Fix the Handbrake, to feel better +** Camera +*** DONE Don't Roll if car is upside down +*** DONE While camera is in fly mode, track the target +*** TODO Avoid walls and stuff, to see car +* Characters +* Story +** 01_Bring_Neonspeedster_To_Racetrack [1/4] +*** DONE Paps rig +*** TODO Moria rig +*** TODO Jack rig +*** TODO Truck rig +**** TODO Make a drivable truck +**** TODO Make the thing work as truck +**** TODO See what can be removed +**** TODO Make it breakable. diff --git a/fonts/Economica-Bold.ttf b/fonts/Economica-Bold.ttf new file mode 100644 index 0000000..601dbcf Binary files /dev/null and b/fonts/Economica-Bold.ttf differ diff --git a/fonts/Economica-BoldItalic.ttf b/fonts/Economica-BoldItalic.ttf new file mode 100644 index 0000000..1e6f831 Binary files /dev/null and b/fonts/Economica-BoldItalic.ttf differ diff --git a/fonts/Economica-Italic.ttf b/fonts/Economica-Italic.ttf new file mode 100644 index 0000000..88a1879 Binary files /dev/null and b/fonts/Economica-Italic.ttf differ diff --git a/fonts/Economica-Regular.ttf b/fonts/Economica-Regular.ttf new file mode 100644 index 0000000..87b7c70 Binary files /dev/null and b/fonts/Economica-Regular.ttf differ diff --git a/fonts/OFL.txt b/fonts/OFL.txt new file mode 100644 index 0000000..3fc2675 --- /dev/null +++ b/fonts/OFL.txt @@ -0,0 +1,94 @@ +Copyright (c) 2012, Vicente Lamonaca (produccion.taller@gmail.com www.tipografia-montevideo.info www.tipotype.com), +with Reserved Font Name Economica. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/logo.png b/logo.png new file mode 100644 index 0000000..6c62c1f Binary files /dev/null and b/logo.png differ diff --git a/python_dumpster/Main_update_v22-02-2024.py b/python_dumpster/Main_update_v22-02-2024.py new file mode 100644 index 0000000..96fab2f --- /dev/null +++ b/python_dumpster/Main_update_v22-02-2024.py @@ -0,0 +1,1326 @@ +import bge +import bpy +import aud +import time +import datetime +import threading +import random +import numpy +import math +import mathutils + +import Reuse +import Distraction +import Opt + + +# SPAWNING ASSETS AND STUFF + +spawnAtDistance = 300 +maxCars = 6 + +scene = bge.logic.getCurrentScene() +cont = bge.logic.getCurrentController() +dani = scene.objects["Dani_Box"] +cam = scene.active_camera + +FPS = bge.logic.getAverageFrameRate() +goodFPS = FPS >= 30 + +camSurround = Opt.Surround(cam.position, spawnAtDistance/2)#, cam.orientation.to_euler()) +camSurroundCars = Opt.Surround(cam.position, spawnAtDistance)#, cam.orientation.to_euler()) + +inCinema = scene.objects["CinemaColider"]["InCinema"] + +#print(bge.logic.globalDict.get("reuse")) + +# Destroy those needing destroying +Reuse.SelfDestructDo() + +# Dani water boyancy +if dani["underwater"] and dani.position[2] < -9.5 and not dani["driving"]: + dani.applyForce([0,0,min(0.6, -0.5*(dani.position[2]+9.5))]) + scene.objects["DanisRig"]["underwater"] = True +elif dani.position[2] > -9.5: + dani["underwater"] = False + scene.objects["DanisRig"]["underwater"] = False + + + +# Available car to spawn +NPC_cars = ["NeonSpeedsterBox", + "RedKissBox", + "DarkShadowBox", + "HatchBack01-white", + "HatchBack01-orange", + "HatchBack01-blue", + "HatchBack01-black"] + +npccp = [1, + 1, + 1, + 10, + 8, + 5, + 3] + +NPC_cars_probability = [] +for i in npccp: + NPC_cars_probability.append( 1 / sum(npccp) * i) +#print(sum(NPC_cars_probability), NPC_cars_probability) + +# Storing some values to the globalDict +bge.logic.globalDict["NPC_cars"] = NPC_cars +bge.logic.globalDict["spawnAtDistance"] = spawnAtDistance +bge.logic.globalDict["maxCars"] = maxCars + + +def GenerateDoorObject(i): + # Location doesn't matter when you dealing with rotation + doorObject = {"ClosedRotX":i.get("ClosedRotX", i.orientation.to_euler()[0]), + "ClosedRotY":i.get("ClosedRotY", i.orientation.to_euler()[1]), + "ClosedRotZ":i.get("ClosedRotZ", i.orientation.to_euler()[2]), + + "OpenRotX":i.get("OpenRotX", i.orientation.to_euler()[0]), + "OpenRotY":i.get("OpenRotY", i.orientation.to_euler()[1]), + "OpenRotZ":i.get("OpenRotZ", i.orientation.to_euler()[2]), + + "ClosedLocX":i.get("ClosedLocX", i.position[0]), + "ClosedLocY":i.get("ClosedLocY", i.position[1]), + "ClosedLocZ":i.get("ClosedLocZ", i.position[2]), + + "door": i + } + # But rotation and location both matter when you dealing with location + move = [0,0,0] + moveback = [0,0,0] + + for naxis, axis in enumerate(["X", "Y", "Z"]): + code = "OpenLoc"+axis + move[naxis] = i.get(code, 0) + moveback[naxis] = -i.get(code, 0) + + i.applyMovement(move, True) + for naxis, axis in enumerate(["X", "Y", "Z"]): + code = "OpenLoc"+axis + doorObject[code] = i.position[naxis] + i.applyMovement(moveback, True) + + return doorObject + +def OpenCloseDoor(door, openClose): + + onTarget = [] + + for n, axis in enumerate(["X", "Y", "Z"]): + + targetRot = door[openClose+"Rot"+axis] + targetLoc = door[openClose+"Loc"+axis] + + dRot = door["door"].orientation.to_euler() + + onRotTarget = round(targetRot, 2) == round(dRot[n], 2) + onLocTraget = round(targetLoc, 1) == round(door["door"].position[n], 1) + + if not onRotTarget: + dRot[n] += (targetRot - dRot[n])/15 + + + else: + dRot[n] = targetRot + + door["door"].orientation = dRot + + if not onLocTraget: + + door["door"].position[n] += (targetLoc - door["door"].position[n])/15 + + + else: + door["door"].position[n] = targetLoc + + onTarget.append( onRotTarget and onLocTraget ) + + return all( onTarget ) + +# INITIALIZING + +# This runs only on the first frame + +if "spawns" not in bge.logic.globalDict: + bge.logic.globalDict["spawns"] = {} # Spawn points for cars + bge.logic.globalDict["light-spawns"] = {} # Spawn points for destructables ( such as lights ) + bge.logic.globalDict["allcars"] = [] # List of all spawned cars + bge.logic.globalDict["spawnedCarModels"] = [] # List of models of cars that were spawned already. + bge.logic.globalDict["all-lights"] = [] # List with all spawned lights + bge.logic.globalDict["sound-ambiances"] = [] # Points generating ambiant sounds + bge.logic.globalDict["elevators"] = {} # Handles Elevators + bge.logic.globalDict["doors"] = {} # Handles Doors + bge.logic.globalDict["trees"] = {"spawned":[], + "cu":1000 } # Handles Trees + + # Adding spawn points to the lists + for i in scene.objects: + if "spawn" in i: + + + lightSpawns = bge.logic.globalDict["light-spawns"] + objectData = {"position": i.worldPosition.copy(), + "orientation": i.worldOrientation.copy(), + "spawn": i.get("spawn"), + "to_spawn": i["to_spawn"], + "race":i.get("race"), + "npc":i.get("npc")} + + addr = Opt.Address(objectData["position"], spawnAtDistance) + if addr not in bge.logic.globalDict["spawns"]: + bge.logic.globalDict["spawns"][addr] = [] + + bge.logic.globalDict["spawns"][addr].append(objectData) + i.endObject() + + + + elif "Elevator" in i: + elevator = {"elevator":i, + "radius":i.get("Radius", 5), + "levels":[], + "doors":{}} + for level in range(10): + if "Level"+str(level) in i: + elevator["levels"].append(i["Level"+str(level)]) + + + for door in scene.objects: + if door.get("ElevatorDoor") == i["Elevator"]: + + + # Door's motion matrix + doorObject = GenerateDoorObject(door) + elevator["doors"][door["Level"]] = doorObject + + bge.logic.globalDict["elevators"][i["Elevator"]] = elevator + + elif "Door" in i: + + doorObject = GenerateDoorObject(i) + if i["Door"] not in bge.logic.globalDict["doors"]: + bge.logic.globalDict["doors"][i["Door"]] = {"doors":[], + "radius":3} + bge.logic.globalDict["doors"][i["Door"]]["doors"].append(doorObject) + if "Radius" in i: + bge.logic.globalDict["doors"][i["Door"]]["radius"] = i["Radius"] + bge.logic.globalDict["doors"][i["Door"]]["position"] = i.position.copy() + + + + + elif "LightSpawn" in i: + + lightSpawns = bge.logic.globalDict["light-spawns"] + objectData = {"position": i.worldPosition.copy(), + "orientation": i.worldOrientation.copy(), + "movez": i.get("movez", 0), + "to_spawn": i["to_spawn"], + "broken":False, + "spawned_object": i.get("spawned_object"), + "good": i.get("good"), + "LightSpawn": i["LightSpawn"]} + + addr = Opt.Address(objectData["position"], spawnAtDistance/2) + if addr not in lightSpawns: + lightSpawns[addr] = [] + + lightSpawns[addr].append(objectData) + i.endObject() + + elif "ambiance-sound" in i: + bge.logic.globalDict["sound-ambiances"].append(i) + + + ######## TREES ####### + + elif "Tree" in i.name: + tree_object = {"position":i.position.copy(), + "orientation":i.orientation.copy(), + "scaling":i.scaling.copy(), + "tree":"NormalTreeTrunk", + "treelow":"TreeLowres", + "treeverylow":"TreeBillboard", + "to_spawn":True} + i.endObject() + addr = Opt.Address(tree_object["position"], spawnAtDistance) + if addr not in bge.logic.globalDict["trees"]: + bge.logic.globalDict["trees"][addr] = [] + bge.logic.globalDict["trees"][addr].append(tree_object) + + + # Cheat code modes + + bge.logic.globalDict["pursuit-cheat"] = False + bge.logic.globalDict["derby-cheat"] = False + bge.logic.globalDict["gravity"] = True + + # Making races + + bge.logic.globalDict["races"] = {} # Object containing infor about races + + # Yes i'm using BPY, shut up! + for collection in bpy.data.collections['Races'].children: + + race = {"starters":[], + "checkpoints":[], + "racers":[], + "racer_spawns": [], + "started":False } + + for object in collection.objects: + tag = {"location":object.location, + "rotation":object.rotation_euler, + "radius":object.scale[0], + "OnLoop":object.get("OnLoop"), + "Uturn":object.get("Uturn"), + "cylinder":None} + + # Race starters ( the blue cylinder ) + + if object.name.startswith("Starter"): + race["starters"].append(tag) + race["laps"] = object["laps"] + + # Race checkpoints ( the yellow cylinder ) + + else: + race["checkpoints"].append(tag) + + bge.logic.globalDict["races"][collection.name] = race + + # Precalculating objects for smoothness + preData = {"MetalGate_good":50, + "GatePart":50, + "LightStand": 20, + "NormalTreeTrunk":20, + #"TreeLowres":8, + "TreeBillboard":30, + "House_Shelf":4, + "Moria's Bed":1, + "Moria's Bed.001":1, + "Sparkle": 100, + "Smoke": 20, + "LightStand.Borked.Head": 5, + "LightStand.Borked.Tail": 5, + "GatePart.Level0": 50, + "GatePart.Level1": 50, + "Fire": 200, + "Road Blocker": 12 + } + + print("Precalculating... ") + for n, object in enumerate(preData): + for i in range( preData[object] ): + Reuse.Create(object, selfDestructFrames = 1, selfDestructInactive = False ) + +# This is a good idea in theory ( so I'm keeping this code ). But... +# For some reason cars become way faster when I do this. Weird. + +# for carModel in NPC_cars: +# +# car = Reuse.Create(carModel, selfDestructFrames = 1 ) +# car["underwater"] = False +# rig = car.children[car.get("rig","RIG")] +# body = rig.children["Car_body"] +# body["good"] = body.meshes[0].name +# bge.logic.globalDict["spawnedCarModels"].append(carModel) + + +# SPAWNING CARS + +spawnedCarModels = bge.logic.globalDict["spawnedCarModels"] +spawnedCars = len( bge.logic.globalDict["allcars"] ) + +SpawnsCloseEnough = [] +for i in camSurroundCars: + SpawnsCloseEnough += bge.logic.globalDict["spawns"].get(i, []) + + +for spawnPoint in SpawnsCloseEnough: + + inview = cam.pointInsideFrustum(spawnPoint["position"]) == cam.INSIDE + distance = cam.getDistanceTo(spawnPoint["position"]) + + # Car information + carModel = spawnPoint["spawn"] + carBehaviour = spawnPoint.get("npc") + carRace = spawnPoint.get("race") + + # If this is a road npc, we want to choose a random car model + if carBehaviour == "npc": + + carModel = numpy.random.choice(NPC_cars, p=NPC_cars_probability) + toSpawn = spawnPoint["to_spawn"] and inview and spawnAtDistance / 2 < distance < spawnAtDistance and spawnedCars < maxCars + + else: + toSpawn = spawnPoint["to_spawn"] and inview and distance < spawnAtDistance + + # Trying not to add any more cars than nessesary + if toSpawn and carModel in Reuse.amounts and not Reuse.reuse.get(carModel): + + force_add = False + + # Making sure that the race still works + if carRace and not dani.get("race"): + for car in bge.logic.globalDict["allcars"]: + if car.name == carModel: + if not car.get("active"): + Reuse.Delete(car, inactive=True) + else: + force_add = True + break + if carRace: + force_add = True + + if not force_add: + continue + + + # Spawning a car + if goodFPS and toSpawn: + + # Making car + car, new = Reuse.Create(carModel, declarenew=True) + + # If you claimed the car, one more car! + if car["active"]: + car, new = Reuse.Create(carModel, declarenew=True) + + car.position = spawnPoint["position"] + car.orientation = spawnPoint["orientation"] + + if carBehaviour == "npc": + car.setLinearVelocity([0,-15,0], True) + else: + car["spawnPoint"] = spawnPoint + + car["npc"] = carBehaviour + car["enemy"] = "" + car["chased"] = False + car["launchtime"] = 300 + + if carRace: + + car["race"] = carRace + car["racing"] = False + car["checkpoint"] = 0 + car["rescue"] = bge.logic.globalDict["races"][carRace]["starters"][0]["location"] + car["lap"] = 0 + car["blown"] = False + bge.logic.globalDict["races"][carRace]["racers"].append(car) + bge.logic.globalDict["races"][carRace]["racer_spawns"].append(spawnPoint) + + # Cars are deformable during game, so we will need to restore them + rig = car.children[car.get("rig","RIG")] + body = rig.children["Car_body"] + + # Doors ( some cars have them falling ) + try: + rig.children["Door_Pease.001"].visible = True + rig.children["Door_Pease"].visible = True + except: + pass + + # If it's a fresh car + if new: + body["good"] = body.meshes[0].name + + # Making sure not to spawn too many cars. + spawnedCarModels.append(carModel) + + else: + + # Restoring car + body.replaceMesh( body["good"] ) + vehicle = bge.constraints.getVehicleConstraint(car["cid"]) + + # Restoring wheels + for i in range(4): + vehicle.setSuspensionStiffness(car.get("suspension", 200), i) + vehicle.setTyreFriction(car.get("friction", 1), i) + vehicle.setRollInfluence(car.get("roll", 0), i) + bge.logic.globalDict[car["cid"]]["wheels"][i].setVisible(True) + for i in ["Front_Right", "Front_Left", "Back_Left", "Back_Right"]: + car[i] = 1 + + + bge.logic.globalDict["allcars"].append(car) + + spawnPoint["to_spawn"] = False + spawnPoint["to_spawn_timer"] = 1000 + break + + # If player is standing still keep spawning cars near by + if not spawnPoint["to_spawn"] and spawnPoint["to_spawn_timer"]: + spawnPoint["to_spawn_timer"] -= 1 + elif not spawnPoint["to_spawn"] and carBehaviour == "npc": + spawnPoint["to_spawn"] = True + + +# LIGHTS AND OTHER DISTRACTABLES + +# Getting all the spawns within a certain distance +SpawnsCloseEnough = [] +for i in camSurround: + SpawnsCloseEnough += bge.logic.globalDict["light-spawns"].get(i, []) + +# Sorting them by distance +byDistance = [] +for n, i in enumerate(SpawnsCloseEnough): + distance = cam.getDistanceTo(i["position"]) + byDistance.append([distance, n, i]) +byDistance = sorted(byDistance) + +# Spawning the objects +for i in byDistance: + distance, n, i = i + + inmemory = Reuse.reuse.get(i["LightSpawn"]) + inview = cam.pointInsideFrustum(i["position"]) == cam.INSIDE + if goodFPS and inview and inmemory and i["to_spawn"] and not i["broken"]: + obj = Reuse.Create(i["LightSpawn"]) + obj.suspendDynamics(True) + obj.position = i["position"] + obj.position[2] += obj.get("movez", 0) + obj.orientation = i["orientation"] + obj["spawned_by"] = i + obj["despawn_damping"] = random.randint(100,200) + + bge.logic.globalDict["all-lights"].append(obj) + + if obj.get("good"): + obj.replaceMesh(obj["good"]) + + i["to_spawn"] = False + + +# Deleting them when they are far away +for obj in bge.logic.globalDict["all-lights"]: + + objAddress = Opt.Address(obj.position, spawnAtDistance/2) + inview = obj.getDistanceTo(cam) < 20 or cam.pointInsideFrustum(obj.position) == cam.INSIDE + + if objAddress not in camSurround or not inview: + + if not obj in Reuse.reuse.get(obj.name, []): + Reuse.Delete(obj) + + obj["spawned_by"]["to_spawn"] = True + bge.logic.globalDict["all-lights"].remove(obj) + break + +# Fixing broken ones when out of frame +lookingAt = random.choice(list(bge.logic.globalDict["light-spawns"].keys())) +if lookingAt not in camSurround: + listOfLights = bge.logic.globalDict["light-spawns"][lookingAt] + theLightToFix = random.choice(listOfLights) + if theLightToFix.get("broken"): + theLightToFix["broken"] = False + print("Fixed", theLightToFix["LightSpawn"], "at:", lookingAt) + + +##### TREES ###### + +# Making two pulls of points. +# One for where to spawn Trees. +# Other for where to take trees from. + +# Where to put trees +trees = bge.logic.globalDict["trees"] +PutTreesAt = [] +n = 0 +for addr in camSurroundCars: + for spawnPoint in bge.logic.globalDict["trees"].get(addr, []): + n += 1 + distance = cam.getDistanceTo(spawnPoint["position"]) + PutTreesAt.append([distance, n, spawnPoint]) +PutTreesAt = sorted(PutTreesAt) + +# Where to take trees +GetTreesFrom = [] +for n, tree in enumerate(bge.logic.globalDict["trees"]["spawned"]): + distance = cam.getDistanceTo(tree) + GetTreesFrom.append([distance, n, tree]) +GetTreesFrom = reversed(sorted(GetTreesFrom)) + +# Lets actually populate the trees +for spawnPoint in PutTreesAt: + + distance, n, spawnPoint = spawnPoint + + if not spawnPoint["to_spawn"]: + continue + inview = cam.pointInsideFrustum(spawnPoint["position"]) == cam.INSIDE + + tree = None + + if goodFPS and inview and Reuse.reuse.get(spawnPoint["treeverylow"]): + tree = Reuse.Create(spawnPoint["treeverylow"]) + tree.position = spawnPoint["position"] + tree.orientation = spawnPoint["orientation"] + tree.scaling = spawnPoint["scaling"] / 1.603 + + spawnPoint["to_spawn"] = False + tree["spawned_by"] = spawnPoint + tree["lod"] = 0 + bge.logic.globalDict["trees"]["spawned"].append(tree) + break + + elif goodFPS and inview: + for tree in GetTreesFrom: + treeDistance, treeN, tree = tree + treeInview = cam.pointInsideFrustum(tree.position) == cam.INSIDE or treeDistance > 100 + if ( treeDistance > distance or not treeInview ) and tree["spawned_by"]["treeverylow"] == spawnPoint["treeverylow"]: + + + bge.logic.globalDict["trees"]["spawned"].remove(tree) + tree["spawned_by"]["to_spawn"] = True + Reuse.Delete(tree, inactive=True) + tree = None + break + +# And low let's implement the lod system for trees. + +for tree in GetTreesFrom: + treeDistance, treeN, tree = tree + + inview = cam.pointInsideFrustum(tree.position) == cam.INSIDE + newtree = None + + # Hires + if goodFPS and treeDistance <= 50 and Reuse.reuse.get(tree["spawned_by"]["tree"]) and not tree["lod"] == 2 and inview: + newtree = Reuse.Create(tree["spawned_by"]["tree"]) + newtree["lod"] = 2 + + # Lowres + elif goodFPS and treeDistance > 50 and Reuse.reuse.get(tree["spawned_by"]["treeverylow"]) and not tree["lod"] == 0: + newtree = Reuse.Create(tree["spawned_by"]["treeverylow"]) + newtree["lod"] = 2 + + if goodFPS and newtree: + + newtree.position = tree.position + newtree.orientation = tree.orientation + newtree.scaling = tree.scaling + newtree["spawned_by"] = tree["spawned_by"] + #trees["keep"] = random.randint(0,20) + + Reuse.Delete(tree) + bge.logic.globalDict["trees"]["spawned"].append(newtree) + bge.logic.globalDict["trees"]["spawned"].remove(tree) + break + +#SpawnsCloseEnough = [] +#for i in camSurroundCars: +# SpawnsCloseEnough += bge.logic.globalDict["trees"].get(i, []) +# +#byDistance = [] +#for n, i in enumerate(SpawnsCloseEnough): +# distance = cam.getDistanceTo(i["position"]) +# byDistance.append([distance, n, i]) +#byDistance = sorted(byDistance) + +#bge.logic.globalDict["trees"]["cu"] = byDistance[-1][0] + +#for i in byDistance: +# +# distance, n, i = i + +# #addr = Opt.Address(i["position"], spawnAtDistance) +# inmemory = Reuse.reuse.get(i["treeverylow"]) +# +# inview = True#cam.pointInsideFrustum(i["position"]) == cam.INSIDE +# tree = None +# +# if inmemory and inview and i["to_spawn"]: +# +## if i.get("keep"): +## i["keep"] -= 1 +## continue +# +# tree = Reuse.Create(i["treeverylow"]) +# +# tree.position = i["position"] +# tree.orientation = i["orientation"] +# tree.scaling = i["scaling"] / 1.603 +# +# i["to_spawn"] = False +# +# tree["spawned_by"] = i +# tree["lod"] = 0 +# tree["keep"] = 20 +# +# bge.logic.globalDict["trees"]["spawned"].append(tree) +# +# elif not inmemory and distance < bge.logic.globalDict["trees"]["cu"]: +# bge.logic.globalDict["trees"]["cu"] = distance + +#byDistance = [] +#for n, tree in enumerate(bge.logic.globalDict["trees"]["spawned"]): +# +# distance = cam.getDistanceTo(tree.position) +# byDistance.append([distance, n, tree]) + +#byDistance = sorted(byDistance) + +#for tree in reversed(byDistance): +# +# +# distance, n, tree = tree +# +# addr = Opt.Address(tree.position, spawnAtDistance) +# inview = addr in camSurroundCars# and cam.pointInsideFrustum(i["position"]) == cam.INSIDE +# +# if not inview or distance > bge.logic.globalDict["trees"]["cu"]: +# +# if tree["keep"]: +# tree["keep"] -= 1 +# continue +# +# bge.logic.globalDict["trees"]["spawned"].remove(tree) +# Reuse.Delete(tree, inactive=True) +# tree["spawned_by"]["to_spawn"] = True +# tree["spawned_by"]["keep"] = 20 +# break +# +#for tree in byDistance: +# +# distance, n, tree = tree +# inview = cam.pointInsideFrustum(i["position"]) == cam.INSIDE +# +# if inview: +# +# tree["keep"] = 20 +# +# newtree = None +# +# if distance <= 70 and Reuse.reuse.get(tree["spawned_by"]["tree"]) and tree["lod"] != 2: +# +# newtree = Reuse.Create(tree["spawned_by"]["tree"]) +# newtree["lod"] = 2 +# +# +# elif distance > 70 and Reuse.reuse.get(tree["spawned_by"]["treeverylow"]) and tree["lod"] != 0: +# +# newtree = Reuse.Create(tree["spawned_by"]["treeverylow"]) +# newtree["lod"] = 0 +# +# +# if newtree: +# +# newtree.position = tree.position +# newtree.orientation = tree.orientation +# newtree.scaling = tree.scaling +# newtree["spawned_by"] = tree["spawned_by"] +# newtree["keep"] = 20 +# +# Reuse.Delete(tree) +# bge.logic.globalDict["trees"]["spawned"].append(newtree) +# bge.logic.globalDict["trees"]["spawned"].remove(tree) +# break +# + + +# ELEVATORS + +# Seeing on what level dani is. +def atLevel(object): + levelis = 0 + closest = 1000 + for n, i in enumerate(elevator["levels"]): + dist = object.position[2] - i + if dist < 0: dist *= -1 + + if dist < closest: + closest = dist + levelis = n + return levelis + +for elevatorname in bge.logic.globalDict["elevators"]: + + elevator = bge.logic.globalDict["elevators"][elevatorname] + + # Seeing if Dani is anywhere near an elevator + eloc = elevator["elevator"].position.copy() + eloc[2] = dani.position[2] + + if dani.getDistanceTo(eloc) < elevator["radius"]: + + + + # If dani not at the same level as the elevator + if atLevel(dani) != atLevel(elevator["elevator"]): + bge.logic.globalDict["print"] = "Press E to call Elevator." + + # Pressing E to call it + if 27 in bge.logic.globalDict["keys"]: + elevator["going_to"] = atLevel(dani) + elevator["closing_doors"] = True + + else: + bge.logic.globalDict["print"] = "Press 0-"+str(len(elevator["levels"])-1)+" activate the elevator." + + numbers = [13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] + for num in numbers: + if num in bge.logic.globalDict["keys"] and numbers.index(num) < len(elevator["levels"]): + elevator["going_to"] = numbers.index(num) + elevator["closing_doors"] = True + + # Elevator coming + if elevator.get("going_to") != None and not elevator.get("closing_doors"): + destination = elevator["levels"][elevator["going_to"]] + position = elevator["elevator"].position[2] + + if destination < position: + elevator["elevator"].position[2] -= 0.1 + else: + elevator["elevator"].position[2] += 0.1 + + if round(destination, 1) == round(position, 1): + elevator["going_to"] = None + elevator["closing_doors"] = True + + # Doors + if elevator.get("closing_doors"): + + cancelBucket = [] + + # If elevator is moving, all doors should be closed. + if elevator.get("going_to") != None: + openLevel = -1 + else: + openLevel = atLevel(elevator["elevator"]) + + for level in elevator["doors"]: + + door = elevator["doors"][level] + + # The elevator is at the current door's level, + # the door should be open. + if level == openLevel: + openClose = "Open" + else: + openClose = "Closed" + + onTarget = OpenCloseDoor(door, openClose) + + cancelBucket.append(onTarget) + + if all(cancelBucket): + elevator["closing_doors"] = False + +# DOOR ( in general ) + +for doorname in bge.logic.globalDict["doors"]: + + doors = bge.logic.globalDict["doors"][doorname] + + if doors.get("doOpenClose"): + cancelBucket = [] + for door in doors["doors"]: + onTarget = OpenCloseDoor(door, doors["OpenClosed"]) + cancelBucket.append(onTarget) + if all(cancelBucket): + doors["doOpenClose"] = False + + else: + + + + if doors.get("OpenClosed", "Closed") == "Closed" and dani.getDistanceTo(doors["position"]) < doors["radius"]: + + doors["OpenClosed"] = "Open" + doors["doOpenClose"] = True + + elif doors.get("OpenClosed") == "Open" and dani.getDistanceTo(doors["position"]) > doors["radius"]: + + doors["OpenClosed"] = "Closed" + doors["doOpenClose"] = True + + # Sound + if doors.get("doOpenClose"): + + device = bge.logic.globalDict["SoundDevice"] + + for n, door in enumerate(doors["doors"]): + + s = "//sfx/home_door.ogg" + code = str(n)+s + if code not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][code] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][code] + sound["play"] = device.play(sound["sound"]) + sound["play"].location = door["door"].position + sound["play"].velocity = door["door"].getLinearVelocity() + sound["play"].relative = False + sound["play"].distance_maximum = 100 + sound["play"].distance_reference = 1 + sound["play"].attenuation = 1 + sound["play"].pitch = random.uniform(1, 2) + sound["play"].volume = 0.3 + + +# WHEEL UP DOWN + +cam_parent = scene.objects["DaniCam_Parent"] + +wheelup = cont.sensors["wheel_up"] +wheeldown = cont.sensors["wheel_down"] +if wheelup.status: + cam_parent.scaling /= 1.1 +elif wheeldown.status and cam_parent.scaling[0] < 20: + cam_parent.scaling *= 1.1 + +# CHEAT CODES + +cheat_spawn = scene.objects["Dani_Cheater"] +keys = cheat_spawn.sensors["Keyboard"] +bge.logic.globalDict["keys"] = [] +for key in keys.events: + bge.logic.globalDict["keys"].append(key[0]) + +if keys.events: + + #print("Key pressed code:",keys.events) + + if "cheat" not in bge.logic.globalDict: + bge.logic.globalDict["cheat"] = [0] + + for i in keys.events: + if i[0] != bge.logic.globalDict["cheat"][-1]: + bge.logic.globalDict["cheat"].append(i[0]) + + # SPAWN CHEATS + + cheats = { + "NeonSpeedsterBox":[36, 27, 37, 36, 41, 38, 27, 26, 41, 42, 27, 40], + "RedKissBox":[40, 27, 26, 33, 31, 41], + "DarkShadowBox":[26, 23, 40, 33, 41, 30, 23, 26, 37, 45] + } + + for i in cheats: + code = bge.logic.globalDict["cheat"][-(len(cheats[i])):] + + if code == cheats[i]: + print("Spawning:", i) + + car = Reuse.Create(i) + car["npc"] = "" + car["race"] = "" + bge.logic.globalDict["allcars"].append(car) + car.position = cheat_spawn.position + car.orientation = cheat_spawn.orientation + car["underwater"] = False + rig = car.children[car.get("rig","RIG")] + body = rig.children["Car_body"] + body["good"] = body.meshes[0].name + bge.logic.globalDict["spawnedCarModels"].append(i) + + bge.logic.globalDict["cheat"] = [0] + + + bge.logic.globalDict["print"] = "A Vehicle Is Waiting For You, Sir." + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + teleportations = [ + [[-17.86, 899.2, 4.738], [40,23,25,27,42,40,23,25,33]], # Racetrack + [[-752.78, -937.4, 411.08], [30, 37, 43, 41, 27]], # House + [[-937.3, -277.1, 257.7], [35, 23, 36, 41, 31, 37, 36]],# Mansion + [[-2.122, 1322, 99.85],[42, 37, 45, 27, 40, 13]], # Tower0 + [[-172.4, 1147, 99.68],[42, 37, 45, 27, 40, 14]], # Tower1 + [[200, 1204, 100],[42, 37, 45, 27, 40, 15]], # Tower2 + [[12.19, 1127, 139.3],[42, 37, 45, 27, 40, 16]], # Tower3 + [[148.4, 941.7, 98.83],[42, 37, 45, 27, 40, 17]], # Tower4 + [[-725, -984.6, 409.2],[25, 31, 36, 27, 35, 23]] # Cinema + ] + + for i in teleportations: + code = bge.logic.globalDict["cheat"][-(len(i[1])):] + + if code == i[1]: + dani.position = i[0] + bge.logic.globalDict["cheat"] = [0] + + bge.logic.globalDict["print"] = "Teleportation Finished, Sir." + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + # Puruit code + code = bge.logic.globalDict["cheat"][-(len("pursuit")):] + if code == [38, 43, 40, 41, 43, 31, 42]: + bge.logic.globalDict["pursuit-cheat"] = not bge.logic.globalDict["pursuit-cheat"] + bge.logic.globalDict["cheat"] = [0] + + if bge.logic.globalDict["pursuit-cheat"]: + bge.logic.globalDict["print"] = "Everybody Hates You, Sir. At Your Request, Sir." + else: + bge.logic.globalDict["print"] = "Everybody Stopped Hating You, Sir." + + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + # Derby code + code = bge.logic.globalDict["cheat"][-(len("derby")):] + if code == [26, 27, 40, 24, 47]: + bge.logic.globalDict["derby-cheat"] = not bge.logic.globalDict["derby-cheat"] + bge.logic.globalDict["cheat"] = [0] + + if bge.logic.globalDict["derby-cheat"]: + bge.logic.globalDict["print"] = "Everybody Is Crazy, Sir." + else: + bge.logic.globalDict["print"] = "Everybody Stopped Being Crazy, Sir." + + + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + + + # Gravity code + code = bge.logic.globalDict["cheat"][-(len("gravity")):] + if code == [29, 40, 23, 44, 31, 42, 47]: + bge.logic.globalDict["gravity"] = not bge.logic.globalDict["gravity"] + if bge.logic.globalDict["gravity"]: + bge.constraints.setGravity(0, 0, -9.8) + else: + bge.constraints.setGravity(0, 0, 0) + + bge.logic.globalDict["cheat"] = [0] + + if bge.logic.globalDict["gravity"]: + bge.logic.globalDict["print"] = "We're Back To Earth, Sir." + else: + bge.logic.globalDict["print"] = "We're Up In Space, Sir." + + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + # Explosion code + code = bge.logic.globalDict["cheat"][-(len("bom")):] + if code == [24,37,35]: + expos = cheat_spawn.position.copy() + expos[2] = dani.position[2] + Distraction.Explosion(expos, mass=1, size=2) + + bge.logic.globalDict["print"] = "Boom! , Sir." + bge.logic.globalDict["cheat"] = [0] + + # no ding here, the boom is enough + +# RACES +for racename in bge.logic.globalDict["races"]: + race = bge.logic.globalDict["races"][racename] + + pointer = scene.objects["PointingArrow"] + posindicator = scene.objects["Dani_Pos_Indicator"] + timeindicator = scene.objects["Dani_Time_Indicator"] + lapindicator = scene.objects["Dani_Lap_Indicator"] + + if not race["started"] and not dani.get("race") and race["racers"]: + + for starter in race["starters"]: + + if not starter["cylinder"] and dani.getDistanceTo(starter["location"]) < spawnAtDistance: + starter["cylinder"] = Reuse.Create("Starter.Cylinder") + starter["cylinder"].position = starter["location"] + starter["cylinder"].orientation = starter["rotation"] + starter["cylinder"].scaling[0] = starter["radius"] + starter["cylinder"].scaling[1] = starter["radius"] + + if dani.getDistanceTo(starter["location"]) < starter["radius"]: + if dani["driving"]: + bge.logic.globalDict["print"] = "Press R to start the race." + + if keys.events and 40 in keys.events[0]: + + race["started"] = True + Reuse.Delete(starter["cylinder"]) + starter["cylinder"] = None + + race["start-time"] = bge.logic.getRealTime() + + for racer in race["racers"]: + racer["racing"] = True + racer["launchtime"] = 100 + + # Testing + racer["npc"] = "racer" + #racer["active"] = False + + # Beam of energy + racer["beam"] = scene.addObject("Racer.Indicator", "Racer.Indicator") + + dani["race"] = racename + dani["checkpoint"] = 0 + dani["lap"] = 0 + + + posindicator.visible = True + pointer.visible = True + timeindicator.visible = True + lapindicator.visible = True + + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["active"]["sound"]) # Play a Ding + + else: + bge.logic.globalDict["print"] = "Come here with a car to race." + + + # If this race is going + elif dani.get("race") == racename: + nextcheck = race["checkpoints"][dani["checkpoint"]] + + # Alight the arrow to the checkpoint + tocheck = pointer.getVectTo(nextcheck["location"]) + pointer.alignAxisToVect(tocheck[1], 1, 0.1) + + # Alighn the arrow to the next checkpoint if close enough to it. + if cam.pointInsideFrustum(nextcheck["location"]) == cam.INSIDE: + nextcheckpoint = race["checkpoints"][ ( dani.get("checkpoint") + 1 ) % len(race["checkpoints"]) ] + tonextcheck = pointer.getVectTo(nextcheckpoint["location"]) + pointer.alignAxisToVect(tonextcheck[1], 1, min(1, max(0, 1-(tocheck[0]/500))) ** 5 ) + + # Align the arrow vertically + pointer.alignAxisToVect( (0,0,1), 2, 1.0 ) + + # Time of the race + currentRaceTime = bge.logic.getRealTime() - race["start-time"] + formattedTime = str(datetime.timedelta(seconds=currentRaceTime))[:-4] + while formattedTime.startswith("0") or formattedTime.startswith(":"): + formattedTime = formattedTime[1:] + if formattedTime.startswith("."): + formattedTime = "0"+formattedTime + + timeindicator["Text"] = formattedTime + lapindicator["Text"] = str(dani["lap"]+1)+"/"+str(race["laps"]) + + if not nextcheck["cylinder"] and dani.getDistanceTo(nextcheck["location"]) < spawnAtDistance: + nextcheck["cylinder"] = Reuse.Create("Tag.Cylinder") + nextcheck["cylinder"].position = nextcheck["location"] + nextcheck["cylinder"].orientation = nextcheck["rotation"] + nextcheck["cylinder"].scaling[0] = nextcheck["radius"] * 0.8 + nextcheck["cylinder"].scaling[1] = nextcheck["radius"] * 0.8 + + if dani.getDistanceTo(nextcheck["location"]) < nextcheck["radius"]: + + if not nextcheck.get("OnLoop"): + dani["prevrescue"] = dani.get("rescue") + dani["rescue"] = nextcheck["location"] + dani["rescueTo"] = dani["checkpoint"] + + Reuse.Delete(nextcheck["cylinder"]) + nextcheck["cylinder"] = None + + dani["checkpoint"] += 1 + bge.logic.globalDict["SoundDevice"].play(bge.logic.globalDict["sounds"]["checkpoint"]["sound"]) # Play a Ding + + if dani["checkpoint"] == len(race["checkpoints"]): + dani["checkpoint"] = 0 + dani["lap"] += 1 + + # If finished race + if dani["lap"] == race["laps"]:# or not dani["driving"]: + dani["race"] = None + race["started"] = False + + posindicator.visible = False + timeindicator.visible = False + pointer.visible = False + lapindicator.visible = False + + + race["racer_spawns"] = [] + + for racer in race["racers"]: + racer["npc"] = "npc" + racer["beam"].endObject() + + race["racers"] = [] + + if nextcheck["cylinder"]: + Reuse.Delete(nextcheck["cylinder"]) + nextcheck["cylinder"] = None + + # If won + if race["positions"].index(dani) == 0: + bge.logic.globalDict["print"] = "You've Won This Race! Time: "+formattedTime + # If finished + else: + bge.logic.globalDict["print"] = "You finished number "+str(race["positions"].index(dani)+1) + " Time: "+formattedTime + + + positions = [] + for racer in race["racers"]: + racer["into_race"] = [race["laps"] - racer["lap"], len(race["checkpoints"]) - racer["checkpoint"], racer.getDistanceTo(race["checkpoints"][racer["checkpoint"]]["location"])] + positions.append([racer["into_race"], racer]) + + dani["into_race"] = [race["laps"] - dani["lap"], len(race["checkpoints"]) - dani["checkpoint"], dani.getDistanceTo(race["checkpoints"][dani["checkpoint"]]["location"])] + positions.append([dani["into_race"], dani]) + + positions = sorted(positions) + race["positions"] = [] + for position, racer in positions: + race["positions"].append(racer) + + posindicator["Text"] = str(race["positions"].index(dani)+1) + + + +# Messages + +if bge.logic.globalDict.get("print") and bge.logic.globalDict.get("last_print") != bge.logic.globalDict["print"]: + print("Message:", bge.logic.globalDict["print"]) + scene.objects["Messages"]["Text"] = str(bge.logic.globalDict.get("print")) + scene.objects["Messages"].playAction("MessagesAction", 0, 100) + bge.logic.globalDict["last_print"] = bge.logic.globalDict["print"] + bge.logic.globalDict["print"] = "" + + +# SOUND + +#print(dir(aud)) + +if "SoundDevice" not in bge.logic.globalDict: + bge.logic.globalDict["SoundDevice"] = aud.Device() + bge.logic.globalDict["SoundDevice"].distance_model = aud.DISTANCE_MODEL_INVERSE_CLAMPED + bge.logic.globalDict["sounds"] = {} + + bge.logic.globalDict["sounds"]["active"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/active.ogg")), + "play":None} + + # Angry pursuit sounds + + bge.logic.globalDict["sounds_angry_start"] = [] + + for i in range(8): + i += 1 + i = str(i) + if len(i) < 2: + i = "0"+i + i = "//sfx/voices/angry_pursuit/start_"+i+".ogg" + bge.logic.globalDict["sounds"][i] = {"sound":aud.Sound(bge.logic.expandPath(i)), + "play":None} + bge.logic.globalDict["sounds_angry_start"].append(i) + + + bge.logic.globalDict["sounds_angry_hit"] = [] + + for i in range(5): + i += 1 + i = str(i) + if len(i) < 2: + i = "0"+i + i = "//sfx/voices/angry_pursuit/hit_"+i+".ogg" + bge.logic.globalDict["sounds"][i] = {"sound":aud.Sound(bge.logic.expandPath(i)), + "play":None} + bge.logic.globalDict["sounds_angry_hit"].append(i) + + # Dani voices + + bge.logic.globalDict["sounds"]["dani_yelling"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/yelling.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_entering"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/entering.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_ohno"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohno.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_ohnoindeed"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohnoindeed.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_wearesinking"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/wearesinking.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["dani_ohgod"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/voices/dani/ohgod.ogg")), + "play":None} + + + # Car Sounds + + bge.logic.globalDict["sounds"]["door"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/door.ogg")), + "play":None} + bge.logic.globalDict["sounds"]["tire_pop"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/tire_pop.ogg")), + "play":None} + + # Race sounds + + bge.logic.globalDict["sounds"]["checkpoint"] = {"sound":aud.Sound(bge.logic.expandPath("//sfx/checkpoint.ogg")), + "play":None} + + +device = bge.logic.globalDict["SoundDevice"] +device.listener_location = scene.active_camera.worldPosition +device.listener_orientation = scene.active_camera.worldOrientation.to_quaternion() +device.listener_velocity = scene.active_camera.getLinearVelocity() + +for i in bge.logic.globalDict["sound-ambiances"]: + s = i["ambiance-sound"] + #s = "//sfx/ambiance/forest.ogg" + if s not in bge.logic.globalDict["sounds"]: + bge.logic.globalDict["sounds"][s] = {"sound":aud.Sound(bge.logic.expandPath(s)), + "play":None} + + sound = bge.logic.globalDict["sounds"][s] + + if not sound["play"] or not sound["play"].status: + sound["play"] = device.play(sound["sound"]) + + scale = i.scaling[0] + distance = i.getDistanceTo(dani) + + if inCinema: + sound["inCinema"] = sound.get("inCinema", 1) * 0.95 + elif sound.get("inCinema", 1) < 1: + if sound["inCinema"] < 0.1: + sound["inCinema"] = 0.1 + sound["inCinema"] = sound.get("inCinema", 1) * 1.05 + sound["play"].volume = max(0, min(1, (1-(distance / scale ))*5))*i.get("volume", 1) * sound.get("inCinema", 1) + +########## LODS FOR CHUNKS OF THE ROAD ############# +if "LODchunks" not in bge.logic.globalDict: + bge.logic.globalDict["LODchunks"] = {"TheRacetrack":{ + "object":scene.objects["TheRacetrack"], + "high":"TheRacetrackHigh", + "low":"TheRacetrackLow", + "radius":1000, + "now":"high" + }, + + "TheCity":{ + "object":scene.objects["TheCity"], + "high":"TheCityHigh", + "low":"TheCityLow", + "radius":1500, + "now":"high" + }, + + "TheHouse":{ + "object":scene.objects["TheHouse"], + "high":"TheHouseGood", + "low":"TheHouseBorked", + "radius":200, + "now":"high" + }, + + + } + +chunks = bge.logic.globalDict["LODchunks"] + +for chunkname in chunks: + chunk = chunks[chunkname] + + if chunk["now"] == "low" and cam.getDistanceTo(chunk["object"]) < chunk["radius"]: + print("Making", chunkname, "high") + chunk["object"].replaceMesh(chunk["high"]) + chunk["object"].restorePhysics() + chunk["now"] = "high" + + elif chunk["now"] == "high" and cam.getDistanceTo(chunk["object"]) > chunk["radius"]: + print("Making", chunkname, "low") + chunk["object"].replaceMesh(chunk["low"]) + if not dani.get("race"): + chunk["object"].suspendPhysics() + chunk["now"] = "low" + diff --git a/racedata/Race_BigBuilding/data_01.json b/racedata/Race_BigBuilding/data_01.json new file mode 100644 index 0000000..116e2e7 --- /dev/null +++ b/racedata/Race_BigBuilding/data_01.json @@ -0,0 +1,79 @@ +{ +"speed": [ +19.47047233581543 , +28.695953369140625 , +34.272308349609375 , +22.839120864868164 , +35.49073028564453 , +36.18384552001953 , +26.405841827392578 , +36.80004119873047 , +24.694549560546875 , +32.90505599975586 , +25.253585815429688 , +23.290348052978516 , +20.765544891357422 , +29.06432342529297 , +19.201831817626953 , +48.21841812133789 , +36.53797149658203 , +17.104976654052734 , +32.42231750488281 , +29.689577102661133 , +39.140628814697266 , +46.942405700683594 , +52.550262451171875 , +61.05805206298828 +], +"time":[ +3.5653900100000016 , +6.781268500000003 , +8.587536806000003 , +14.5922984 , +18.244144163000005 , +22.066794674000004 , +25.262257287 , +32.317976699 , +35.964942449000006 , +44.987977310000005 , +51.12034587500001 , +56.99959093000001 , +60.769708648000005 , +69.73444119000001 , +71.18043171900001 , +79.220370138 , +81.71922719900002 , +85.00884944 , +88.32716642400001 , +92.00067669300002 , +94.979335297 , +98.934810776 , +101.61330894700001 , +104.59028440000002 +], +"rot":[ +[-0.009425356984138489, -0.0032177797984331846, 2.6645872592926025] , +[0.4339008331298828, 0.06504074484109879, 2.824570417404175] , +[0.5393848419189453, -0.1043122187256813, 3.1088130474090576] , +[-0.07354794442653656, -0.5006471872329712, -1.6698423624038696] , +[-0.20410318672657013, -0.5391314029693604, -1.4937175512313843] , +[-0.3626016676425934, -0.5246425271034241, -1.0846848487854004] , +[-0.4815000891685486, -0.21434171497821808, -0.8064825534820557] , +[0.013308639638125896, -0.15695986151695251, -0.5021470785140991] , +[-0.6486340165138245, -0.5712481737136841, -0.900336503982544] , +[-0.09834565967321396, -0.2213810682296753, -1.711607575416565] , +[-0.4896208941936493, 0.010391822084784508, -1.1123923063278198] , +[-0.10912516713142395, -0.20200513303279877, -0.6804932355880737] , +[-0.3823515474796295, 0.15957728028297424, -0.527664303779602] , +[0.09372024238109589, -0.017329612746834755, 0.5058645009994507] , +[-0.030249271541833878, 0.26276054978370667, 0.06079315021634102] , +[-0.1713239848613739, -0.002728884806856513, 1.0983858108520508] , +[-0.005801045801490545, -0.02022010087966919, 0.7507718205451965] , +[-0.4120676517486572, 0.6411003470420837, 0.5670502781867981] , +[0.46784350275993347, 0.46227192878723145, 1.715516448020935] , +[0.5550453662872314, 0.3693121075630188, 2.2164559364318848] , +[0.3147442042827606, 0.16243435442447662, 2.825946092605591] , +[0.004009793978184462, -0.004397588782012463, 2.702540397644043] , +[0.28857335448265076, 0.1048942431807518, 2.883606433868408] , +[0.3831179738044739, 0.08331694453954697, 2.975795269012451] +]} diff --git a/racedata/Race_Pito/data-01.json b/racedata/Race_Pito/data-01.json new file mode 100644 index 0000000..7013fe9 --- /dev/null +++ b/racedata/Race_Pito/data-01.json @@ -0,0 +1,76 @@ +{ +"speed": [ +26.358402252197266 , +21.851234436035156 , +22.34600830078125 , +35.7989501953125 , +26.217530250549316 , +26.85378074645996 , +33.8651123046875 , +23.299406051635742 , +22.026166915893555 , +23.528913497924805 , +26.148305892944336 , +30.280473709106445 , +23.370298385620117 , +28.574520111083984 , +29.749557495117188 , +19.382225036621094 , +36.2252197265625 , +43.09573745727539 , +46.76814270019531 , +47.48198699951172 , +50.24925994873047 , +52.846805572509766 , +30.805631637573242 +], +"time":[ +170.65272959100002 , +172.79986740700002 , +175.59592075900002 , +178.357206599 , +182.51325741 , +186.291296247 , +189.72518670600004 , +192.36938303800002 , +198.250241647 , +201.33201389100003 , +205.44794146200002 , +209.12922035900004 , +213.11044018900003 , +219.506708898 , +221.33645230900004 , +225.15496903500002 , +233.56188715400003 , +235.41891638500005 , +237.67381141600004 , +240.81052518 , +243.59788943300003 , +246.41590875000003 , +248.44874652100003 +], +"rot":[ +[-0.015924813225865364, 0.0011410210281610489, -0.4768390655517578] , +[-0.010619524866342545, 0.006157155614346266, 0.819098711013794] , +[-0.01246673334389925, 0.002614065306261182, 2.2400927543640137] , +[-0.04152778163552284, -0.004130885470658541, 1.943183422088623] , +[-0.04403195157647133, 0.08712327480316162, 2.8876852989196777] , +[-0.029080595821142197, 0.13835664093494415, 2.829178810119629] , +[0.08981640636920929, 0.15955263376235962, -2.9285950660705566] , +[0.24162724614143372, -0.06017084792256355, 2.941547155380249] , +[0.23552058637142181, -0.007189183495938778, 3.119412422180176] , +[0.21812723577022552, -0.037734098732471466, 2.6612043380737305] , +[-0.11147090047597885, -0.17597346007823944, 2.991697311401367] , +[0.6546691656112671, -0.054870348423719406, -2.769106388092041] , +[0.18609264492988586, -0.4377727508544922, -1.875230312347412] , +[0.013437599875032902, -0.023461028933525085, -1.3867586851119995] , +[0.13140811026096344, -0.08332643657922745, -1.9279202222824097] , +[0.15664435923099518, 0.06593117117881775, -2.0268445014953613] , +[-0.28711217641830444, 0.015364741906523705, 0.31375741958618164] , +[-0.32474762201309204, -0.002328319475054741, 0.24374638497829437] , +[-0.32376036047935486, -0.00781808141618967, 0.22786971926689148] , +[-0.20297476649284363, 0.003622535616159439, 0.2503244876861572] , +[-0.15761759877204895, -0.020702043548226357, 0.1932581514120102] , +[-0.15772217512130737, 0.0015355173964053392, 0.25058653950691223] , +[-0.13799037039279938, -0.017578350380063057, 0.1344577968120575] +]} diff --git a/racedata/Race_Test/stupid-01.json b/racedata/Race_Test/stupid-01.json new file mode 100644 index 0000000..70f5210 --- /dev/null +++ b/racedata/Race_Test/stupid-01.json @@ -0,0 +1,160 @@ +{ +"speed": [ +24.627269744873047 , +47.092750549316406 , +42.61936950683594 , +37.54506301879883 , +58.613258361816406 , +68.0971450805664 , +31.452539443969727 , +43.356483459472656 , +59.80781936645508 , +47.419002532958984 , +55.74909591674805 , +58.8038330078125 , +46.581756591796875 , +30.18224334716797 , +53.74342346191406 , +61.3345947265625 , +61.86671447753906 , +21.49956512451172 , +26.613754272460938 , +37.49406051635742 , +48.516029357910156 , +55.17901611328125 , +37.53427505493164 , +17.593271255493164 , +43.89397430419922 , +54.7431640625 , +18.800701141357422 , +34.03715896606445 , +29.884437561035156 , +35.209720611572266 , +44.17673873901367 , +42.36854934692383 , +23.197813034057617 , +27.257816314697266 , +47.857093811035156 , +54.78614807128906 , +64.80278778076172 , +48.65629577636719 , +60.77760696411133 , +21.162349700927734 , +85.32059478759766 , +63.71363830566406 , +45.5229377746582 , +37.31787872314453 , +40.94782257080078 , +15.69291877746582 , +52.7214241027832 , +36.573299407958984 , +13.764350891113281 , +32.6483268737793 , +49.86388397216797 +], +"time":[ +3.0355660160000006 , +7.50188172 , +11.229805571 , +15.979854163999999 , +19.161029008000003 , +22.195561370000004 , +29.374649738 , +31.788481549 , +36.221103097 , +38.890305227 , +45.323665559000005 , +47.416093097 , +52.92501264500001 , +54.66389584500001 , +61.283976491000004 , +63.98905024800001 , +65.123911546 , +67.615446332 , +71.97461295000001 , +74.62498643200001 , +77.40852643500001 , +81.90872209800001 , +85.40049763500001 , +87.12826983900001 , +93.41054448000001 , +96.651941848 , +99.944720712 , +102.761049466 , +105.41555318300001 , +107.57172573900002 , +115.534601545 , +118.178148603 , +119.04205402700002 , +124.21186123600002 , +127.666679868 , +131.274512638 , +133.828632973 , +136.792306179 , +139.015821643 , +142.868449142 , +149.555674804 , +151.043801314 , +156.667236127 , +163.169707616 , +166.0047752 , +170.733802444 , +175.361103648 , +176.966964908 , +178.758813312 , +181.299940905 , +184.953017273 +], +"rot":[ +[-0.05026862025260925, 0.08184890449047089, -0.8148138523101807] , +[-0.1316002756357193, 0.0031764789018779993, -1.9675085544586182] , +[0.26708489656448364, -0.06156376749277115, -2.7510504722595215] , +[0.3162369728088379, 0.09000321477651596, 1.9450427293777466] , +[0.3255309760570526, -0.010332695208489895, 1.873444676399231] , +[0.2722633481025696, 0.024671392515301704, 1.7028207778930664] , +[-0.005381275434046984, 0.15349845588207245, 0.8245863318443298] , +[-0.011376403272151947, 0.014430257491767406, 0.8542606234550476] , +[-0.0803285464644432, 9.974920249078423e-05, 1.2288496494293213] , +[-0.07249549776315689, 0.08725707232952118, 1.8526216745376587] , +[-0.10431861132383347, 0.04276389628648758, 1.8115432262420654] , +[-0.062373124063014984, 0.2827167809009552, 1.517848253250122] , +[0.2936558127403259, 0.24947433173656464, 2.574117422103882] , +[0.02728387713432312, 0.14541742205619812, -2.128079414367676] , +[0.019615476951003075, 0.0018755028722807765, -1.6218808889389038] , +[-0.039600472897291183, -0.0001759183214744553, -1.4711010456085205] , +[-0.013717529363930225, -0.00015236262697726488, -1.4960139989852905] , +[0.010752955451607704, -0.011315465904772282, -1.9219878911972046] , +[-0.2489994913339615, -0.005062761250883341, 0.41395682096481323] , +[-0.1993929147720337, -0.015562037006020546, 0.3979560434818268] , +[-0.22047235071659088, -0.10357409715652466, 0.32793688774108887] , +[-0.19730879366397858, 0.002541373483836651, 0.19774995744228363] , +[-0.1718272715806961, 0.032392289489507675, 0.2755044996738434] , +[-0.13473595678806305, 0.05587274953722954, 1.063307285308838] , +[-0.060322586447000504, -0.002062979619950056, 1.757095217704773] , +[-0.059139054268598557, 0.010276955552399158, 1.913145661354065] , +[-0.04730139300227165, 0.3112684488296509, -2.8238513469696045] , +[-0.012114813551306725, 0.0024171662516891956, -2.4337821006774902] , +[0.09516211599111557, -0.005561040714383125, -1.452688217163086] , +[0.10693250596523285, -0.011977361515164375, -0.9787044525146484] , +[-0.16203682124614716, 0.012190017849206924, 0.3146638572216034] , +[-0.11242318898439407, -0.10967957228422165, -0.19336585700511932] , +[-0.01968350261449814, -0.15010792016983032, -0.9864364862442017] , +[-0.12329412251710892, 0.028628505766391754, -1.5601487159729004] , +[-0.17669115960597992, -0.0020655354019254446, -1.347058653831482] , +[-0.17644508183002472, 0.0009354822104796767, -1.3292815685272217] , +[-0.3563596308231354, -0.09675119072198868, -1.5348401069641113] , +[0.0772935226559639, -0.08689399063587189, -1.5763452053070068] , +[0.01565294712781906, -0.007086094468832016, -1.6168186664581299] , +[-0.4715840220451355, 0.7129260897636414, 2.9566893577575684] , +[-0.207715705037117, 0.027285262942314148, -1.6732585430145264] , +[-0.042232636362314224, -0.04647138714790344, -1.7285231351852417] , +[0.023538127541542053, 1.1733207702636719, -2.571376085281372] , +[0.14935921132564545, -0.056539710611104965, 2.851289749145508] , +[0.2860586941242218, -0.09459391236305237, 2.2907943725585938] , +[0.11912424117326736, 0.36828234791755676, 0.8598084449768066] , +[0.20029667019844055, 0.14303922653198242, 1.5025924444198608] , +[0.23549142479896545, -0.027062680572271347, 2.3216426372528076] , +[0.16652050614356995, -0.39845919609069824, -2.6487646102905273] , +[-0.021859584376215935, 0.005698161665350199, -2.2003278732299805] , +[-0.21812351047992706, 0.07756779342889786, -1.7219876050949097] +]} diff --git a/racedata/Race_ToRaceTrackAndBack/data-01.json b/racedata/Race_ToRaceTrackAndBack/data-01.json new file mode 100644 index 0000000..ce32423 --- /dev/null +++ b/racedata/Race_ToRaceTrackAndBack/data-01.json @@ -0,0 +1,175 @@ +{ +"speed": [ +11.520125389099121 , +35.4174919128418 , +48.963592529296875 , +59.18014907836914 , +62.10212326049805 , +48.34816360473633 , +29.57212257385254 , +32.26112747192383 , +34.97370147705078 , +18.7431640625 , +24.274316787719727 , +23.62272834777832 , +20.690147399902344 , +25.073972702026367 , +33.980159759521484 , +32.72504806518555 , +20.377763748168945 , +20.001949310302734 , +21.908119201660156 , +9.94851303100586 , +13.313704490661621 , +15.770674705505371 , +18.543357849121094 , +15.920366287231445 , +21.7354793548584 , +30.198137283325195 , +36.15170669555664 , +21.350290298461914 , +27.91997528076172 , +21.607690811157227 , +22.180803298950195 , +29.26111602783203 , +27.72776985168457 , +9.342388153076172 , +19.195575714111328 , +21.072982788085938 , +4.100090980529785 , +21.526302337646484 , +28.333513259887695 , +23.641836166381836 , +9.200776100158691 , +55.06287384033203 , +53.451881408691406 , +63.938533782958984 , +61.43254089355469 , +39.29024124145508 , +55.41264724731445 , +54.05284118652344 , +37.15116882324219 , +25.709457397460938 , +29.141069412231445 , +16.663942337036133 , +21.69783592224121 , +20.352880477905273 , +26.29122543334961 , +65.15868377685547 +], +"time":[ +6.4058701399999975 , +16.299862351999998 , +24.292330026000002 , +29.082968635999997 , +33.299860673 , +36.303023109 , +43.059633875 , +46.018999336 , +47.643505915 , +51.02223038100001 , +54.728302464 , +58.199815298 , +60.110697206000005 , +63.448575584000004 , +66.297977973 , +68.88603403900001 , +70.96493387199999 , +72.02714741100002 , +73.038448875 , +74.848823218 , +76.367119583 , +78.58938322899999 , +81.419440748 , +85.20895058300002 , +88.08085490100001 , +90.03782245400001 , +92.648943995 , +96.424005537 , +99.798790989 , +102.132583444 , +103.955840136 , +105.996664622 , +107.809086788 , +110.10640624500002 , +114.33213851900001 , +116.07253735200001 , +118.88573479600001 , +121.632792472 , +123.40774804900002 , +126.78710064 , +130.488984777 , +138.40776393500002 , +141.818720861 , +146.24281845500002 , +150.345210511 , +154.745981722 , +161.09281436 , +169.917108689 , +178.46524859400003 , +180.221853685 , +181.77062631 , +183.85376920400003 , +186.43360400000003 , +188.569479843 , +190.438454559 , +200.18231331700002 +], +"rot":[ +[0.322396457195282, 0.23881563544273376, -0.5920934677124023] , +[0.2871566116809845, -0.08072828501462936, -2.77008056640625] , +[0.022408029064536095, -0.01136639341711998, -3.1154706478118896] , +[0.05134904757142067, -0.004599934909492731, -3.070141553878784] , +[-0.09269172698259354, 0.0029444443061947823, -2.9999289512634277] , +[-0.15221868455410004, 0.024385621771216393, -3.086212635040283] , +[-0.10805381089448929, -0.04080692678689957, -2.940916061401367] , +[-0.009314289316534996, 0.011107872240245342, -2.244126081466675] , +[-0.00936639029532671, -0.01191023550927639, -2.363689422607422] , +[-0.011720740236341953, 0.004976195283234119, 2.495209217071533] , +[-0.007899435237050056, 0.01411510817706585, -1.7536320686340332] , +[-0.008956667967140675, -0.0006760396063327789, -0.9592467546463013] , +[-0.24615974724292755, -0.6175566911697388, -1.5662286281585693] , +[-0.01202403660863638, -0.0017966692103073, -1.8118407726287842] , +[-0.01174661424010992, -0.0033297506161034107, -2.4180521965026855] , +[-0.00354713830165565, -0.012529026716947556, -2.9205024242401123] , +[-0.101983942091465, -0.04468701034784317, 2.6437571048736572] , +[-0.0007664926815778017, -0.07988952100276947, 2.3414931297302246] , +[-0.007196569349616766, 0.006172348279505968, 2.4515678882598877] , +[-0.011907476000487804, 0.01922224648296833, -1.9352858066558838] , +[-0.011658741161227226, 0.0006468094070442021, -1.5991077423095703] , +[-0.0072335898876190186, 0.006306258495897055, -0.3650907576084137] , +[0.34733378887176514, 0.0029268087819218636, -0.2777894139289856] , +[-0.02114652842283249, -0.693382740020752, 0.04864123463630676] , +[0.019431520253419876, -0.331204891204834, 0.3845299780368805] , +[-0.24231138825416565, -0.3257531225681305, 0.7372809648513794] , +[-0.13575506210327148, -0.21794818341732025, 0.9611744284629822] , +[0.11747820675373077, -0.08134105801582336, 0.4339974522590637] , +[-0.013516846112906933, -0.06342270225286484, 1.280249834060669] , +[0.061240632086992264, -0.061734601855278015, 1.5704983472824097] , +[0.006887916009873152, -0.1518782526254654, 2.22128963470459] , +[-0.10946555435657501, -0.17215779423713684, 2.8074395656585693] , +[-0.14479641616344452, -0.13234378397464752, -3.1260485649108887] , +[-0.21056227385997772, 0.0864257887005806, -2.1548895835876465] , +[-0.015288061462342739, -0.006663475185632706, -1.644439458847046] , +[-0.003344947937875986, 0.005890160799026489, -1.9707906246185303] , +[-0.013974298723042011, 0.04181711748242378, 0.7127384543418884] , +[-0.011403351090848446, 0.004238484427332878, 0.35164594650268555] , +[-0.009488762356340885, 0.008791396394371986, 0.7719141840934753] , +[-0.6858975887298584, 1.0757564306259155, 0.4944709539413452] , +[-0.29866576194763184, 0.06245581433176994, 0.9689251184463501] , +[0.012169134803116322, -0.024358488619327545, 0.014703278429806232] , +[0.13266009092330933, -0.019439812749624252, 0.21082057058811188] , +[-0.0017350295092910528, -0.0012513829860836267, 0.08694247156381607] , +[-0.060520339757204056, 0.009871666319668293, 0.014981088228523731] , +[-0.10471435636281967, 0.02526852861046791, 0.15353718400001526] , +[-0.20200301706790924, 0.021692775189876556, 0.4099411070346832] , +[-0.2300856113433838, 0.0009850998176261783, 0.24298886954784393] , +[-0.1363544464111328, -0.015092594549059868, 0.1626790165901184] , +[-0.04628438130021095, -0.01569954678416252, -0.18837586045265198] , +[-0.004813441075384617, 0.019966699182987213, 0.0415644533932209] , +[-0.00748268561437726, 0.005573519971221685, 1.5236225128173828] , +[-0.013280867598950863, 0.030180254951119423, 2.7995290756225586] , +[-0.00792701356112957, 0.02658918872475624, -2.051511526107788] , +[-0.006730733904987574, -0.017240850254893303, -2.462642192840576] , +[0.03318160027265549, 0.007224104832857847, -2.8958749771118164] +]} diff --git a/racedata/RacetrackRace/data-01.json b/racedata/RacetrackRace/data-01.json new file mode 100644 index 0000000..56efe4d --- /dev/null +++ b/racedata/RacetrackRace/data-01.json @@ -0,0 +1,253 @@ +{ +"speed": [ +26.538028717041016 , +14.077311515808105 , +18.658981323242188 , +13.642276763916016 , +13.345431327819824 , +18.702871322631836 , +16.047119140625 , +18.768598556518555 , +12.635817527770996 , +15.992992401123047 , +20.55864906311035 , +18.69696617126465 , +23.134925842285156 , +21.005451202392578 , +10.778977394104004 , +15.51213264465332 , +12.688963890075684 , +19.68726348876953 , +21.714815139770508 , +20.48650360107422 , +44.844058990478516 , +29.13050651550293 , +14.36522388458252 , +26.632301330566406 , +21.141298294067383 , +28.36482810974121 , +34.80923080444336 , +41.07177734375 , +35.37945556640625 , +35.714088439941406 , +21.304424285888672 , +29.58685302734375 , +12.328302383422852 , +24.218772888183594 , +18.253358840942383 , +28.568607330322266 , +53.616416931152344 , +66.29619598388672 , +68.32941436767578 , +72.90721130371094 , +78.81604766845703 , +79.69103240966797 , +77.72826385498047 , +53.93451690673828 , +57.103511810302734 , +55.76216125488281 , +24.771047592163086 , +46.022056579589844 , +53.28487014770508 , +73.93230438232422 , +80.64896392822266 , +81.385009765625 , +80.61473846435547 , +79.13780212402344 , +56.18103790283203 , +52.9852180480957 , +49.222511291503906 , +34.73994064331055 , +33.99519729614258 , +33.589481353759766 , +32.65351867675781 , +33.09282684326172 , +34.04570007324219 , +35.97509002685547 , +47.60291290283203 , +68.90727996826172 , +49.79296875 , +33.00839614868164 , +24.07303810119629 , +27.293289184570312 , +34.35337829589844 , +38.572479248046875 , +30.301124572753906 , +23.644100189208984 , +39.042945861816406 , +31.498666763305664 , +23.266845703125 , +23.334239959716797 , +9.860187530517578 , +19.592100143432617 , +27.304677963256836 , +45.135345458984375 +], +"time":[ +8.116664030000003 , +9.965381487000002 , +13.246677532 , +14.635193845000003 , +18.792129416 , +21.382569770000003 , +22.860451079999997 , +26.539923041 , +28.774392659 , +31.181458413999998 , +33.712839218000006 , +35.605871269 , +38.995907190000004 , +40.104372484 , +45.975803135 , +48.299599242000006 , +48.585197645 , +51.896950127000004 , +58.52263849300001 , +59.939220689 , +64.93012263700001 , +69.88484050400001 , +72.34535318100001 , +76.931022993 , +79.300703666 , +82.545155674 , +84.551670075 , +87.96719661200001 , +90.768797852 , +93.62990334400001 , +96.68967967700002 , +107.201981458 , +110.06563018 , +114.26384199700001 , +117.520456249 , +119.38233081000001 , +125.19056378200003 , +126.396600135 , +127.25683364300002 , +128.485345913 , +129.53639668000002 , +130.48395716800002 , +131.650086046 , +132.98336374200002 , +134.556082002 , +136.05712032600002 , +138.024308608 , +141.316818229 , +143.777475325 , +145.001971864 , +145.92328762900001 , +146.834561406 , +147.62410069700002 , +148.593186454 , +149.83803384700002 , +151.67746488900002 , +154.264217849 , +156.53769745300002 , +158.90166785600002 , +161.404467962 , +164.01867503900002 , +166.28514362 , +168.43718329200001 , +170.485944127 , +174.516517082 , +176.295076886 , +177.32922254200002 , +178.51555399800003 , +184.21800381000003 , +189.73977150800002 , +191.77973041 , +194.137573862 , +195.90127665900002 , +198.52870619200002 , +203.85514616600003 , +208.135847073 , +210.16703229700002 , +212.60313926400002 , +214.693184015 , +216.738090768 , +218.09940265600002 , +222.297700631 +], +"rot":[ +[-0.011944389902055264, 0.006112223025411367, 1.700330376625061] , +[-0.007173628080636263, 0.02288164757192135, -2.852203845977783] , +[-0.0861905962228775, -0.10517077893018723, -1.669675588607788] , +[0.01138912420719862, -0.19917206466197968, 2.7695469856262207] , +[-0.018195543438196182, 0.08142709732055664, 1.8283754587173462] , +[-0.00950363464653492, 0.07866732031106949, 2.5361087322235107] , +[-0.0038584310095757246, 0.022968797013163567, -2.5849733352661133] , +[0.011713280342519283, 0.0001692562800599262, -2.090550184249878] , +[0.008348735049366951, 0.015365973114967346, -1.2970794439315796] , +[-0.010748106054961681, 0.00412603747099638, -0.19918647408485413] , +[-0.025385981425642967, -0.20469634234905243, 0.19799326360225677] , +[-0.051951754838228226, -0.19977568089962006, -0.884537935256958] , +[0.03312642127275467, 0.0024061871226876974, -1.6786614656448364] , +[-0.00650023203343153, -0.005300144664943218, -2.0751631259918213] , +[0.012969786301255226, -0.005354337394237518, 2.917091131210327] , +[0.006736284587532282, 0.006057392805814743, 2.079996109008789] , +[0.013036495074629784, 0.011535299010574818, 2.3155832290649414] , +[-0.011130016297101974, 0.0028641719836741686, -1.0052982568740845] , +[0.016862086951732635, -0.0015058484859764576, -0.09880482405424118] , +[-0.004691624082624912, 0.021046828478574753, 0.6781924962997437] , +[-0.017728032544255257, -0.0012465771287679672, 1.430189847946167] , +[0.09602674096822739, 0.2094225287437439, 1.5242966413497925] , +[0.005686495453119278, 0.21619883179664612, -2.740380048751831] , +[0.07098455727100372, 0.03149515017867088, -2.9034860134124756] , +[0.30910688638687134, -0.20950938761234283, 3.0955755710601807] , +[0.24025997519493103, 0.20947375893592834, -1.9201265573501587] , +[-0.017547672614455223, 0.3748147487640381, -1.0774811506271362] , +[0.0030184010975062847, 0.10431521385908127, -0.7535775899887085] , +[-0.003876808099448681, -0.10495706647634506, -2.004059314727783] , +[-0.2196682244539261, -0.007554696407169104, 3.1220242977142334] , +[-0.407574325799942, -0.22309058904647827, -3.1093294620513916] , +[-0.012773668393492699, -0.0005817283526994288, -2.7518677711486816] , +[-0.10252165049314499, -0.008252921514213085, -1.5677368640899658] , +[-0.23955923318862915, -0.09994788467884064, -1.1482408046722412] , +[-0.00963659305125475, 0.0071213580667972565, 0.3983650505542755] , +[-0.012236895971000195, -0.0007551054586656392, 0.528429388999939] , +[-0.011944236233830452, -0.0016313528176397085, 0.5354834198951721] , +[-0.41956332325935364, -0.010190033353865147, 0.5716493129730225] , +[-0.9236911535263062, -0.01155498344451189, 0.5711032748222351] , +[-1.7139198780059814, 0.0073111592791974545, 0.5829598307609558] , +[-2.394428014755249, 0.041423454880714417, 0.5533641576766968] , +[-2.999908208847046, 0.016444142907857895, 0.5245150327682495] , +[2.462455987930298, 0.007922210730612278, 0.5644753575325012] , +[1.7629728317260742, -0.0012621483765542507, 0.5560276508331299] , +[1.0346837043762207, -0.01906212419271469, 0.5698538422584534] , +[0.30741551518440247, -0.0173384640365839, 0.6008566617965698] , +[-0.0699830874800682, 0.6417710185050964, 1.5418297052383423] , +[-0.012416953220963478, -0.0019070307025685906, 1.586891531944275] , +[-0.02583491988480091, 0.0006539520109072328, 1.5676714181900024] , +[-0.9542934894561768, -0.03121805749833584, 1.5464407205581665] , +[-1.7998085021972656, 0.06815160065889359, 1.5507073402404785] , +[-2.6492412090301514, 0.026394544169306755, 1.5490610599517822] , +[2.914134979248047, -0.0038345723878592253, 1.5682618618011475] , +[1.9956632852554321, -0.1380651444196701, 1.521267056465149] , +[1.040777564048767, -0.0338020958006382, 1.5767815113067627] , +[-0.04618873447179794, -0.029588982462882996, 1.6126329898834229] , +[-0.03317926824092865, -0.052183810621500015, 1.629468321800232] , +[-0.06891582161188126, -0.07569558918476105, 2.3472342491149902] , +[-0.07659902423620224, -0.02957615628838539, 3.0788307189941406] , +[-0.07151692360639572, -0.03721490502357483, -2.4958367347717285] , +[-0.07168643176555634, -0.02208682894706726, -1.6576002836227417] , +[-0.07112537324428558, -0.02289627306163311, -1.0200819969177246] , +[-0.07321549206972122, 0.003364601405337453, -0.3980576694011688] , +[-0.020596753805875778, 0.018566880375146866, -0.037922296673059464] , +[-0.2480498105287552, -0.06663892418146133, -0.13459287583827972] , +[-1.3133546113967896, 0.008962471969425678, -0.31686049699783325] , +[-1.3110071420669556, 0.013296318240463734, -0.13220596313476562] , +[-1.3061256408691406, -0.1864778697490692, -0.18522538244724274] , +[1.292571783065796, -0.025291956961154938, 3.010225296020508] , +[1.1369845867156982, 0.02480003423988819, 3.01930570602417] , +[-0.011420882306993008, -0.014218311756849289, 3.0177178382873535] , +[0.019959215074777603, -0.00828701350837946, 3.049135684967041] , +[-0.0030481216963380575, 0.024305671453475952, -2.4306211471557617] , +[-0.007101575843989849, 0.007121120113879442, -1.5782235860824585] , +[0.12303955852985382, 0.024861041456460953, -1.0062673091888428] , +[0.009652684442698956, -0.01020199153572321, -1.0544575452804565] , +[-0.002112274756655097, 0.023144695907831192, -0.2179049253463745] , +[0.16161054372787476, -0.02564871497452259, 0.12074816226959229] , +[0.21369516849517822, 0.11316730082035065, 1.4975491762161255] , +[0.21483078598976135, 0.04324842244386673, 1.7023143768310547] , +[0.28562748432159424, -0.011749212630093098, 1.6832541227340698] , +[0.012233277782797813, -0.007409573532640934, 1.5701661109924316] +]} diff --git a/racedata/old-data-01.json b/racedata/old-data-01.json new file mode 100644 index 0000000..6ad29a0 --- /dev/null +++ b/racedata/old-data-01.json @@ -0,0 +1,160 @@ +{ +"speed": [ +34.53645706176758 , +42.18523406982422 , +39.258819580078125 , +36.71881866455078 , +58.93662643432617 , +72.29933166503906 , +45.77790069580078 , +41.69926071166992 , +42.62849426269531 , +43.45172119140625 , +51.235660552978516 , +31.478574752807617 , +39.46429443359375 , +28.128252029418945 , +57.83876419067383 , +62.50741958618164 , +58.854637145996094 , +22.849945068359375 , +26.428817749023438 , +36.63786697387695 , +47.69795227050781 , +54.362796783447266 , +41.81144714355469 , +14.943954467773438 , +46.46694564819336 , +39.81470489501953 , +39.50140380859375 , +37.47561264038086 , +15.179645538330078 , +20.929906845092773 , +45.30957794189453 , +23.44857406616211 , +23.066326141357422 , +25.70628547668457 , +47.98323059082031 , +54.75291061401367 , +58.678802490234375 , +44.3359489440918 , +50.71199417114258 , +61.02040481567383 , +46.14556884765625 , +36.1912727355957 , +45.200992584228516 , +26.560962677001953 , +33.57011795043945 , +25.442731857299805 , +45.04869842529297 , +21.671131134033203 , +23.820812225341797 , +28.113801956176758 , +55.13059616088867 +], +"time":[ +3.7331726409999995 , +10.839709940000002 , +15.604859501 , +21.232931573 , +25.423423467000003 , +29.299977412 , +38.75026077 , +41.63020803100001 , +47.96183718900001 , +52.53954244200001 , +58.496759698000005 , +62.853535308000005 , +69.863829096 , +71.044700398 , +78.77773761 , +81.53409601400001 , +82.65185832200001 , +85.32760139800001 , +89.074524456 , +92.084296941 , +95.26055315500001 , +100.65397920500001 , +104.88031547000001 , +106.758814842 , +113.99198440800001 , +117.720443904 , +120.68088727600002 , +123.16474497600001 , +125.38076530100003 , +129.732231237 , +137.45067690500002 , +141.032491157 , +142.436841254 , +144.848307046 , +148.58897748500002 , +152.46698020300002 , +155.38062296 , +158.850439759 , +161.84902360100003 , +166.33817853300002 , +171.558688668 , +174.706659002 , +177.52119836300002 , +181.63567185000002 , +184.77241402400003 , +190.474174792 , +194.84427218500002 , +197.623955681 , +200.56679723200003 , +202.875682436 , +206.77588530600002 +], +"rot":[ +[-0.01624995656311512, 0.07849447429180145, -0.5860120058059692] , +[-0.13108374178409576, 0.0036811053287237883, -1.9615410566329956] , +[0.2670449912548065, -0.042434122413396835, -2.7690749168395996] , +[0.33141058683395386, 0.0562259703874588, 2.039043664932251] , +[0.3329033851623535, -0.019622452557086945, 1.9211680889129639] , +[0.2681850492954254, -0.12282246351242065, 1.63251793384552] , +[0.07227663695812225, 0.013098720461130142, 1.353943943977356] , +[0.05529430881142616, -0.011405624449253082, 0.7894548773765564] , +[-0.06097666174173355, -0.009611496701836586, 1.1667016744613647] , +[-0.09110146760940552, -0.003160672029480338, 1.8139026165008545] , +[-0.07910168915987015, 0.052563153207302094, 1.7210103273391724] , +[-0.07669410854578018, 0.24394765496253967, 1.6646289825439453] , +[0.2888643443584442, 0.19726473093032837, 2.4627699851989746] , +[0.0036310763098299503, 0.050845541059970856, 3.0246827602386475] , +[0.02131607010960579, 0.003683441085740924, -1.6427737474441528] , +[-0.04108688235282898, 0.0028885663487017155, -1.4885928630828857] , +[-0.004137610550969839, 0.01848621480166912, -1.4435324668884277] , +[0.01292195450514555, 0.028108134865760803, -0.6352774500846863] , +[-0.2486754208803177, 0.012429589405655861, 0.4717749059200287] , +[-0.20020051300525665, -0.015403581783175468, 0.4037122428417206] , +[-0.2037292718887329, -0.06907863914966583, 0.35424113273620605] , +[-0.19704695045948029, 0.007243760861456394, 0.188624307513237] , +[-0.19659633934497833, 0.00966715905815363, 0.1812870055437088] , +[-0.14462502300739288, 0.11804241687059402, 0.643571138381958] , +[-0.05990368127822876, -0.0002554748207330704, 1.7723714113235474] , +[-0.060604702681303024, 0.003209835384041071, 1.830974817276001] , +[-0.008336379192769527, 0.010820192284882069, 2.8646183013916016] , +[-0.007981760427355766, 0.015732446685433388, -2.289222478866577] , +[0.05147334933280945, -0.0643678605556488, -0.5811130404472351] , +[0.11037056148052216, -0.0053720674477517605, -1.30815851688385] , +[-0.1621924191713333, 0.0018318506190553308, 0.25206780433654785] , +[-0.15930092334747314, -0.02305584028363228, 0.11540792882442474] , +[0.010223581455647945, 0.0012618519831448793, -0.2890264391899109] , +[-0.051978521049022675, 0.00850313063710928, -1.318840503692627] , +[-0.17644517123699188, -0.0011852768948301673, -1.3395663499832153] , +[-0.1761690080165863, -0.0021828312892466784, -1.3442251682281494] , +[-0.3152637481689453, -0.07436665892601013, -1.5566202402114868] , +[0.3682832717895508, 0.013875464908778667, -1.4670772552490234] , +[0.017779087647795677, -0.021842246875166893, -1.6439850330352783] , +[-0.1307658851146698, 0.0003210034337826073, -1.7973430156707764] , +[-0.18067330121994019, 0.0007494472083635628, -1.7816379070281982] , +[-0.07303235679864883, -0.004364720545709133, -2.101363182067871] , +[-0.09024622291326523, -0.0003755079524125904, -2.415090799331665] , +[0.1307559609413147, -0.0565742664039135, 3.039773464202881] , +[0.3108542561531067, -0.08981055021286011, 2.4484992027282715] , +[0.3178117573261261, 0.3399386405944824, 1.3576174974441528] , +[0.22571131587028503, 0.13748253881931305, 1.5181454420089722] , +[0.22656512260437012, 0.10890141874551773, 1.6666717529296875] , +[0.5554865002632141, 0.16415885090827942, 2.5439395904541016] , +[-0.0215617548674345, 0.008771177381277084, -2.2904481887817383] , +[-0.2129850834608078, 0.016720633953809738, -1.6787928342819214] +]} diff --git a/run.py b/run.py new file mode 100644 index 0000000..9ba909d --- /dev/null +++ b/run.py @@ -0,0 +1,667 @@ +# GPL3 or any later version +# (C) J.Y.Amihud ( blenderdumbass ) 2024 + +# This is the launcher of the game. + +import os +import subprocess +import time +import json +import threading +import gi +gi.require_version('Gtk', '3.0') +from gi.repository import Gtk +from gi.repository import GLib + + +from Scripts.Common import * + +#### MAKING THE WINDOW #### + +win = Gtk.Window() +win.set_title("Dani's Race") +win.set_size_request(799, 280) +win.connect("destroy", Gtk.main_quit) +win.set_default_icon_from_file("SettingUI/icon.png") +box = Gtk.VBox() +win.add(box) + +#### Building the UI itself ### + +banner = Gtk.Image.new_from_file('SettingUI/banner.jpg') +box.pack_start(banner, 0,0,0) + +playbox = Gtk.HBox() +box.pack_end(playbox, 1,5,5) + +def play(w): + + data = load_settings() + if data: + blenderplayer = data.get("blenderplayer") + + command = [blenderplayer] + + if data.get("fullscreen"): + command.append("-f") + else: + command.append("-w") + + + command.append(data.get("resolution").split(":")[0]) + command.append(data.get("resolution").split(":")[1]) + + # The file of the game + command.append(os.getcwd()+"/assembly.blend") + + # print(command) + subprocess.Popen(command) + + else: + set_settings(w) + +playbutton = Gtk.Button("Start Game") +playbutton.connect("clicked", play) +playbox.pack_end(playbutton, 0,5,5) + + +################### SETTINGS ######################### + +def set_settings(w): + + settingsWindow = Gtk.Dialog("Dani's Race Settings", + buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, + Gtk.STOCK_OK, Gtk.ResponseType.OK), + ) + + settingsWindow.set_size_request(400, 400) + + box = settingsWindow.get_content_area() + + # Scroller + scrl = Gtk.ScrolledWindow() + box.pack_start(scrl, 1,1,1) + + # Actuall useful box + box = Gtk.VBox() + scrl.add(box) + + # UPBGE executable + + upbgebox = Gtk.HBox() + upbgelabel = Gtk.Label("UPBGE executable: ") + upbgeinput = Gtk.Entry() + + upbgeinput.set_text("blenderplayer") + upbgeinput.set_tooltip_text("Location of the blenderplayer executable on the system ( should in UPBGE folder ).") + + upbgebox.pack_start(upbgelabel, 0,5,5) + upbgebox.pack_start(upbgeinput, 1,5,5) + + box.pack_start(upbgebox, 0,5,5) + + ###### GRAPHICS / COMPLEXITY ##### + + box.pack_start(Gtk.Label("Graphics"), 0,5,5) + + # Resolution + + resbox = Gtk.HBox() + resfull = Gtk.CheckButton("Fullscreen") + resfull.set_tooltip_text("Whether to run the game Fullscreen.") + + resmenu = Gtk.ComboBoxText() + resmenu.set_tooltip_text("What resolution to run the game at.") + + resolutions = [ + "7680:4320", + "5120:2880", + "3840:2160", + "2560:1440", + "1920:1080", + "1600:900", + "1366:768", + "1280:720", + "1024:576", + "960:540", + "854:480", + "640:360" + ] + + for i in resolutions: + resmenu.append_text(i) + resmenu.set_active(4) + + resbox.pack_start(resfull, 0,5,5) + resbox.pack_end(resmenu, 0,5,5) + + box.pack_start(resbox, 0,5,5) + + # Compositing + + compositor = Gtk.CheckButton("Post Processing Effects") + compositor.set_tooltip_text("Bloom, sun beams, film and color grading effects.") + compositor.set_active(True) + box.pack_start(compositor, 0,5,5) + + # Reflections + + reflections = Gtk.CheckButton("Screen-Space Reflections") + reflections.set_tooltip_text("Pseudo-accurate reflections.") + reflections.set_active(True) + box.pack_start(reflections, 0,5,5) + + # Trees, Poles, Fenses + + tplbox = Gtk.HBox() + + trees = Gtk.CheckButton("Trees") + trees.set_tooltip_text("Whether to spawn Trees.") + trees.set_active(True) + + poles = Gtk.CheckButton("Light Poles") + poles.set_tooltip_text("Whether to spawn Light Poles.") + poles.set_active(True) + + fences = Gtk.CheckButton("Fences") + fences.set_tooltip_text("Whether to spawn Metal and or Wooden fences.") + fences.set_active(True) + + tplbox.pack_start(trees, 1,5,5) + tplbox.pack_start(poles, 1,5,5) + tplbox.pack_start(fences, 1,5,5) + + box.pack_start(tplbox, 0,5,5) + + # Amount of cars + carsbox = Gtk.HBox() + carsbox.pack_start(Gtk.Label("Traffic Density"), 0,5,5) + maxcarsadjust = Gtk.Adjustment(4, lower=1, upper=20, step_increment=1) + maxcarsinput = Gtk.SpinButton(adjustment=maxcarsadjust, digits=0) + maxcarsinput.set_tooltip_text("How many NPC cars to spawn on roads.") + carsbox.pack_end(maxcarsinput, 0,5,5) + box.pack_start(carsbox, 0,5,5) + + + # Sampling + + samplesbox = Gtk.HBox() + sampleslabel = Gtk.Label("Main Samples: ") + samplesadjust = Gtk.Adjustment(1, lower=1, upper=64, step_increment=1) + samplesinput = Gtk.SpinButton(adjustment=samplesadjust, digits=0) + + samplesinput.set_sensitive(False) + + samplesinput.set_tooltip_text("How many times to render the image per frame. More gives more accurate soft shadows and anti-aliasing. Less is faster.") + + samplesbox.pack_start(sampleslabel, 0,5,5) + samplesbox.pack_end(samplesinput, 0,5,5) + + box.pack_start(samplesbox, 0,5,5) + + # Subsurface Sampling + + skinsamplesbox = Gtk.HBox() + skinsampleslabel = Gtk.Label("Skin Samples: ") + skinsamplesadjust = Gtk.Adjustment(5, lower=1, upper=64, step_increment=1) + skinsamplesinput = Gtk.SpinButton(adjustment=skinsamplesadjust, digits=0) + + skinsamplesinput.set_sensitive(False) + + skinsamplesinput.set_tooltip_text("How accurate the subsurface materials ( skin ) should be. More, prettier, less faster.") + + skinsamplesbox.pack_start(skinsampleslabel, 0,5,5) + skinsamplesbox.pack_end(skinsamplesinput, 0,5,5) + + box.pack_start(skinsamplesbox, 0,5,5) + + # Volumetric sampling + + box.pack_start(Gtk.HSeparator(), 0,5,5) ############################# + + volumesamplesbox = Gtk.HBox() + volumesampleslabel = Gtk.Label("Volumetric Samples: ") + volumesamplesadjust = Gtk.Adjustment(32, lower=1, upper=64, step_increment=1) + volumesamplesinput = Gtk.SpinButton(adjustment=volumesamplesadjust, digits=0) + + volumesamplesinput.set_tooltip_text("How accurate should be fog, smoke and fire effects. More prettier, less faster.") + + volumesamplesbox.pack_start(volumesampleslabel, 0,5,5) + volumesamplesbox.pack_end(volumesamplesinput, 0,5,5) + + box.pack_start(volumesamplesbox, 0,5,5) + + + # Volumetric lighting + + volumelight = Gtk.CheckButton("Volumetric Lighting") + volumelight.set_active(True) + volumelight.set_tooltip_text("Enable light to interact with volumetric materials.") + + box.pack_start(volumelight, 0,5,5) + + # Volumetric shadow + + def show_hide_volume_shadows(w): + volumeshadowsamplesbox.set_visible( w.get_active() ) + + volumeshadow = Gtk.CheckButton("Volumetric Shadows") + volumeshadow.set_active(False) + volumeshadow.set_tooltip_text("Enable volumetrics to cast shadows ( Very slow ).") + volumeshadow.connect("clicked", show_hide_volume_shadows) + + box.pack_start(volumeshadow, 0,5,5) + + # Volumetric shadow sampling + + volumeshadowsamplesbox = Gtk.HBox() + volumeshadowsampleslabel = Gtk.Label("Volumetric Shadows Samples: ") + volumeshadowsamplesadjust = Gtk.Adjustment(3, lower=1, upper=64, step_increment=1) + volumeshadowsamplesinput = Gtk.SpinButton(adjustment=volumeshadowsamplesadjust, digits=0) + + volumeshadowsamplesinput.set_tooltip_text("How accurate should be volumentric shadows. More prettier, less faster.") + + volumeshadowsamplesbox.pack_start(volumeshadowsampleslabel, 0,5,5) + volumeshadowsamplesbox.pack_end(volumeshadowsamplesinput, 0,5,5) + + box.pack_start(volumeshadowsamplesbox, 0,5,5) + + # Shadows + + box.pack_start(Gtk.HSeparator(), 0,5,5) ############################# + + def show_hide_shadows(w): + softshadows.set_visible( w.get_active() ) + contactshadows.set_visible( w.get_active() ) + shadowcubebox.set_visible( w.get_active() ) + shadowcascadebox.set_visible( w.get_active() ) + + + shadows = Gtk.CheckButton("Shadows") + shadows.set_active(True) + shadows.set_tooltip_text("Whether to render Shadows.") + shadows.connect("clicked", show_hide_shadows) + + box.pack_start(shadows, 0,5,5) + + # Soft Shadows + + softshadows = Gtk.CheckButton("Soft Shadows") + softshadows.set_active(True) + softshadows.set_tooltip_text("Whether to try rendering soft Shadows. ( Requires more than one Sample )") + + box.pack_start(softshadows, 0,5,5) + + # Contact Shadows + + contactshadows = Gtk.CheckButton("Contact Shadows") + contactshadows.set_active(True) + contactshadows.set_tooltip_text("Render a special pass of shadows to fill up small cracks.") + + box.pack_start(contactshadows, 0,5,5) + + # Shadow resolution + + shadowresolutions = [ + "4096", + "2048", + "1024", + "512", + "256", + "128", + "64" + ] + + shadowcubebox = Gtk.HBox() + shadowcubelabel = Gtk.Label("Lamp Shadow Resolution: ") + shadowcubemenu = Gtk.ComboBoxText() + + for i in shadowresolutions: + shadowcubemenu.append_text(i) + + shadowcubemenu.set_active(3) + + shadowcubebox.pack_start(shadowcubelabel,0,5,5) + shadowcubebox.pack_end(shadowcubemenu,0,5,5) + + box.pack_start(shadowcubebox, 0,5,5) + + + # Shadow cascade size + + shadowcascadebox = Gtk.HBox() + shadowcascadelabel = Gtk.Label("Sun Shadow Resolution: ") + shadowcascademenu = Gtk.ComboBoxText() + + for i in shadowresolutions: + shadowcascademenu.append_text(i) + + shadowcascademenu.set_active(2) + + shadowcascadebox.pack_start(shadowcascadelabel,0,5,5) + shadowcascadebox.pack_end(shadowcascademenu,0,5,5) + + box.pack_start(shadowcascadebox, 0,5,5) + + ############ CONTROLS ######################## + box.pack_start(Gtk.HSeparator(), 0,5,5) ############################# + box.pack_start(Gtk.Label("Controls"), 0,5,5) + + def show_hide_car_controls(w): + controlboxes["veh_forward"].set_visible( not w.get_active() ) + controlboxes["veh_backward"].set_visible( not w.get_active() ) + controlboxes["veh_left"].set_visible( not w.get_active() ) + controlboxes["veh_right"].set_visible( not w.get_active() ) + + carmouseguides.set_visible( w.get_active() ) + carmouseshow.set_visible( w.get_active() ) + + + controlcarmouse = Gtk.CheckButton("Control Car With a Mouse") + controlcarmouse.connect("clicked", show_hide_car_controls ) + controlcarmouse.set_tooltip_text("Use the mouse to control the car. Allows for more precise input. Has a learning curve.") + box.pack_start(controlcarmouse, 0,5,5) + + carmouseguides = Gtk.CheckButton("Mouse Control Guides") + carmouseguides.set_active(True) + carmouseguides.set_tooltip_text("Show guides on the screen when controlling the car with the mouse.") + box.pack_start(carmouseguides, 0,5,5) + + carmouseshow = Gtk.CheckButton("Show Mouse Cursor") + carmouseshow.set_tooltip_text("Show mouse cursor when controlling the car with the mouse. ( Easier to understand than the guides, but can obstruct the view ).") + box.pack_start(carmouseshow, 0,5,5) + + controlnames = { + "veh_forward" :"Drive Forward", + "veh_backward" :"Drive Backward", + "veh_left" :"Steer Left", + "veh_right" :"Steer Right", + "veh_drift" :"Handbrake", + "veh_nitro" :"Use Nitro", + "veh_resque" :"Resque During Race", + "veh_gear_up" :"Shift Gears Up", + "veh_gear_down":"Shift Gears Down", + "veh_auto_gear":"Switch Automatic Gearbox", + "veh_dynamcam" :"Switch Dynamic Camera", + "veh_upload" :"Truck Deploy Cargo", + "chr_forward" :"Walk Forward", + "chr_backward" :"Walk Backward", + "chr_left" :"Walk Left", + "chr_right" :"Walk Right", + "chr_jump" :"Jump", + "chr_get_car" :"Enter Car", + "chr_swimdown" :"Swim Down" + + + } + + controldefaults = { + "veh_forward" :"UpArrow", + "veh_backward" :"DownArrow", + "veh_left" :"LeftArrow", + "veh_right" :"RightArrow", + "veh_drift" :"Space", + "veh_nitro" :"Z", + "veh_resque" :"BackSpace", + "veh_gear_up" :"W", + "veh_gear_down":"S", + "veh_auto_gear":"Q", + "veh_dynamcam" :"C", + "veh_upload" :"D", + "chr_forward" :"W", + "chr_backward" :"S", + "chr_left" :"A", + "chr_right" :"D", + "chr_jump" :"Space", + "chr_get_car" :"Enter", + "chr_swimdown" :"LShift" + + } + + controlwidgets = {} + controlboxes = {} + + for control in controlnames: + + cbox = Gtk.HBox() + box.pack_start(cbox, 0,5,5) + controlboxes[control] = cbox + + cbox.pack_start(Gtk.Label(controlnames[control]), 0,5,5) + controlwidgets[control] = Gtk.ComboBoxText() + cbox.pack_end(controlwidgets[control], 0,5,5) + + for key in keycodes: + controlwidgets[control].append_text(key) + controlwidgets[control].set_active( list(keycodes.keys()).index(controldefaults[control]) ) + + + + ######## NETWORK ###### + + box.pack_start(Gtk.HSeparator(), 0,5,5) ############################# + box.pack_start(Gtk.Label("Multiplayer"), 0,5,5) + + def show_hide_multiplayer(w): + + hostbox.set_visible( w.get_active() ) + usernamebox.set_visible( w.get_active() ) + multiplayerroombox.set_visible( w.get_active() ) + + multiplayer = Gtk.CheckButton("Multiplayer") + multiplayer.connect("clicked", show_hide_multiplayer) + box.pack_start(multiplayer, 0,5,5) + + + hostbox = Gtk.HBox() + hostbox.pack_start(Gtk.Label("Hostname:"), 0,5,5) + hostname = Gtk.Entry() + hostbox.pack_end(hostname, 0,5,5) + box.pack_start(hostbox, 0,5,5) + + usernamebox = Gtk.HBox() + usernamebox.pack_start(Gtk.Label("Username:"),0,5,5) + username = Gtk.Entry() + usernamebox.pack_end(username,0,5,5) + box.pack_start(usernamebox, 0,5,5) + + multiplayerroombox = Gtk.HBox() + multiplayerroombox.pack_start(Gtk.Label("Room:"),0,5,5) + multiplayerroom = Gtk.Entry() + multiplayerroombox.pack_end(multiplayerroom,0,5,5) + box.pack_start(multiplayerroombox, 0,5,5) + + + + + ######### TIME ######## + + + box.pack_start(Gtk.HSeparator(), 0,5,5) ############################# + box.pack_start(Gtk.Label("In Game Time"), 0,5,5) + + clockspeedbox = Gtk.HBox() + clockspeedbox.pack_start(Gtk.Label("In Game Clock Speed:"), 0,5,5) + clockspeedadjustment = Gtk.Adjustment(20, lower=0.1, upper=10000, step_increment=0.1) + clockspeedentry = Gtk.SpinButton(adjustment=clockspeedadjustment, digits=2) + clockspeedentry.set_tooltip_text("How much the clock in the game is faster than the real clock. 1 is real time speed. 10 is ten times faster.") + clockspeedbox.pack_end(clockspeedentry, 0,5,5) + + box.pack_start(clockspeedbox, 0,5,5) + + + + ############################################# DEV SETTINGS ##################### + + box.pack_start(Gtk.HSeparator(), 0,5,5) ############################# + + box.pack_start(Gtk.Label("For Developers"), 0,5,5) + + def show_hide_devmode(w): + devmode_cheats.set_visible( w.get_active() ) + devmode_visible_rays.set_visible( w.get_active() ) + + if not w.get_active(): + devmode_cheats.set_active( False ) + devmode_visible_rays.set_active( False ) + + devmode = Gtk.CheckButton("Developer Mode") + devmode.set_tooltip_text("Developer mode is mode used in development of the game.") + box.pack_start(devmode, 0,5,5) + devmode.connect("clicked", show_hide_devmode ) + + devmode_cheats = Gtk.CheckButton("Developer Keyboard Cheatcodes") + devmode_cheats.set_tooltip_text("Enable keyboards cheat codes, for testing purposes.") + box.pack_start(devmode_cheats, 0,5,5) + + devmode_visible_rays = Gtk.CheckButton("Developer Visible Rays") + devmode_visible_rays.set_tooltip_text("Show the rays that are used for AIs to see.") + box.pack_start(devmode_visible_rays, 0,5,5) + + + + + ################################################################################ + + + + # Loading settings + data = load_settings() + + if data: + upbgeinput.set_text( data.get("blenderplayer", "blenderplayer") ) + resfull.set_active( data.get("fullscreen", False ) ) + resmenu.set_active( resolutions.index( data.get("resolution", "1920:1080" ) ) ) + compositor.set_active( data.get("compositor", True ) ) + reflections.set_active( data.get("reflections", True ) ) + trees.set_active( data.get("trees", True ) ) + poles.set_active( data.get("poles", True ) ) + fences.set_active( data.get("fences", True ) ) + samplesadjust.set_value( data.get("samples", 1 ) ) + skinsamplesadjust.set_value( data.get("skinsamples", 5 ) ) + volumesamplesadjust.set_value( data.get("volumesamples", 32 ) ) + volumelight.set_active( data.get("volumelight", True ) ) + volumeshadow.set_active( data.get("volumeshadow", False ) ) + volumeshadowsamplesadjust.set_value( data.get("volshadsampl", 3 ) ) + shadows.set_active( data.get("shadows", True ) ) + softshadows.set_active( data.get("softshadows", True ) ) + contactshadows.set_active( data.get("cntctshadows", True ) ) + shadowcubemenu.set_active( shadowresolutions.index( data.get("shadowslamps", "512" ) ) ) + shadowcascademenu.set_active( shadowresolutions.index( data.get("shadowssun", "1024" ) ) ) + devmode.set_active( data.get("devmode", False ) ) + devmode_cheats.set_active( data.get("dev-cheats", False ) ) + devmode_visible_rays.set_active(data.get("dev-rays", False ) ) + clockspeedadjustment.set_value( data.get("clockspeed", 20.0 ) ) + controlcarmouse.set_active( data.get("veh_mouse", False ) ) + maxcarsadjust.set_value( data.get("maxcars", 4 ) ) + carmouseguides.set_active( data.get("veh_mouse_guides",True ) ) + carmouseshow.set_active( data.get("veh_mouse_cursor",False ) ) + multiplayer.set_active( data.get("multiplayer", False ) ) + hostname.set_text( data.get("mp-host", "http://localhost:6969") ) + username.set_text( data.get("mp-name", "Dani" ) ) + multiplayerroom.set_text( data.get("mp-room", "Main" ) ) + + + for control in controlwidgets: + try: + controlwidgets[control].set_active( list(keycodes.keys()).index(data.get(control)) ) + except: + controlwidgets[control].set_active( list(keycodes.keys()).index(controldefaults[control]) ) + + + + settingsWindow.show_all() + + show_hide_shadows(shadows) + show_hide_volume_shadows(volumeshadow) + show_hide_devmode(devmode) + show_hide_car_controls(controlcarmouse) + show_hide_multiplayer(multiplayer) + + response = settingsWindow.run() + + if response == Gtk.ResponseType.OK: + folder = get_settings_folder() + f = folder+"/config.json" + + data = {"blenderplayer":upbgeinput.get_text(),# + "fullscreen" :resfull.get_active(),# + "resolution" :resmenu.get_active_text(),# + "compositor" :compositor.get_active(),# + "reflections" :reflections.get_active(),# + "trees" :trees.get_active(),# + "poles" :poles.get_active(),# + "fences" :fences.get_active(),# + "samples" :int(samplesadjust.get_value()),# + "skinsamples" :int(skinsamplesadjust.get_value()),# + "volumesamples":int(volumesamplesadjust.get_value()),# + "volumelight" :volumelight.get_active(),# + "volumeshadow" :volumeshadow.get_active(),# + "volshadsampl" :int(volumeshadowsamplesadjust.get_value()),# + "shadows" :shadows.get_active(), # + "softshadows" :softshadows.get_active(),# + "cntctshadows" :contactshadows.get_active(),# + "shadowslamps" :shadowcubemenu.get_active_text(),# + "shadowssun" :shadowcascademenu.get_active_text(), # + "devmode" :devmode.get_active(), + "dev-cheats" :devmode_cheats.get_active(), + "dev-rays" :devmode_visible_rays.get_active(), + "clockspeed" :clockspeedadjustment.get_value(), + "veh_mouse" :controlcarmouse.get_active(), + "maxcars" :int(maxcarsadjust.get_value()), + "veh_mouse_guides":carmouseguides.get_active(), + "veh_mouse_cursor":carmouseshow.get_active(), + "multiplayer" :multiplayer.get_active(), + "mp-host" :hostname.get_text(), + "mp-name" :username.get_text(), + "mp-room" :multiplayerroom.get_text() + + } + + for control in controlwidgets: + data[control] = controlwidgets[control].get_active_text() + + with open(f, "w") as save: + json.dump(data, save, indent=4, sort_keys=True) + + + settingsWindow.destroy() + +settingsbutton = Gtk.Button("Settings") +settingsbutton.connect("clicked", set_settings) +settingsbutton.set_relief(Gtk.ReliefStyle.NONE) +playbox.pack_end(settingsbutton, 0,5,5) + + +def get_settings_folder(): + + game = "danisrace" + + try: + data_dir = os.environ["XDG_DATA_HOME"] + "/" + game + except: + data_dir = os.path.expanduser("~/.local/share/"+game) + + try: + os.makedirs(data_dir) + except: + pass + + return data_dir + +def load_settings(): + + folder = get_settings_folder() + f = folder+"/config.json" + + try: + with open(f) as o: + return json.load(o) + except: return None + + + +#### Launching he ui ###### + +win.show_all() +Gtk.main() diff --git a/sfx/.xdp-telephone_EQ.png-R2HduU b/sfx/.xdp-telephone_EQ.png-R2HduU new file mode 100644 index 0000000..5cd90d1 Binary files /dev/null and b/sfx/.xdp-telephone_EQ.png-R2HduU differ diff --git a/sfx/active.ogg b/sfx/active.ogg new file mode 100644 index 0000000..4e9ed06 Binary files /dev/null and b/sfx/active.ogg differ diff --git a/sfx/ambiance/forest.ogg b/sfx/ambiance/forest.ogg new file mode 100644 index 0000000..01789e4 Binary files /dev/null and b/sfx/ambiance/forest.ogg differ diff --git a/sfx/ambiance/water.ogg b/sfx/ambiance/water.ogg new file mode 100644 index 0000000..ce791d9 Binary files /dev/null and b/sfx/ambiance/water.ogg differ diff --git a/sfx/boom.ogg b/sfx/boom.ogg new file mode 100644 index 0000000..c596e38 Binary files /dev/null and b/sfx/boom.ogg differ diff --git a/sfx/checkpoint.ogg b/sfx/checkpoint.ogg new file mode 100644 index 0000000..43f9c6f Binary files /dev/null and b/sfx/checkpoint.ogg differ diff --git a/sfx/door.ogg b/sfx/door.ogg new file mode 100644 index 0000000..e3e18bd Binary files /dev/null and b/sfx/door.ogg differ diff --git a/sfx/drift.ogg b/sfx/drift.ogg new file mode 100644 index 0000000..f957d63 Binary files /dev/null and b/sfx/drift.ogg differ diff --git a/sfx/engines/neonspeedster.ogg b/sfx/engines/neonspeedster.ogg new file mode 100644 index 0000000..2c3cd07 Binary files /dev/null and b/sfx/engines/neonspeedster.ogg differ diff --git a/sfx/engines/redkiss.ogg b/sfx/engines/redkiss.ogg new file mode 100644 index 0000000..e0d728b Binary files /dev/null and b/sfx/engines/redkiss.ogg differ diff --git a/sfx/engines/regular.ogg b/sfx/engines/regular.ogg new file mode 100644 index 0000000..35c0b47 Binary files /dev/null and b/sfx/engines/regular.ogg differ diff --git a/sfx/engines/truck.ogg b/sfx/engines/truck.ogg new file mode 100644 index 0000000..6bd38b5 Binary files /dev/null and b/sfx/engines/truck.ogg differ diff --git a/sfx/fixtool.ogg b/sfx/fixtool.ogg new file mode 100644 index 0000000..f142d42 Binary files /dev/null and b/sfx/fixtool.ogg differ diff --git a/sfx/gear_shit.ogg b/sfx/gear_shit.ogg new file mode 100644 index 0000000..a6138c9 Binary files /dev/null and b/sfx/gear_shit.ogg differ diff --git a/sfx/glass.ogg b/sfx/glass.ogg new file mode 100644 index 0000000..2ab2565 Binary files /dev/null and b/sfx/glass.ogg differ diff --git a/sfx/grind.ogg b/sfx/grind.ogg new file mode 100644 index 0000000..e19798c Binary files /dev/null and b/sfx/grind.ogg differ diff --git a/sfx/hit.ogg b/sfx/hit.ogg new file mode 100644 index 0000000..21a2cf1 Binary files /dev/null and b/sfx/hit.ogg differ diff --git a/sfx/home_door.ogg b/sfx/home_door.ogg new file mode 100644 index 0000000..ee971dc Binary files /dev/null and b/sfx/home_door.ogg differ diff --git a/sfx/map.xcf b/sfx/map.xcf new file mode 100644 index 0000000..b8286f1 Binary files /dev/null and b/sfx/map.xcf differ diff --git a/sfx/money.ogg b/sfx/money.ogg new file mode 100644 index 0000000..4af0710 Binary files /dev/null and b/sfx/money.ogg differ diff --git a/sfx/music/ringtone.ogg b/sfx/music/ringtone.ogg new file mode 100644 index 0000000..f48f247 Binary files /dev/null and b/sfx/music/ringtone.ogg differ diff --git a/sfx/nitro.ogg b/sfx/nitro.ogg new file mode 100644 index 0000000..3dc9b04 Binary files /dev/null and b/sfx/nitro.ogg differ diff --git a/sfx/painttool.ogg b/sfx/painttool.ogg new file mode 100644 index 0000000..c6a0a69 Binary files /dev/null and b/sfx/painttool.ogg differ diff --git a/sfx/redline.ogg b/sfx/redline.ogg new file mode 100644 index 0000000..acd497c Binary files /dev/null and b/sfx/redline.ogg differ diff --git a/sfx/scrape.ogg b/sfx/scrape.ogg new file mode 100644 index 0000000..a89ccc1 Binary files /dev/null and b/sfx/scrape.ogg differ diff --git a/sfx/sources/scene_02.aup b/sfx/sources/scene_02.aup new file mode 100644 index 0000000..3cbc839 --- /dev/null +++ b/sfx/sources/scene_02.aup @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f078.au b/sfx/sources/scene_02_data/eff/d1f/eff1f078.au new file mode 100644 index 0000000..c862dc2 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f078.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f090.au b/sfx/sources/scene_02_data/eff/d1f/eff1f090.au new file mode 100644 index 0000000..54c015a Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f090.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f0b3.au b/sfx/sources/scene_02_data/eff/d1f/eff1f0b3.au new file mode 100644 index 0000000..cda8d78 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f0b3.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f1f5.au b/sfx/sources/scene_02_data/eff/d1f/eff1f1f5.au new file mode 100644 index 0000000..9a06e03 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f1f5.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f2d0.au b/sfx/sources/scene_02_data/eff/d1f/eff1f2d0.au new file mode 100644 index 0000000..24b03d1 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f2d0.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f340.au b/sfx/sources/scene_02_data/eff/d1f/eff1f340.au new file mode 100644 index 0000000..a4c8598 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f340.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f539.au b/sfx/sources/scene_02_data/eff/d1f/eff1f539.au new file mode 100644 index 0000000..787d173 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f539.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f69c.au b/sfx/sources/scene_02_data/eff/d1f/eff1f69c.au new file mode 100644 index 0000000..9fcde32 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f69c.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f6c0.au b/sfx/sources/scene_02_data/eff/d1f/eff1f6c0.au new file mode 100644 index 0000000..9ab8021 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f6c0.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f6c9.au b/sfx/sources/scene_02_data/eff/d1f/eff1f6c9.au new file mode 100644 index 0000000..8c49adb Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f6c9.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f6f8.au b/sfx/sources/scene_02_data/eff/d1f/eff1f6f8.au new file mode 100644 index 0000000..67dc5ac Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f6f8.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f82a.au b/sfx/sources/scene_02_data/eff/d1f/eff1f82a.au new file mode 100644 index 0000000..3bb8e70 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f82a.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f8c9.au b/sfx/sources/scene_02_data/eff/d1f/eff1f8c9.au new file mode 100644 index 0000000..0316da3 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f8c9.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f8ed.au b/sfx/sources/scene_02_data/eff/d1f/eff1f8ed.au new file mode 100644 index 0000000..f58e815 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f8ed.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f967.au b/sfx/sources/scene_02_data/eff/d1f/eff1f967.au new file mode 100644 index 0000000..5aff7c4 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f967.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1f9aa.au b/sfx/sources/scene_02_data/eff/d1f/eff1f9aa.au new file mode 100644 index 0000000..0b68a41 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1f9aa.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1fa55.au b/sfx/sources/scene_02_data/eff/d1f/eff1fa55.au new file mode 100644 index 0000000..0b2effe Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1fa55.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1faf9.au b/sfx/sources/scene_02_data/eff/d1f/eff1faf9.au new file mode 100644 index 0000000..44a4df0 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1faf9.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1fb00.au b/sfx/sources/scene_02_data/eff/d1f/eff1fb00.au new file mode 100644 index 0000000..9794c86 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1fb00.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1fd51.au b/sfx/sources/scene_02_data/eff/d1f/eff1fd51.au new file mode 100644 index 0000000..6f35aed Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1fd51.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1fdee.au b/sfx/sources/scene_02_data/eff/d1f/eff1fdee.au new file mode 100644 index 0000000..fd81123 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1fdee.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1fe6a.au b/sfx/sources/scene_02_data/eff/d1f/eff1fe6a.au new file mode 100644 index 0000000..429ba90 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1fe6a.au differ diff --git a/sfx/sources/scene_02_data/eff/d1f/eff1fffd.au b/sfx/sources/scene_02_data/eff/d1f/eff1fffd.au new file mode 100644 index 0000000..983e7f4 Binary files /dev/null and b/sfx/sources/scene_02_data/eff/d1f/eff1fffd.au differ diff --git a/sfx/splash.ogg b/sfx/splash.ogg new file mode 100644 index 0000000..2101cbb Binary files /dev/null and b/sfx/splash.ogg differ diff --git a/sfx/tire_pop.ogg b/sfx/tire_pop.ogg new file mode 100644 index 0000000..a5e7ed6 Binary files /dev/null and b/sfx/tire_pop.ogg differ diff --git a/sfx/voices/angry_pursuit/hit_01.ogg b/sfx/voices/angry_pursuit/hit_01.ogg new file mode 100644 index 0000000..269da27 Binary files /dev/null and b/sfx/voices/angry_pursuit/hit_01.ogg differ diff --git a/sfx/voices/angry_pursuit/hit_02.ogg b/sfx/voices/angry_pursuit/hit_02.ogg new file mode 100644 index 0000000..8e02286 Binary files /dev/null and b/sfx/voices/angry_pursuit/hit_02.ogg differ diff --git a/sfx/voices/angry_pursuit/hit_03.ogg b/sfx/voices/angry_pursuit/hit_03.ogg new file mode 100644 index 0000000..91edb56 Binary files /dev/null and b/sfx/voices/angry_pursuit/hit_03.ogg differ diff --git a/sfx/voices/angry_pursuit/hit_04.ogg b/sfx/voices/angry_pursuit/hit_04.ogg new file mode 100644 index 0000000..13fb0aa Binary files /dev/null and b/sfx/voices/angry_pursuit/hit_04.ogg differ diff --git a/sfx/voices/angry_pursuit/hit_05.ogg b/sfx/voices/angry_pursuit/hit_05.ogg new file mode 100644 index 0000000..68dc697 Binary files /dev/null and b/sfx/voices/angry_pursuit/hit_05.ogg differ diff --git a/sfx/voices/angry_pursuit/start_01.ogg b/sfx/voices/angry_pursuit/start_01.ogg new file mode 100644 index 0000000..23164a9 Binary files /dev/null and b/sfx/voices/angry_pursuit/start_01.ogg differ diff --git a/sfx/voices/angry_pursuit/start_02.ogg b/sfx/voices/angry_pursuit/start_02.ogg new file mode 100644 index 0000000..49a8978 Binary files /dev/null and b/sfx/voices/angry_pursuit/start_02.ogg differ diff --git a/sfx/voices/angry_pursuit/start_03.ogg b/sfx/voices/angry_pursuit/start_03.ogg new file mode 100644 index 0000000..05f53d6 Binary files /dev/null and b/sfx/voices/angry_pursuit/start_03.ogg differ diff --git a/sfx/voices/angry_pursuit/start_04.ogg b/sfx/voices/angry_pursuit/start_04.ogg new file mode 100644 index 0000000..b170007 Binary files /dev/null and b/sfx/voices/angry_pursuit/start_04.ogg differ diff --git a/sfx/voices/angry_pursuit/start_05.ogg b/sfx/voices/angry_pursuit/start_05.ogg new file mode 100644 index 0000000..52abb9a Binary files /dev/null and b/sfx/voices/angry_pursuit/start_05.ogg differ diff --git a/sfx/voices/angry_pursuit/start_06.ogg b/sfx/voices/angry_pursuit/start_06.ogg new file mode 100644 index 0000000..2644457 Binary files /dev/null and b/sfx/voices/angry_pursuit/start_06.ogg differ diff --git a/sfx/voices/angry_pursuit/start_07.ogg b/sfx/voices/angry_pursuit/start_07.ogg new file mode 100644 index 0000000..50588a5 Binary files /dev/null and b/sfx/voices/angry_pursuit/start_07.ogg differ diff --git a/sfx/voices/angry_pursuit/start_08.ogg b/sfx/voices/angry_pursuit/start_08.ogg new file mode 100644 index 0000000..2fba944 Binary files /dev/null and b/sfx/voices/angry_pursuit/start_08.ogg differ diff --git a/sfx/voices/dani/entering.ogg b/sfx/voices/dani/entering.ogg new file mode 100644 index 0000000..3fa86be Binary files /dev/null and b/sfx/voices/dani/entering.ogg differ diff --git a/sfx/voices/dani/jump.ogg b/sfx/voices/dani/jump.ogg new file mode 100644 index 0000000..9075f0a Binary files /dev/null and b/sfx/voices/dani/jump.ogg differ diff --git a/sfx/voices/dani/land.ogg b/sfx/voices/dani/land.ogg new file mode 100644 index 0000000..65b25ee Binary files /dev/null and b/sfx/voices/dani/land.ogg differ diff --git a/sfx/voices/dani/ohgod.ogg b/sfx/voices/dani/ohgod.ogg new file mode 100644 index 0000000..613f8a5 Binary files /dev/null and b/sfx/voices/dani/ohgod.ogg differ diff --git a/sfx/voices/dani/ohno.ogg b/sfx/voices/dani/ohno.ogg new file mode 100644 index 0000000..f74ae7b Binary files /dev/null and b/sfx/voices/dani/ohno.ogg differ diff --git a/sfx/voices/dani/ohnoindeed.ogg b/sfx/voices/dani/ohnoindeed.ogg new file mode 100644 index 0000000..690bacf Binary files /dev/null and b/sfx/voices/dani/ohnoindeed.ogg differ diff --git a/sfx/voices/dani/walk.ogg b/sfx/voices/dani/walk.ogg new file mode 100644 index 0000000..3725898 Binary files /dev/null and b/sfx/voices/dani/walk.ogg differ diff --git a/sfx/voices/dani/wearesinking.ogg b/sfx/voices/dani/wearesinking.ogg new file mode 100644 index 0000000..11ed145 Binary files /dev/null and b/sfx/voices/dani/wearesinking.ogg differ diff --git a/sfx/voices/dani/yelling.ogg b/sfx/voices/dani/yelling.ogg new file mode 100644 index 0000000..2cb9485 Binary files /dev/null and b/sfx/voices/dani/yelling.ogg differ diff --git a/sfx/voices/jack/crash1.ogg b/sfx/voices/jack/crash1.ogg new file mode 100644 index 0000000..007664f Binary files /dev/null and b/sfx/voices/jack/crash1.ogg differ diff --git a/sfx/voices/jack/crash2.ogg b/sfx/voices/jack/crash2.ogg new file mode 100644 index 0000000..164cff7 Binary files /dev/null and b/sfx/voices/jack/crash2.ogg differ diff --git a/sfx/voices/jack/crash3.ogg b/sfx/voices/jack/crash3.ogg new file mode 100644 index 0000000..122cb8c Binary files /dev/null and b/sfx/voices/jack/crash3.ogg differ diff --git a/sfx/voices/jack/crash4.ogg b/sfx/voices/jack/crash4.ogg new file mode 100644 index 0000000..50d551d Binary files /dev/null and b/sfx/voices/jack/crash4.ogg differ diff --git a/sfx/voices/jack/crash5.ogg b/sfx/voices/jack/crash5.ogg new file mode 100644 index 0000000..94205f1 Binary files /dev/null and b/sfx/voices/jack/crash5.ogg differ diff --git a/sfx/voices/jack/near1.ogg b/sfx/voices/jack/near1.ogg new file mode 100644 index 0000000..5d9468d Binary files /dev/null and b/sfx/voices/jack/near1.ogg differ diff --git a/sfx/voices/jack/near2.ogg b/sfx/voices/jack/near2.ogg new file mode 100644 index 0000000..23f63ab Binary files /dev/null and b/sfx/voices/jack/near2.ogg differ diff --git a/sfx/voices/jack/near3.ogg b/sfx/voices/jack/near3.ogg new file mode 100644 index 0000000..906f08a Binary files /dev/null and b/sfx/voices/jack/near3.ogg differ diff --git a/sfx/voices/jack/speed1.ogg b/sfx/voices/jack/speed1.ogg new file mode 100644 index 0000000..73f90c2 Binary files /dev/null and b/sfx/voices/jack/speed1.ogg differ diff --git a/sfx/voices/jack/speed2.ogg b/sfx/voices/jack/speed2.ogg new file mode 100644 index 0000000..4437b52 Binary files /dev/null and b/sfx/voices/jack/speed2.ogg differ diff --git a/sfx/voices/jack/speed3.ogg b/sfx/voices/jack/speed3.ogg new file mode 100644 index 0000000..6d85636 Binary files /dev/null and b/sfx/voices/jack/speed3.ogg differ diff --git a/sfx/voices/jack/speed4.ogg b/sfx/voices/jack/speed4.ogg new file mode 100644 index 0000000..74de663 Binary files /dev/null and b/sfx/voices/jack/speed4.ogg differ diff --git a/sfx/voices/jack/speed5.ogg b/sfx/voices/jack/speed5.ogg new file mode 100644 index 0000000..6556933 Binary files /dev/null and b/sfx/voices/jack/speed5.ogg differ diff --git a/sfx/voices/jack/speed6.ogg b/sfx/voices/jack/speed6.ogg new file mode 100644 index 0000000..903a9b4 Binary files /dev/null and b/sfx/voices/jack/speed6.ogg differ diff --git a/sfx/voices/jack/speed7.ogg b/sfx/voices/jack/speed7.ogg new file mode 100644 index 0000000..d87f8cb Binary files /dev/null and b/sfx/voices/jack/speed7.ogg differ diff --git a/sfx/voices/jack/speed8.ogg b/sfx/voices/jack/speed8.ogg new file mode 100644 index 0000000..ec98d1f Binary files /dev/null and b/sfx/voices/jack/speed8.ogg differ diff --git a/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/01.ogg b/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/01.ogg new file mode 100644 index 0000000..0f0c7d7 Binary files /dev/null and b/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/01.ogg differ diff --git a/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/brief.ogg b/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/brief.ogg new file mode 100644 index 0000000..d362e76 Binary files /dev/null and b/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/brief.ogg differ diff --git a/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/lose.ogg b/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/lose.ogg new file mode 100644 index 0000000..306c7bb Binary files /dev/null and b/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/lose.ogg differ diff --git a/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/win.ogg b/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/win.ogg new file mode 100644 index 0000000..4aa0a50 Binary files /dev/null and b/sfx/voices/scenes/01_Bring_Neonspeedster_To_Racetrack/win.ogg differ diff --git a/sfx/voices/scenes/02_Bring_Truck_To_House/brief.ogg b/sfx/voices/scenes/02_Bring_Truck_To_House/brief.ogg new file mode 100644 index 0000000..c532059 Binary files /dev/null and b/sfx/voices/scenes/02_Bring_Truck_To_House/brief.ogg differ diff --git a/sfx/voices/scenes/02_Bring_Truck_To_House/d1.ogg b/sfx/voices/scenes/02_Bring_Truck_To_House/d1.ogg new file mode 100644 index 0000000..78abf33 Binary files /dev/null and b/sfx/voices/scenes/02_Bring_Truck_To_House/d1.ogg differ diff --git a/sfx/voices/scenes/02_Bring_Truck_To_House/d2.ogg b/sfx/voices/scenes/02_Bring_Truck_To_House/d2.ogg new file mode 100644 index 0000000..1217824 Binary files /dev/null and b/sfx/voices/scenes/02_Bring_Truck_To_House/d2.ogg differ diff --git a/sfx/voices/scenes/02_Bring_Truck_To_House/twist.ogg b/sfx/voices/scenes/02_Bring_Truck_To_House/twist.ogg new file mode 100644 index 0000000..397897f Binary files /dev/null and b/sfx/voices/scenes/02_Bring_Truck_To_House/twist.ogg differ diff --git a/sfx/voices/scenes/02_Bring_Truck_To_House/yes.ogg b/sfx/voices/scenes/02_Bring_Truck_To_House/yes.ogg new file mode 100644 index 0000000..9a56fe0 Binary files /dev/null and b/sfx/voices/scenes/02_Bring_Truck_To_House/yes.ogg differ diff --git a/sfx/wood.ogg b/sfx/wood.ogg new file mode 100644 index 0000000..3f440a8 Binary files /dev/null and b/sfx/wood.ogg differ diff --git a/sounds/hit.ogg b/sounds/hit.ogg new file mode 100644 index 0000000..21a2cf1 Binary files /dev/null and b/sounds/hit.ogg differ diff --git a/sounds/scrape.ogg b/sounds/scrape.ogg new file mode 100644 index 0000000..a89ccc1 Binary files /dev/null and b/sounds/scrape.ogg differ diff --git a/sounds/wood.ogg b/sounds/wood.ogg new file mode 100644 index 0000000..3f440a8 Binary files /dev/null and b/sounds/wood.ogg differ diff --git a/tests/Racetrack.blend b/tests/Racetrack.blend new file mode 100644 index 0000000..9647b7a Binary files /dev/null and b/tests/Racetrack.blend differ diff --git a/tests/bgelogic/NLLogic.py b/tests/bgelogic/NLLogic.py new file mode 100644 index 0000000..a3d0620 --- /dev/null +++ b/tests/bgelogic/NLLogic.py @@ -0,0 +1,116 @@ +# MACHINE GENERATED +import bge, bpy, sys, importlib +import mathutils +from uplogic import nodes +import math + +def _initialize(owner): + network = nodes.LogicNetwork() + PAR0000 = nodes.ParameterObjectProperty() + PAR0001 = nodes.ParameterVector3Simple() + CON0002 = nodes.ConditionCollision() + CON0003 = nodes.ConditionAnd() + ACT0004 = nodes.ActionSetGameObjectGameProperty() + CON0005 = nodes.ObjectPropertyOperator() + CON0006 = nodes.ObjectPropertyOperator() + ACT0007 = nodes.ActionSetGameObjectGameProperty() + ACT0008 = nodes.ActionAddToGameObjectGameProperty() + CON0009 = nodes.ConditionNot() + ACT0010 = nodes.ActionApplyLocation() + CON0011 = nodes.ConditionOnUpdate() + CON0012 = nodes.ConditionKeyPressed() + CON0013 = nodes.ConditionKeyPressed() + ACT0014 = nodes.ActionApplyTorque() + CON0015 = nodes.ConditionKeyPressed() + ACT0016 = nodes.ActionAddToGameObjectGameProperty() + ACT0017 = nodes.ActionApplyTorque() + PAR0000.game_object = "NLO:U_O" + PAR0000.property_name = "speed" + PAR0001.input_x = 0.0 + PAR0001.input_y = PAR0000 + PAR0001.input_z = 0.0 + CON0002.game_object = "NLO:U_O" + CON0002.use_mat = False + CON0002.prop = "" + CON0002.material = None + CON0002.pulse = True + CON0003.condition_a = CON0002 + CON0003.condition_b = CON0012 + ACT0004.condition = CON0005 + ACT0004.game_object = "NLO:U_O" + ACT0004.property_name = "speed" + ACT0004.property_value = 0.30000001192092896 + CON0005.game_object = "NLO:U_O" + CON0005.property_name = "speed" + CON0005.compare_value = 0.30000001192092896 + CON0005.operator = 2 + CON0006.game_object = "NLO:U_O" + CON0006.property_name = "speed" + CON0006.compare_value = 0.0 + CON0006.operator = 3 + ACT0007.condition = CON0006 + ACT0007.game_object = "NLO:U_O" + ACT0007.property_name = "speed" + ACT0007.property_value = 0.0 + ACT0008.operator = nodes.ActionAddToGameObjectGameProperty.op_by_code("ADD") + ACT0008.condition = CON0003 + ACT0008.game_object = "NLO:U_O" + ACT0008.property_name = "speed" + ACT0008.property_value = 0.0020000000949949026 + CON0009.condition = CON0003 + ACT0010.local = True + ACT0010.condition = CON0011 + ACT0010.game_object = "NLO:U_O" + ACT0010.movement = PAR0001.OUTV + CON0012.key_code = bge.events.UPARROWKEY + CON0012.pulse = True + CON0013.key_code = bge.events.LEFTARROWKEY + CON0013.pulse = True + ACT0014.local = True + ACT0014.condition = CON0013 + ACT0014.game_object = "NLO:U_O" + ACT0014.torque = mathutils.Vector((0.0, 0.0, 100.0)) + CON0015.key_code = bge.events.RIGHTARROWKEY + CON0015.pulse = True + ACT0016.operator = nodes.ActionAddToGameObjectGameProperty.op_by_code("ADD") + ACT0016.condition = CON0009 + ACT0016.game_object = "NLO:U_O" + ACT0016.property_name = "speed" + ACT0016.property_value = -0.0010000000474974513 + ACT0017.local = True + ACT0017.condition = CON0015 + ACT0017.game_object = "NLO:U_O" + ACT0017.torque = mathutils.Vector((0.0, 0.0, -100.0)) + network.add_cell(PAR0000) + network.add_cell(CON0002) + network.add_cell(CON0005) + network.add_cell(CON0011) + network.add_cell(CON0013) + network.add_cell(CON0015) + network.add_cell(ACT0017) + network.add_cell(PAR0001) + network.add_cell(ACT0004) + network.add_cell(ACT0010) + network.add_cell(ACT0014) + network.add_cell(CON0006) + network.add_cell(CON0012) + network.add_cell(CON0003) + network.add_cell(ACT0008) + network.add_cell(ACT0007) + network.add_cell(CON0009) + network.add_cell(ACT0016) + owner["IGNLTree_Logic"] = network + network._owner = owner + network.setup() + network.stopped = not owner.get('NL__Logic') + return network + +def pulse_network(controller): + owner = controller.owner + network = owner.get("IGNLTree_Logic") + if network is None: + network = _initialize(owner) + if network.stopped: return + shutdown = network.evaluate() + if shutdown is True: + controller.sensors[0].repeat = False diff --git a/tests/bgelogic/NLNodeTree.py b/tests/bgelogic/NLNodeTree.py new file mode 100644 index 0000000..b4f26c6 --- /dev/null +++ b/tests/bgelogic/NLNodeTree.py @@ -0,0 +1,62 @@ +# MACHINE GENERATED +import bge, bpy, sys, importlib +import mathutils +from uplogic import nodes +import math + +def _initialize(owner): + network = nodes.LogicNetwork() + CON0000 = nodes.ConditionOnce() + ACT0001 = nodes.VehicleSetAttributes() + ACT0002 = nodes.ActionCreateVehicleFromParent() + ACT0003 = nodes.VehicleApplyForce() + CON0004 = nodes.ConditionKeyPressed() + CON0000.input_condition = False + CON0000.repeat = False + CON0000.reset_time = 0.5 + ACT0001.condition = ACT0002.OUT + ACT0001.constraint = ACT0002.VEHICLE + ACT0001.wheelcount = 4 + ACT0001.set_suspension_compression = False + ACT0001.suspension_compression = 0.0 + ACT0001.set_suspension_stiffness = False + ACT0001.suspension_stiffness = 0.0 + ACT0001.set_suspension_damping = False + ACT0001.suspension_damping = 0.0 + ACT0001.set_tyre_friction = False + ACT0001.tyre_friction = 0.0 + ACT0001.value_type = 'REAR' + ACT0002.condition = CON0000 + ACT0002.game_object = "NLO:NeonSpeedsterBox" + ACT0002.suspension = 0.05999999865889549 + ACT0002.stiffness = 50.0 + ACT0002.damping = 5.0 + ACT0002.friction = 2.0 + ACT0002.wheel_size = 1.0 + ACT0003.condition = CON0004 + ACT0003.vehicle = "NLO:NeonSpeedsterBox" + ACT0003.wheelcount = 2 + ACT0003.power = 1.0 + ACT0003.value_type = 'REAR' + CON0004.key_code = bge.events.WKEY + CON0004.pulse = True + network.add_cell(CON0000) + network.add_cell(ACT0002) + network.add_cell(CON0004) + network.add_cell(ACT0001) + network.add_cell(ACT0003) + owner["IGNLTree_NodeTree"] = network + network._owner = owner + network.setup() + network.stopped = not owner.get('NL__NodeTree') + return network + +def pulse_network(controller): + owner = controller.owner + network = owner.get("IGNLTree_NodeTree") + if network is None: + network = _initialize(owner) + if network.stopped: return + shutdown = network.evaluate() + if shutdown is True: + controller.sensors[0].repeat = False diff --git a/tests/bgelogic/__init__.py b/tests/bgelogic/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/car_test.blend b/tests/car_test.blend new file mode 100644 index 0000000..dd717ac Binary files /dev/null and b/tests/car_test.blend differ diff --git a/tests/fileIgot/car.blend b/tests/fileIgot/car.blend new file mode 100644 index 0000000..6a63afb Binary files /dev/null and b/tests/fileIgot/car.blend differ diff --git a/tests/fileIgot/scripts.py b/tests/fileIgot/scripts.py new file mode 100755 index 0000000..47dd0d6 --- /dev/null +++ b/tests/fileIgot/scripts.py @@ -0,0 +1,191 @@ +# ############################# +# Vehicle Physics Demo +# ############################# +# This file is part of the book: +# "Game Development with Blender" +# by Dalai Felinto and Mike Pan +# +# Published by "CENGAGE Learning" in 2013 +# +# You are free to use-it, modify it and redistribute +# as long as you keep the original credits when pertinent. +# +# File tested with Blender 2.66 +# +# Copyright - February 2013 +# This work is licensed under the Creative Commons +# Attribution-Share Alike 3.0 Unported License +# ############################# + +## First, we import all the python module +from bge import constraints +from bge import logic +from bge import events +from bge import render + +import math + +## Set specific vehicle characteristics ## +wheelRadius = 0.5 +wheelBaseWide = 1.0 +wheelFrontOffset = 0.7 +wheelBackOffset = -1.8 +AttachHeightLocal = 0.2 +suspensionLength = 0.8 +influence = 0.02 +stiffness = 20.0 +damping = 2.0 +compression = 4.0 +friction = 10.0 +Stability = 0.05 + +## This is called from the car object +## is run once at the start of the game +def carInit(): + ## setup aliases for Blender API access ## + cont = logic.getCurrentController() + logic.scene = logic.getCurrentScene() + logic.car = cont.owner + + ## setup general vehicle characteristics ## + wheelAttachDirLocal = [0,0,-1] + wheelAxleLocal = [-1,0,0] + + ## setup vehicle physics ## + vehicle = constraints.createConstraint(logic.car.getPhysicsId(), 0, constraints.VEHICLE_CONSTRAINT) + logic.car["cid"] = vehicle.getConstraintId() + vehicle = constraints.getVehicleConstraint(logic.car["cid"]) + + ## initialize temporary variables ## + logic.car["dS"] = 0.0 + + ## attached wheel based on actuator name ## + wheel0 = logic.scene.objects["Wheel0"] + wheelAttachPosLocal = [wheelBaseWide ,wheelFrontOffset, AttachHeightLocal] + vehicle.addWheel(wheel0,wheelAttachPosLocal,wheelAttachDirLocal,wheelAxleLocal,suspensionLength,wheelRadius,1) + + wheel1 = logic.scene.objects["Wheel1"] + wheelAttachPosLocal = [-wheelBaseWide ,wheelFrontOffset, AttachHeightLocal] + vehicle.addWheel(wheel1,wheelAttachPosLocal,wheelAttachDirLocal,wheelAxleLocal,suspensionLength,wheelRadius,1) + + wheel2 = logic.scene.objects["Wheel2"] + wheelAttachPosLocal = [wheelBaseWide ,wheelBackOffset, AttachHeightLocal] + vehicle.addWheel(wheel2,wheelAttachPosLocal,wheelAttachDirLocal,wheelAxleLocal,suspensionLength,wheelRadius,0) + + wheel3 = logic.scene.objects["Wheel3"] + wheelAttachPosLocal = [-wheelBaseWide ,wheelBackOffset, AttachHeightLocal] + vehicle.addWheel(wheel3,wheelAttachPosLocal,wheelAttachDirLocal,wheelAxleLocal,suspensionLength,wheelRadius,0) + + ## set vehicle roll tendency ## + vehicle.setRollInfluence(influence,0) + vehicle.setRollInfluence(influence,1) + vehicle.setRollInfluence(influence,2) + vehicle.setRollInfluence(influence,3) + + ## set vehicle suspension hardness ## + vehicle.setSuspensionStiffness(stiffness,0) + vehicle.setSuspensionStiffness(stiffness,1) + vehicle.setSuspensionStiffness(stiffness,2) + vehicle.setSuspensionStiffness(stiffness,3) + + ## set vehicle suspension dampness ## + vehicle.setSuspensionDamping(damping,0) + vehicle.setSuspensionDamping(damping,1) + vehicle.setSuspensionDamping(damping,2) + vehicle.setSuspensionDamping(damping,3) + + ## set vehicle suspension compression ratio ## + vehicle.setSuspensionCompression(compression,0) + vehicle.setSuspensionCompression(compression,1) + vehicle.setSuspensionCompression(compression,2) + vehicle.setSuspensionCompression(compression,3) + + ## set vehicle tire friction ## + vehicle.setTyreFriction(friction,0) + vehicle.setTyreFriction(friction,1) + vehicle.setTyreFriction(friction,2) + vehicle.setTyreFriction(friction,3) + + +## called from main car object +## is run once at the start of the game +def carHandler(): + + vehicle = constraints.getVehicleConstraint(logic.car["cid"]) + + ## calculate speed by using the back wheel rotation speed ## + S = vehicle.getWheelRotation(2)+vehicle.getWheelRotation(3) + logic.car["speed"] = (S - logic.car["dS"])*10.0 + + ## apply engine force ## + vehicle.applyEngineForce(logic.car["force"],0) + vehicle.applyEngineForce(logic.car["force"],1) + vehicle.applyEngineForce(logic.car["force"],2) + vehicle.applyEngineForce(logic.car["force"],3) + + ## calculate steering with varying sensitivity ## + if math.fabs(logic.car["speed"])<15.0: s = 2.0 + elif math.fabs(logic.car["speed"])<28.0: s=1.5 + elif math.fabs(logic.car["speed"])<40.0: s=1.0 + else: s=0.5 + + ## steer front wheels + vehicle.setSteeringValue(logic.car["steer"]*s,0) + vehicle.setSteeringValue(logic.car["steer"]*s,1) + + ## slowly ease off gas and center steering ## + logic.car["steer"] *= 0.6 + logic.car["force"] *= 0.9 + + ## align car to Z axis to prevent flipping ## + logic.car.alignAxisToVect([0.0,0.0,1.0], 2, Stability) + + ## store old values ## + logic.car["dS"] = S + + + +## called from main car object +def keyHandler(): + cont = logic.getCurrentController() + keys = cont.sensors["key"].events + for key in keys: + ## up arrow + if key[0] == events.UPARROWKEY: + logic.car["force"] = -15.0 + ## down arrow + elif key[0] == events.DOWNARROWKEY: + logic.car["force"] = 10.0 + ## right arrow + elif key[0] == events.RIGHTARROWKEY: + logic.car["steer"] -= 0.05 + ## left arrow + elif key[0] == events.LEFTARROWKEY: + logic.car["steer"] += 0.05 + ## Reverse + elif key[0] == events.RKEY: + if key[1] == 1: + # re-orient car + if logic.car["jump"] > 2.0: + pos = logic.car.worldPosition + logic.car.position = (pos[0], pos[1], pos[2]+3.0) + logic.car.alignAxisToVect([0.0,0.0,1.0], 2, 1.0) + logic.car.setLinearVelocity([0.0,0.0,0.0],1) + logic.car.setAngularVelocity([0.0,0.0,0.0],1) + logic.car["jump"] = 0 + ## Spacebar + elif key[0] == events.SPACEKEY: + # hackish Brake + if logic.car["speed"] > 2.0: + logic.car["force"] = 15.0 + if logic.car["speed"] < -2.0: + logic.car["force"] = -15.0 + + +## called from shadow lamp +def shadow(): + cont = logic.getCurrentController() + ownpos = [-5.0,0.0,8.0] + pos = logic.car.worldPosition + cont.owner.worldPosition = [pos[0]+ownpos[0], pos[1]+ownpos[1], pos[2]+ownpos[2]] + diff --git a/tests/grid.png b/tests/grid.png new file mode 100644 index 0000000..cfa969a Binary files /dev/null and b/tests/grid.png differ diff --git a/tests/kloppenheim_06_puresky_4k.exr b/tests/kloppenheim_06_puresky_4k.exr new file mode 100644 index 0000000..3343d2c Binary files /dev/null and b/tests/kloppenheim_06_puresky_4k.exr differ diff --git a/textures/0026.jpg b/textures/0026.jpg new file mode 100644 index 0000000..2877593 Binary files /dev/null and b/textures/0026.jpg differ diff --git a/textures/28.hdr b/textures/28.hdr new file mode 100644 index 0000000..a31dec0 Binary files /dev/null and b/textures/28.hdr differ diff --git a/textures/Angry_Puruit.png b/textures/Angry_Puruit.png new file mode 100644 index 0000000..9b7b57f Binary files /dev/null and b/textures/Angry_Puruit.png differ diff --git a/textures/Bark_Tile.jpg b/textures/Bark_Tile.jpg new file mode 100644 index 0000000..0729333 Binary files /dev/null and b/textures/Bark_Tile.jpg differ diff --git a/textures/Bark_normal.png b/textures/Bark_normal.png new file mode 100644 index 0000000..0d291c6 Binary files /dev/null and b/textures/Bark_normal.png differ diff --git a/textures/DaniPhoneTexture.png b/textures/DaniPhoneTexture.png new file mode 100644 index 0000000..c81912e Binary files /dev/null and b/textures/DaniPhoneTexture.png differ diff --git a/textures/DaniS_BED-b.png b/textures/DaniS_BED-b.png new file mode 100644 index 0000000..4148ad7 Binary files /dev/null and b/textures/DaniS_BED-b.png differ diff --git a/textures/DaniS_BED-c.png b/textures/DaniS_BED-c.png new file mode 100644 index 0000000..b89c374 Binary files /dev/null and b/textures/DaniS_BED-c.png differ diff --git a/textures/DanisFace.png b/textures/DanisFace.png new file mode 100644 index 0000000..b3ce2cb Binary files /dev/null and b/textures/DanisFace.png differ diff --git a/textures/GlovesColor.png b/textures/GlovesColor.png new file mode 100644 index 0000000..80f6ada Binary files /dev/null and b/textures/GlovesColor.png differ diff --git a/textures/GlovesNormal.png b/textures/GlovesNormal.png new file mode 100644 index 0000000..8702052 Binary files /dev/null and b/textures/GlovesNormal.png differ diff --git a/textures/Ground.jpg b/textures/Ground.jpg new file mode 100644 index 0000000..7606fca Binary files /dev/null and b/textures/Ground.jpg differ diff --git a/textures/JackFace.png b/textures/JackFace.png new file mode 100644 index 0000000..72ad43d Binary files /dev/null and b/textures/JackFace.png differ diff --git a/textures/JacksBodyTexture.png b/textures/JacksBodyTexture.png new file mode 100644 index 0000000..8b56984 Binary files /dev/null and b/textures/JacksBodyTexture.png differ diff --git a/textures/Kippa.png b/textures/Kippa.png new file mode 100644 index 0000000..07a2629 Binary files /dev/null and b/textures/Kippa.png differ diff --git a/textures/Light_dots.png b/textures/Light_dots.png new file mode 100644 index 0000000..205bffe Binary files /dev/null and b/textures/Light_dots.png differ diff --git a/textures/Logo.jpg b/textures/Logo.jpg new file mode 100644 index 0000000..cd69427 Binary files /dev/null and b/textures/Logo.jpg differ diff --git a/textures/LogoHat.png b/textures/LogoHat.png new file mode 100644 index 0000000..0dbcf76 Binary files /dev/null and b/textures/LogoHat.png differ diff --git a/textures/MoriasFace.png b/textures/MoriasFace.png new file mode 100644 index 0000000..5dcca46 Binary files /dev/null and b/textures/MoriasFace.png differ diff --git a/textures/MoriasPijama.jpg b/textures/MoriasPijama.jpg new file mode 100644 index 0000000..1021d45 Binary files /dev/null and b/textures/MoriasPijama.jpg differ diff --git a/textures/MoriasRacingOutfit.png b/textures/MoriasRacingOutfit.png new file mode 100644 index 0000000..ee55b2f Binary files /dev/null and b/textures/MoriasRacingOutfit.png differ diff --git a/textures/Morias_BED-c.png b/textures/Morias_BED-c.png new file mode 100644 index 0000000..78069b8 Binary files /dev/null and b/textures/Morias_BED-c.png differ diff --git a/textures/NeonSpeedster_leather.jpg b/textures/NeonSpeedster_leather.jpg new file mode 100644 index 0000000..f6d4d56 Binary files /dev/null and b/textures/NeonSpeedster_leather.jpg differ diff --git a/textures/NightLightBake.jpg b/textures/NightLightBake.jpg new file mode 100644 index 0000000..6a3201c Binary files /dev/null and b/textures/NightLightBake.jpg differ diff --git a/textures/Normalmap.png b/textures/Normalmap.png new file mode 100644 index 0000000..79e1ddb Binary files /dev/null and b/textures/Normalmap.png differ diff --git a/textures/PapsFaceTexture.png b/textures/PapsFaceTexture.png new file mode 100644 index 0000000..69dbb67 Binary files /dev/null and b/textures/PapsFaceTexture.png differ diff --git a/textures/Shoenormal.png b/textures/Shoenormal.png new file mode 100644 index 0000000..99cf1fd Binary files /dev/null and b/textures/Shoenormal.png differ diff --git a/textures/ads.png b/textures/ads.png new file mode 100644 index 0000000..817d551 Binary files /dev/null and b/textures/ads.png differ diff --git a/textures/banner_full.jpg b/textures/banner_full.jpg new file mode 100644 index 0000000..c2e16f6 Binary files /dev/null and b/textures/banner_full.jpg differ diff --git a/textures/bed_texture.jpg b/textures/bed_texture.jpg new file mode 100644 index 0000000..2f00d7d Binary files /dev/null and b/textures/bed_texture.jpg differ diff --git a/textures/black.png b/textures/black.png new file mode 100644 index 0000000..83de55a Binary files /dev/null and b/textures/black.png differ diff --git a/textures/blank_paper.png b/textures/blank_paper.png new file mode 100644 index 0000000..89e08da Binary files /dev/null and b/textures/blank_paper.png differ diff --git a/textures/books.png b/textures/books.png new file mode 100644 index 0000000..5d55aad Binary files /dev/null and b/textures/books.png differ diff --git a/textures/boxify.png b/textures/boxify.png new file mode 100644 index 0000000..cdb3344 Binary files /dev/null and b/textures/boxify.png differ diff --git a/textures/bush.png b/textures/bush.png new file mode 100644 index 0000000..45889e3 Binary files /dev/null and b/textures/bush.png differ diff --git a/textures/cliff.jpg b/textures/cliff.jpg new file mode 100644 index 0000000..f69090c Binary files /dev/null and b/textures/cliff.jpg differ diff --git a/textures/computer_UI_mocks.xcf b/textures/computer_UI_mocks.xcf new file mode 100644 index 0000000..45e43d9 Binary files /dev/null and b/textures/computer_UI_mocks.xcf differ diff --git a/textures/computer_instructions.jpg b/textures/computer_instructions.jpg new file mode 100644 index 0000000..b7c5e20 Binary files /dev/null and b/textures/computer_instructions.jpg differ diff --git a/textures/computer_instructions.png b/textures/computer_instructions.png new file mode 100644 index 0000000..53d6a8c Binary files /dev/null and b/textures/computer_instructions.png differ diff --git a/textures/computericons/icons.png b/textures/computericons/icons.png new file mode 100644 index 0000000..6418fb7 Binary files /dev/null and b/textures/computericons/icons.png differ diff --git a/textures/dirt.jpg b/textures/dirt.jpg new file mode 100644 index 0000000..b40e52d Binary files /dev/null and b/textures/dirt.jpg differ diff --git a/textures/eyes.jpg b/textures/eyes.jpg new file mode 100644 index 0000000..b11f4b4 Binary files /dev/null and b/textures/eyes.jpg differ diff --git a/textures/eyes_brown.jpg b/textures/eyes_brown.jpg new file mode 100644 index 0000000..6e976e0 Binary files /dev/null and b/textures/eyes_brown.jpg differ diff --git a/textures/fence.blend b/textures/fence.blend new file mode 100644 index 0000000..67f63af Binary files /dev/null and b/textures/fence.blend differ diff --git a/textures/fire_element1.jpg b/textures/fire_element1.jpg new file mode 100644 index 0000000..e9ac23b Binary files /dev/null and b/textures/fire_element1.jpg differ diff --git a/textures/fire_element2.jpg b/textures/fire_element2.jpg new file mode 100644 index 0000000..f8b0473 Binary files /dev/null and b/textures/fire_element2.jpg differ diff --git a/textures/front_light.png b/textures/front_light.png new file mode 100644 index 0000000..ae5d459 Binary files /dev/null and b/textures/front_light.png differ diff --git a/textures/gage.png b/textures/gage.png new file mode 100644 index 0000000..73132c3 Binary files /dev/null and b/textures/gage.png differ diff --git a/textures/gnomeLogo.png b/textures/gnomeLogo.png new file mode 100644 index 0000000..13711bf Binary files /dev/null and b/textures/gnomeLogo.png differ diff --git a/textures/green_tree_cutout.png b/textures/green_tree_cutout.png new file mode 100644 index 0000000..6de174b Binary files /dev/null and b/textures/green_tree_cutout.png differ diff --git a/textures/grey_black.jpg b/textures/grey_black.jpg new file mode 100644 index 0000000..f5bb50c Binary files /dev/null and b/textures/grey_black.jpg differ diff --git a/textures/islands_foam.png b/textures/islands_foam.png new file mode 100644 index 0000000..d59fb13 Binary files /dev/null and b/textures/islands_foam.png differ diff --git a/textures/kloppenheim_06_puresky_4k.exr b/textures/kloppenheim_06_puresky_4k.exr new file mode 100644 index 0000000..3343d2c Binary files /dev/null and b/textures/kloppenheim_06_puresky_4k.exr differ diff --git a/textures/leather.jpg b/textures/leather.jpg new file mode 100644 index 0000000..23667a4 Binary files /dev/null and b/textures/leather.jpg differ diff --git a/textures/lightdots.png b/textures/lightdots.png new file mode 100644 index 0000000..298c07d Binary files /dev/null and b/textures/lightdots.png differ diff --git a/textures/loading.jpg b/textures/loading.jpg new file mode 100644 index 0000000..5b4da42 Binary files /dev/null and b/textures/loading.jpg differ diff --git a/textures/map.jpg b/textures/map.jpg new file mode 100644 index 0000000..772704d Binary files /dev/null and b/textures/map.jpg differ diff --git a/textures/metal_fence.png b/textures/metal_fence.png new file mode 100644 index 0000000..b456b68 Binary files /dev/null and b/textures/metal_fence.png differ diff --git a/textures/nitros.jpg b/textures/nitros.jpg new file mode 100644 index 0000000..6f49ad5 Binary files /dev/null and b/textures/nitros.jpg differ diff --git a/textures/nitros.xcf b/textures/nitros.xcf new file mode 100644 index 0000000..411ead3 Binary files /dev/null and b/textures/nitros.xcf differ diff --git a/textures/oak-branch-a.png b/textures/oak-branch-a.png new file mode 100644 index 0000000..2e5a3c0 Binary files /dev/null and b/textures/oak-branch-a.png differ diff --git a/textures/oak-branch-c.png b/textures/oak-branch-c.png new file mode 100644 index 0000000..546710a Binary files /dev/null and b/textures/oak-branch-c.png differ diff --git a/textures/palm_cutout.png b/textures/palm_cutout.png new file mode 100644 index 0000000..5745f72 Binary files /dev/null and b/textures/palm_cutout.png differ diff --git a/textures/palm_trunk.jpg b/textures/palm_trunk.jpg new file mode 100644 index 0000000..bb472e6 Binary files /dev/null and b/textures/palm_trunk.jpg differ diff --git a/textures/palmleaf_managable.png b/textures/palmleaf_managable.png new file mode 100644 index 0000000..c331d23 Binary files /dev/null and b/textures/palmleaf_managable.png differ diff --git a/textures/primary.png b/textures/primary.png new file mode 100644 index 0000000..eb56144 Binary files /dev/null and b/textures/primary.png differ diff --git a/textures/racer_indicator.png b/textures/racer_indicator.png new file mode 100644 index 0000000..20b420f Binary files /dev/null and b/textures/racer_indicator.png differ diff --git a/textures/satara_night_4k.exr b/textures/satara_night_4k.exr new file mode 100644 index 0000000..92a4b38 Binary files /dev/null and b/textures/satara_night_4k.exr differ diff --git a/textures/screen.png b/textures/screen.png new file mode 100644 index 0000000..1839204 Binary files /dev/null and b/textures/screen.png differ diff --git a/textures/screen_background.jpg b/textures/screen_background.jpg new file mode 100644 index 0000000..80089b0 Binary files /dev/null and b/textures/screen_background.jpg differ diff --git a/textures/sings.png b/textures/sings.png new file mode 100644 index 0000000..49811fb Binary files /dev/null and b/textures/sings.png differ diff --git a/textures/sky/city_day.jpg b/textures/sky/city_day.jpg new file mode 100644 index 0000000..dc3e88c Binary files /dev/null and b/textures/sky/city_day.jpg differ diff --git a/textures/sky/city_evening.jpg b/textures/sky/city_evening.jpg new file mode 100644 index 0000000..64b2408 Binary files /dev/null and b/textures/sky/city_evening.jpg differ diff --git a/textures/sky/city_morning.jpg b/textures/sky/city_morning.jpg new file mode 100644 index 0000000..e043720 Binary files /dev/null and b/textures/sky/city_morning.jpg differ diff --git a/textures/sky/city_night.jpg b/textures/sky/city_night.jpg new file mode 100644 index 0000000..2ab47f8 Binary files /dev/null and b/textures/sky/city_night.jpg differ diff --git a/textures/sky/racetrack_day.jpg b/textures/sky/racetrack_day.jpg new file mode 100644 index 0000000..73fb1fb Binary files /dev/null and b/textures/sky/racetrack_day.jpg differ diff --git a/textures/sky/racetrack_evening.jpg b/textures/sky/racetrack_evening.jpg new file mode 100644 index 0000000..37b76f9 Binary files /dev/null and b/textures/sky/racetrack_evening.jpg differ diff --git a/textures/sky/racetrack_morning.jpg b/textures/sky/racetrack_morning.jpg new file mode 100644 index 0000000..3509e75 Binary files /dev/null and b/textures/sky/racetrack_morning.jpg differ diff --git a/textures/sky/racetrack_night.jpg b/textures/sky/racetrack_night.jpg new file mode 100644 index 0000000..72fcc97 Binary files /dev/null and b/textures/sky/racetrack_night.jpg differ diff --git a/textures/top_left_corner.png b/textures/top_left_corner.png new file mode 100644 index 0000000..c86dc8e Binary files /dev/null and b/textures/top_left_corner.png differ diff --git a/textures/tower_cutout.png b/textures/tower_cutout.png new file mode 100644 index 0000000..fcc7dba Binary files /dev/null and b/textures/tower_cutout.png differ diff --git a/textures/tower_infographic.jpg b/textures/tower_infographic.jpg new file mode 100644 index 0000000..ada8b17 Binary files /dev/null and b/textures/tower_infographic.jpg differ diff --git a/textures/towers/tower_infographic_01.jpg b/textures/towers/tower_infographic_01.jpg new file mode 100644 index 0000000..ad2f392 Binary files /dev/null and b/textures/towers/tower_infographic_01.jpg differ diff --git a/textures/towers/tower_infographic_02.jpg b/textures/towers/tower_infographic_02.jpg new file mode 100644 index 0000000..962b7b2 Binary files /dev/null and b/textures/towers/tower_infographic_02.jpg differ diff --git a/textures/towers/tower_infographic_03.jpg b/textures/towers/tower_infographic_03.jpg new file mode 100644 index 0000000..e51a122 Binary files /dev/null and b/textures/towers/tower_infographic_03.jpg differ diff --git a/textures/towers/tower_infographic_04.jpg b/textures/towers/tower_infographic_04.jpg new file mode 100644 index 0000000..d6e96c2 Binary files /dev/null and b/textures/towers/tower_infographic_04.jpg differ diff --git a/textures/towers/tower_infographic_05.jpg b/textures/towers/tower_infographic_05.jpg new file mode 100644 index 0000000..2520b33 Binary files /dev/null and b/textures/towers/tower_infographic_05.jpg differ diff --git a/textures/wood_fence.jpg b/textures/wood_fence.jpg new file mode 100644 index 0000000..4364132 Binary files /dev/null and b/textures/wood_fence.jpg differ